因为自己的网站还没弄好能被百度引擎搜索到,所以转载到SF上
前情提要
之前裸面北森实习遭遇惨败...闭包本来是自己仔仔细细看过几次的内容也没答好,经过自己仔细的感悟,然后看各大佬的资料,终于又有了一些新的体会(看了之前说的闭包...感觉自己真是一个睿智...🐷)
什么是闭包
之前我一直听标准答案是:
函数记住并访问其所在的词法作用域,叫做闭包现象,而此时函数对作用域的引用叫做闭包。
什么是词法作用域
- JavaScript 采用词法作用域 -> 函数的作用域在函数定义的时候就决定了。
- 当然,与之相对的还有动态作用域 -> 函数的作用域是在函数调用的时候决定了。
下面有个经典的面试题用于理解 JavaScript 词法作用域的特性:
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar();
//结果为1
//因为词法作用域,函数的作用域在函数定义就决定了,所以foo的value只能在foo定义的作用域向上查找,找到全局变量value = 1
利用这个特性,我们在 foo 外再加一个函数。因为词法作用域的关系,foo 的 value 从 foo 定义的作用域向上查找,就能找到 fn 的 value 为 3.
var value = 1;
function fn(){
var value = 3;
function foo() {
console.log(value);
}
foo();
}
function bar() {
var value = 2;
fn();
}
bar();//3
通过 chrome 调试,我们可以看到运行过程大致为这样
//bar执行上下文入栈
value声明为undefined
value赋值为2
//fn执行上下文入栈
value声明为undefined(这里因为词法作用域的关系,在fn的作用域是没有value的)
value赋值为3
//可以看到结果,fn是一个闭包,产生的引用是对value: 3的引用
Closure(fn) value: 3
所以之前我看到《实战 ES2015》上说的闭包的原理是利用高阶函数的特性(函数里面可以声明函数),产生穿越作用域的引用(就是说 foo 函数能向上查找)。
什么是执行上下文
虽说上面已经说了,闭包的产生是因为词法作用域,但是如果了解了执行上下文,才能感受到“记住”的特性。
var value = 1;
function fn(){
var value = 3;
function foo() {
console.log(value);
}
return foo;
}
function bar() {
var value = 2;
return fn();
}
var result = bar();
result();//3
对于这个例子,这个函数的执行上下文栈会经过以下过程
ECStack.push(globalContext)//全局global入栈
ECStack.push(barContext)//bar被执行入栈
ECStack.push(fnContext)//fn入栈
ECStack.pop(fnContext)//fn出栈
ECStack.pop(outContext)//bar出栈
ECStack.push(result)//result入栈
ECStack.pop(result)//result出栈
ECStack.pop(globalContext)//global出栈
/* 以上过程都可以使用调试通过Scope看到 */
可以看到 result 执行上下文入栈的时候,fn 已经出栈了,但是 result 仍然能够“记住” fn 的 value 值。所以,通过闭包,我们可以在其他的执行上下文中,访问到函数的内部变量。
鞭尸环节
来源:https://www.cnblogs.com/kizna...之前一直觉得这个闭包没啥好说的,实际...当时根本看闭包的视角不一样
for (var i = 1 ; i <= 5 ; ++i) {
(function(i){
setTimeout( function timer(){
console.log(i);
} , i*1000);
})(i);
}
从现在自己的视角来看这里呢,因为 timer 在匿名的立即执行函数中定义,所以对于词法作用域来说,timer 能够向上访问到匿名函数的 i。而我们知道 setTimeout 作为任务分发器,它的第一个参数(也是回调函数)会进入任务队列,而当它执行的时候,即使匿名函数的执行上下文已经出栈,但它依然能够访问 i,从而产生了闭包。
闭包的作用
- 函数柯里化
- 延长变量生命周期
- 创建模块(实现面向对象的公有方法抛出私有变量的设计)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。