JS基础核心之闭包
1. 闭包是什么?
function A () { // chrome认为的闭包是A函数
var num = 1
return function B () {
console.log(num)
}
}
var C = A ()
// 函数A中的变量num被全局函数C引用着,以至于不会被垃圾回收机制回收
C() // 1 // 大多数书籍理解的闭包是C函数
在A函数中嵌套B函数,B函数访问A函数中的变量。将A函数复制给C函数并执行。
那么在大多数的理解中,包括许多著名的书籍,文章里都以函数C的名字代指这里生成的闭包。而在chrome中,则以执行上下文A的函数名代指闭包。
而我的理解是:闭包更准确的说是一项技术或者一个特性:只要运用具备阻止垃圾回收机制回收和突破作用域链限制的技术,就是闭包。像是《JavaScript权威指南》打的比方,像是把变量包裹了起来,形象的称为“闭包”。
如果非要指明哪个函数是闭包的话,我愿意将A函数称为定义闭包的函数,C函数为执行闭包的函数。
2 生成闭包的条件是什么?
function foo() { // 定义闭包
var a = 10;
function baz() {
console.log(a);
}
baz(); // 被确认创建闭包
}
baz();
a. 在函数内部创建新的函数;
b. 新的函数在执行时,访问了函数的变量对象。
c. 闭包是在函数被调用执行的时候才被确认创建的。
一句话总结:==函数中闭包判定的准则,即执行时是否在内部定义的函数中访问了上层作用域的变量。==
3. 闭包的作用是什么?
闭包,阻止垃圾回收机制。
闭包,突破作用域链接。
4 闭包的应用
模块模式
《你不知道的JS中》的示例:
function coolModule () {
// 私有变量和函数
var something = 'cool';
var another = [1, 2, 3]
function doSomething () {
console.log(something)
}
function doAnother () {
console.log(another.join('!'))
}
// 公开方法
return {
doSomething,
doAnother
}
})
var foo = coolModule ()
foo.doSomething() // cool
foo.doAnother() // 1!2!3
模块模式需要具备两个必要条件:
1.必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例)。
2.封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态。
什么时候使用模块模式?
如果必须创建一个对象并一某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以使用模块模式。
哪些地方有用到模块模式?
最典型的就是JQuery库,jQuery和$标识符就是JQuery模块的公共API,但它们本身都是函数(由于函数也是对象,它们本身也可以拥有属性)
以后单独拿一个章节,来具体讲讲现在的模块化和未来的模块化机制。
柯里化
具体的内容在函数的柯里化的章节中详细分析。
面试题
- 防抖和节流
戳此传送门
- 定时器
for (var i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i); // 5次6
}, i * 1000);
}
输出6的原因是:被封闭在一个共享的全局作用域中,实际上只有一个i。
for (var i = 1; i <= 5; i++) {
(function (j) {
setTimeout(function timer() {
console.log(j); // 依次输出1至5
}, i * 1000);
})(i)
}
依次输出1-5的原因是:在迭代内部使用IIFE(立即表达函数)为每个迭代生成一个新的作用域,将外部变量的值传递进去并被引用(阻止垃圾回收机制回收),使得每个回调函数都能访问到正确值的变量。
- jn-toast组件认识闭包