手写-js节流(定时器+时间差两种方式)

官方解释:当持续触发事件时,保证一定时间段内只调用一次事件处理函数。

节流实现思路: 实现节流函数, 我们使用定时器是不方便管理的, 实现节流函数我们采用另一个思路

我们获取一个当前时间nowTime, 我们使用new Date().gettime()方法获取, 在设定一个开始时间startTime, 等待时间waitTime

waitTime = interval - (nowTime - startTime), 当前的时间减去开始的时间得到结果, 再使用间隔时间减去这个结果, 就可以得到等待时间

得到等待时间我们在进行判断, 如果等待时间小于等于0, 那么就可以执行回调函数

开始时间startTime我们初始值为0就好, 当第一次执行时, nowTime获取的时间戳是一个非常大的值, 得到的结果waitTime是负值, 所以第一次执行节流函数, 一定会立即执行, 这也符合我们要封装的效果

基础节流函数(时间差的方式):

/**
 * 节流函数
 * @param {function} func 需要节流的目标函数
 * @param {number} interval 执行目标函数的间隔,即多久执行一次目标函数ms
 * 注意:如果初始值为当前时间,则最后一次如果没有达到间隔时间则不会执行
 * */
function throttle(func, interval) {
  // 记录上次执行的时间。如果初始值设置成零,则会首次触发就会执行目标函数,相当于立即执行。
  // let preDoTime = 0;
  let preDoTime = new Date().getTime();
  // 返回函数, 使用闭包使preDoTime变量存在于内存中。
  return function (...args) {
    // 记录调用函数的this指向。
    let _this = this;
    //获取到当前日期
    let currentTime = new Date().getTime();
    if (interval <= currentTime - preDoTime) {// 如果当前时间减去上次执行时间 大于等于设置的时间间隔, 就可以执行目标函数
      func.apply(_this, args)
      preDoTime = currentTime;
    }
  }
}
// 测试程序 start ----
const testFun = function (data) {
  console.log(`console.log---${this.name} --- ${data}`);
}

const resultFun = throttle(testFun, 1500);

for (let i = 0; i <= 5; i++) {
  setTimeout(function () {
    this.name = 'setTimeoutFunc1'
    resultFun(`test${i}`);
  }, (i + 1) * 1000) // 6秒内, 每秒执行一次resultFun函数
}

// 距离上次触发间隔0.5秒
setTimeout(function () {
  this.name = 'setTimeoutFunc2'
  resultFun(`test7`);
}, 8000)
// 测试程序 end ----

初始值为当前时间测试结果:

1b37c47e4b884996ad802ec4b9ed6985.png

 初始值为零测试结果:

6e468fa3d3a24469b2b86facd60f5ffa.png

 节流函数(定时器实现):

/**
 * 节流函数
 * @param {function} func 需要节流的目标函数
 * @param {number} interval 执行目标函数的间隔,即多久执行一次目标函数ms
 * 特点: 首次立即执行
 * */
function throttle(func, interval) {
  let timer = null;
  return function(...args) {
    let _this = this;
    if (!timer) {
      timer = setTimeout(function() {
        func.apply(_this, args);
        timer = null;
      }, interval)
    }
  }
}
// 测试程序 start ----
const testFun = function (data) {
  console.log(`console.log---${this.name} --- ${data}`);
}

const resultFun = throttle(testFun, 1500);

for (let i = 0; i <= 5; i++) {
  setTimeout(function () {
    this.name = 'setTimeoutFunc1'
    resultFun(`test${i}`);
  }, (i + 1) * 1000) // 6秒内, 每秒执行一次resultFun函数
}

// 距离上次触发间隔0.5秒
setTimeout(function () {
  this.name = 'setTimeoutFunc2'
  resultFun(`test7`);
}, 8000)

执行结果:

1326d41366ae423d8dcee9514927bff9.png

节流函数(时间差+定时器 兼顾首次和最后一次)

/**
 * 节流函数
 * @param {function} func 需要节流的目标函数
 * @param {number} interval 执行目标函数的间隔,即多久执行一次目标函数ms
 * 特点: 结合时间差和定时器,做到立即执行的同时,兼顾执行最后一次触发
 * */
function throttle(func, interval) {
  let timer = null;
  let preDoTime = 0;
  return function(...args) {
    // 保存this指向
    let _this = this;
    // 获取当前日期
    let currentTime = +new Date();
    // 计算剩余时间,大于零表示未到执行时间,小于零或者定于零则表示到了执行的时间,可以执行
    let remaining = interval - (currentTime - preDoTime);
    if (remaining <= 0) {
      func.apply(_this, args);
      preDoTime = currentTime;
      // 首次会进入上一个if,首次的时候是没有timer的,所以需要判空
      if(timer) {
        // 如果有定时器存在,就将定时器取消。
        clearTimeout(timer);
        timer = null;
      }
    } else if(!timer) {
      timer = setTimeout(function () {
        // 如果是最后一次则没有执行取消定时器的操作,就会执行当前定时器。
        preDoTime = +new Date(); // 因为是定时器,所以执行日期要重新获取。
        timer = null;
        func.apply(_this, args);
      }, interval)
    }
  }
}
// 测试程序 start ----
const testFun = function (data) {
  console.log(`console.log---${this.name} --- ${data}`);
}

const resultFun = throttle(testFun, 1500);

for (let i = 0; i <= 5; i++) {
  setTimeout(function () {
    this.name = 'setTimeoutFunc1'
    resultFun(`test${i}`);
  }, (i + 1) * 1000) // 6秒内, 每秒执行一次resultFun函数
}

// 距离上次触发间隔0.5秒
setTimeout(function () {
  this.name = 'setTimeoutFunc2'
  resultFun(`test7`);
}, 8000)
// 测试程序 end ----

 测试结果:

f48339c3a53443e587a0bb8330ba6296.png

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,K-Means++算法是一种基于随机初始化的聚类算法,其主要目的是将数据集分成K个簇。而轮廓系数是一种聚类算法的评估指标,用于衡量聚类的质量。 K-Means++算法对于每个簇都会计算出其簇内平均距离(即簇内误差平方和SSE),而轮廓系数则结合了簇内平均距离和簇间距离,其计算公式为: $s(i) = \frac{b(i) - a(i)}{max\{a(i), b(i)\}}$ 其中,$a(i)$表示样本$i$与其所在簇内其他样本的平均距离,$b(i)$表示样本$i$与其他簇中所有样本的平均距离,$max\{a(i), b(i)\}$表示$a(i)$和$b(i)$中的最大值。 接下来是手写K-Means++算法及其轮廓系数评估的代码: ```python import numpy as np from sklearn.metrics import pairwise_distances from sklearn.datasets import make_blobs class KMeansPlusPlus: def __init__(self, n_clusters, max_iter=100): self.n_clusters = n_clusters self.max_iter = max_iter def fit(self, X): # 随机初始化第一个簇中心 centers = [X[np.random.choice(len(X))]] # 选择剩余簇中心 for _ in range(1, self.n_clusters): dist = pairwise_distances(X, centers) dist = np.min(dist, axis=1) dist /= np.sum(dist) new_center = X[np.random.choice(len(X), p=dist)] centers.append(new_center) # 迭代聚类 for _ in range(self.max_iter): # 计算每个样本到各个簇中心的距离,并分类 dist = pairwise_distances(X, centers) labels = np.argmin(dist, axis=1) # 更新簇中心 for i in range(self.n_clusters): centers[i] = np.mean(X[labels == i], axis=0) self.labels_ = labels self.cluster_centers_ = centers def silhouette_score(self, X): # 计算a(i)和b(i) dist = pairwise_distances(X) a = np.zeros_like(dist) b = np.zeros_like(dist) for i in range(len(X)): mask = self.labels_ == self.labels_[i] a[i] = np.mean(dist[i][mask]) b[i] = np.min(np.mean(dist[i][~mask]), np.finfo(float).max) # 计算轮廓系数 s = (b - a) / np.maximum(a, b) s[self.labels_ == -1] = 0 return np.mean(s) # 测试代码 X, y = make_blobs(n_samples=100, centers=3, random_state=42) kmeans = KMeansPlusPlus(n_clusters=3) kmeans.fit(X) score = kmeans.silhouette_score(X) print("轮廓系数:", score) ``` 在上述代码中,`KMeansPlusPlus`类实现了K-Means++算法,`silhouette_score`方法用于计算轮廓系数。首先,`silhouette_score`方法计算每个样本到各个簇中心的距离,并根据当前的聚类结果分类,然后计算出$a(i)$和$b(i)$。最后,根据计算出的$a(i)$和$b(i)$计算出轮廓系数$s(i)$,并将$s(i)$取平均值作为最终的轮廓系数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值