闭包(Closures)是我学习过程中的一个瓶颈。
没有跨不过去的坎,只有走不出的心。
7.1 概念
闭包是一种特殊的现象。
它由两部分组成=>执行上下文(A),以及在该执行上下文中创建的函数(B)。
当B执行时,如果访问了A的变量对象中的值,那么闭包就会产生。
许多书籍、文章里都以函数B的名字指代这里生成的闭包。而在Chrome中,则以执行上下文A的函数名代指闭包。
我们只需知道,一个闭包对象,有A、B共同组成,在以后的篇幅中,都将以Chrome的标准来称呼。
//demo01.js
function foo() {
var a = 20;
var b = 30;
function bar() {
return a + b;
}
return bar;
}
var bar = foo();
bar();
在上面的例子中,首先执行上下文foo,在foo中定义了函数bar,而后通过对外放回bar的方式,得以让bar执行。当bar执行时,访问了foo内部的变量a和b。因此这个时候闭包就产生了。
在Chrome中通过断点调试的方式可以逐步分析该过程,从下图中可以看出,此时闭包的产生,用foo代指。
在图中,箭头所指的正式闭包。其中Call Stack为当前的函数调用栈,Scope为当前正在被执行函数的作用域链,Local为当前活动对象(AO)。
在学习了闭包的基础概念后,下面就来验证一下你是否真的理解了。现在思考一个小问题,把上面的代码稍作改动,是否形成闭包了?
//demo02.js
function foo() {
var a = 20;
var b = 30;
function bar() {
return a + b;
}
bar();
}
foo();
仍然是foo中定义的bar函数在执行时访问了foo中的变量,因此这个时候仍然会形成闭包。如图所示:
在来看一个非常有意思的例子。
//demo03.js
function add(x) {
return function _add(y) {
return x + y;
}
}
add(2)(3);
对于这个既熟悉又陌生的函数,他有闭包产生吗?
当然有。当内部函数_add被返回调用时,访问了add函数变量对象中的x,这个时候,闭包就会产生,如下图所示。一定要记住,函数参数中的变量传递给函数之后也会加到变量对象中。
还有一个例子可以验证大家对于闭包的理解。
看看下面这段代码中是否有闭包产生。
//demo04.js
var name = 'window';
var p = {
name: 'pan',
getName: function() {
return function() {
return this.name;
}
}
}
var getName = p.getName();
var _name = getName();
console.log(_name);
getName在执行的时候,其this是指向window对象的,而这个时候并没有形成闭包的环境,因此这个例子没有闭包。
那么如果按照下面的方式进行改动呢?
//改动一
//demo05.js
var name = "window";
var p = {
name: 'pan',
getName: function() {
return function() {
return this.name;
}
}
}
var getName = p.getName();
var _name = getName.call(p); //利用call的方式改变其this指向
console.log(_name);
//改动二
//demo06.js
var name = "window";
var p = {
name: 'pan',
getName: function() {
var self = this; //利用变量保存的方式保证其访问的是P对象
return function() {
return self.name;
}
}
}
var getName = p.getName();
var _name = getName();
console.log(_name);
上面两处改动分别利用call与变量保存的方式保证了this的指向,那么,它们哪一个产生了闭包哪一个没有产生闭包了?这个问题,希望您与我共同思考。
这些都是我以往的学习笔记。如果您看到此笔记,希望您能指出我的错误。有这么一个群,里面的小伙伴互相监督,坚持每天输出自己的学习心得,不输出就出局。希望您能加入,我们一起终身学习。欢迎添加我的个人微信号:Pan1005919589