前言
闭包是由函数以及声明该函数的词法环境组合而成,该环境包含闭包创建时作用域内的任何局部变量。 —— 来源 MDN 最新解释🤞
特点
- 闭包是一个函数。
- 在外部函数上下文销毁的情况下,仍能访问外部函数作用域中的自由变量。 例子
function say() {
let name = '瑾行'
function hello() {
console.log('hello,' + name)
}
return hello
}
let func = say()
func() // hello, 瑾行
由案例可知,当let func = say()执行结束,虽然say函数的执行上下文已被回收销毁,但执行结果依然可以读取到name变量值。
闭包与作用域链
按道理来说,当执行上下文被销毁后是读取不到变量值的。但不要忘记,创建函数执行上下文的同时也维护了一条作用域链。当闭包函数引用外层函数的局部变量时,即使外层函数上下文被销毁了,但JavaScript依然会让引用的活动变量对象存在内存中,闭包函数依然可以通过作用域链寻找到它。
应用
防抖与节流,以防抖为例。
function debounce(fn, delay) {
let timer
return function() {
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, arguments)
}, delay)
}
}
私有化变量
let obj = (function() {
let name = '瑾行';
function getName() {
console.log('name:', name)
}
return {
getName: getName
}
})()
console.log(obj.name) // undefined
obj.getName() // '瑾行'
性能
若非特定任务需要使用闭包,在函数中创建函数是不明智的,因为闭包在处理速度和内存消耗对脚本具有负面影响。
若创建新的对象或者类,方法通常应该关联对象的原型,而不是定义到对象的构造器中。因为每次构造器被调用,方法都会被重新赋值一次。
例子
function func(name) {
this.name = name.toString()
this.getName = function() {
return this.name
}
}
在上述代码,没有利用闭包的好处,应修改如下:
function func(name) {
this.name = name.toString()
}
func.prototype = {
getName: function() {
return this.name
}
}