Open Brand0n-Zhang opened 3 years ago
一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。
除了一些特殊任务(模拟私有方法),应该避免在函数中创建函数,比如构造函数中创建的函数在每次调用时,都会重新赋值,存在处理速度和内存消耗的问题。
// 防抖,在触发后 n 秒后再执行,如果重新触发,重新计时,
// 能控制是否立即执行,而不是等待后执行
// 保留函数的 this 指向
// 能够取消
function debounce (func, wait, immediate) {
var timeout, result
// 节流过的函数
var debounced = function () {
var context = this
var args = arguments
if (timeout) clearTimeout(timeout)
// 立即执行模式
if (immediate) {
var callNow = !timeout
if (callNow) {
// 如果倒计时结束,立即执行函数
result = func.apply(context, args)
}
// 倒计时重新开始
timeout = setTimeout(function () {
// 倒计时结束
timeout = null
}, wait)
} else {
// 超时后执行
timeout = setTimeout(function () {
func.apply(context, args)
}, wait)
}
// 立即执行时返回函数执行结果
return result
}
// 提供取消函数
debounced.cancel = function () {
clearTimeout(timeout)
timeout = null
}
return debounced
}
// 节流持续触发事件,每隔一段时间,只执行一次事件。
// options.leading 事件触发立即执行
// options.trailing 停止触发事件后,超时时仍会执行一次
function throttle (func, wait, options) {
var context, args, timeout, previous
if (!options) options = {}
var throttled = function () {
var now = new Date().getTime()
context = this
args = arguments
// 首次触发不需要立即执行时
if (!previous && options.leading === false) previous = now
// 距离下次执行还剩的时间
var remaining = wait - (now - previous)
// 超时 或 系统时间调整过导致剩余时间大于等待时间
if (remaining <= 0 || remaining > wait) {
if (timeout) {
// 如果时间已过,但计时器仍未执行,需要手动清除
clearTimeout(timeout)
timeout = null
}
previous = now
func.apply(context, args)
if (!timeout) {
// 如果不存在超时计数器,可以清空变量以便回收内存
context = args = null
}
} else if (!timeout && options.trailing !== false) {
// 超时执行未设置 && 打开最后一次触发后超时执行
timeout = setTimeout(function () {
if (options.leading !== false) {
previous = new Date().getTime()
}
func.apply(context, args)
clearTimeout(timeout)
timeout = null
if (!timeout) {
// 如果不存在超时计数器,可以清空变量回收内存
context = args = null
}
}, remaining)
}
}
// 取消函数
throttled.cancel = function () {
clearTimeout(timeout)
previous = timeout = null
}
return throttled
}