初学闭包时一直以为很简单。但伴随对一个问题深入学习后,才算真正理解了闭包,同时也发现连<<JavaScript高级程序设计>>中都些不准确的地方。

我不准备从头介绍闭包的概念,而是在下面列了几份参考资料。其中以【参考2】最为简洁,本文也是因文中的习题而引出进一步的探讨。

从[参考2]最后提出的习题开始(应该来自<<JavaScript高级 程序设计>> 7.2),期望下面的程序可以输出"My Object",并且预期在取得this.name值时的标识符解析(identifier resolution)顺序如下:

(示例1)

上面程序会在log位置输出"The Window", 而不是期望的"My Object"。

其实红色部分错了。this并不指向创建它的对象,而是指向执行它的对象。
所以在执行下面这句话时,this其实是全局的window:

object.getNameFunc()();

这句话等价于以下两句:

vartempFunc = object.getNameFunc();

tempFunc();

它的执行对象就是全局的window,并且tempFunc和window.tempFunc完全等价的。

其执行对象的变化示意如下:

getNameFunc的执行对象是object, 而返回的闭包函数的执行对象是window。

增加一个示例,可以更明确的了解到这一点:
var name =
"The Window";

var object = {

 name :
"My Object",

getNameFunc:function() {

console.log(this.name);  // The Window

}

};

window.onload = object.getNameFunc;

(示例2)

所以如果要达成期望的输出,必须改变this的指向。有两种做法:
  1. 不用this,而使用参数从父函数传入所需的数据。(不用this,不是简单将this去除。默认还是会使用this访问的。)
  2. 显示改变this 指向 (比如call或apply)。

第一种方法的示例:

var name ="The Window";

var object = {

  name :"My Object",

getNameFunc:function() {

var that =this;

return function(){

return that.name;

};

}

 };

 console.log( window.object.getNameFunc()());

(示例3)

第二种方法的示例:
  1. 将调用方法改为如下:

console.log( object.getNameFunc().call(object) );

2. 简化调用,改为如下:

var name ="The Window";

var object = {

    name :
"My Object",

getNameFunc:function() {

return(function(){

returnthis.name;

}).call(this);

}

 };

console.log( object.getNameFunc());

(示例4)

到底发生了什么?

<<JavaScript高级程序设计>>:

某个函数第一次被调用时,会创建一个执行环境(execution context,环境比上下文更直白)及相应的作用域链(scope
chain),并把作用域(scope)赋值给一个特殊的内部属性([[Scope]])。然后,使用this、arguments和其它命名参数的值来
初始化函数的活动对象(activation object)。但在作用域链中,外部函数(outer)的活动对象(activation
object)处于第二位,外部函数的外部函数的活动对象处于第三位,……直至作为作用域链终点的全局执行环境。(7.2, 3rd Edition)

执行环境包含了三个部分 [详细内容在【参考4】中]:
  a. 词法环境
  b. 变量环境
  c. ThisBinding

(图片来自【参考4】)

如果这样,示例的理解就应该是正确!所以,显然不是这么单纯。再思考一下函数的创建时机。函数的声明与其实例化(instantiation)的时间是不同的,而是在使用时创建。实例化过程中发生了什么?

[参考5]中给的解释:  与外部函数声明对应的函数对象会在全局执行环境的变量实例化过程中被创建。因此,外部函数对象的 [[scope]] 属性中会包含一个只有全局对象的“单项目(one item)”作用域链。
这样就对了。下面就是name的查找顺序(和示例1的this.name不同)。

如下面的示例所示:

var name ="The Window";

var object = {

  name :"My Object",

getNameFunc:function() {

var name="Inner Of getNameFunc";

return function(){

console.log(name); //Inner Of getNameFunc

return name;

};

}

 };

 console.log( window.object.getNameFunc()()); //Inner Of getNameFunc

(示例5)

References:
1. PPK 谈 JavaScript 的 this 关键字
 http://www.cnblogs.com/georgewing/archive/2009/09/29/1576641.html
2. 学习Javascript闭包(Closure)
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
3.  闭包的秘密
http://www.gracecode.com/archives/2385/
4. JavaScript Closures Explained
 http://lostechies.com/derekgreer/2012/02/17/javascript-closures-explained/
5. 理解JavaScript闭包
 http://www.cn-cuckoo.com/main/wp-content/uploads/2007/08/JavaScriptClosures.html

最新文章

  1. 【原】iOS学习之应用之间的操作
  2. EPLAN Electric P8 2.0即将到来,着实令人期待-转caodaping
  3. Visual Studio 2010 简体中文旗舰、专业版(MSDN原版下载)
  4. MyBatis魔法堂:ResultMap详解
  5. js注意事项
  6. 20个2014年最优秀的PHP框架
  7. Ration Rose2003安装及破解
  8. 20170319 - pycurl 提示 libcurl link-time version is older than compile-time version
  9. Git(1):版本库+工作区+暂存区
  10. Shell命令-线上查询及帮助之man、help
  11. 关于cisco日志的配置
  12. layui常见问题记录
  13. 雷林鹏分享:jQuery EasyUI 数据网格 - 创建自定义视图
  14. MT【215】集合中元素个数
  15. vs2010 快捷键
  16. 在浏览器中直接调用webservice的正确写法
  17. 管道读写规则和Pipe Capacity、PIPE_BUF
  18. Delphi 7生成XML
  19. @Resource 注解
  20. POJ 3624 Charm Bracelet(01背包模板)

热门文章

  1. SQL查询连续年份
  2. go语言从例子开始之Example29.关闭通道
  3. [css知识体系]flexbox模型
  4. 使用intellij的idea集成开发工具中的git插件(转)
  5. java 方法的定义与调用
  6. postgres服务安装,启动和配置
  7. fn:indexOf()详解(jsp中JSTL标签库)
  8. Django框架的学习
  9. Docker 镜像添加模块
  10. vue基础八