初学闭包时一直以为很简单。但伴随对一个问题深入学习后,才算真正理解了闭包,同时也发现连<<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

【javascript闭包】转载一篇不错的解释,也有几个大牛的链接的更多相关文章

  1. javascript 闭包 转载

    http://www.jb51.net/article/24101.htm var name = "the window"; var object = { name:"m ...

  2. 我从来不理解JavaScript闭包,直到有人这样向我解释它...

    摘要: 理解JS闭包. 原文:我从来不理解JavaScript闭包,直到有人这样向我解释它... 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 正如标题所述,JavaScript闭包 ...

  3. 我从来不理解 JavaScript 闭包,直到有人这样向我解释它...

    正如标题所述,JavaScript 闭包对我来说一直有点神秘,看过很多闭包的文章,在工作使用过闭包,有时甚至在项目中使用闭包,但我确实是这是在使用闭包的知识. 最近看国外的一些文章,终于,有人用于一种 ...

  4. JavaScript闭包,只学这篇就够了

    # 闭包不是魔法 这篇文章使用一些简单的代码例子来解释JavaScript闭包的概念,即使新手也可以轻松参透闭包的含义. 其实只要理解了核心概念,闭包并不是那么的难于理解.但是,网上充斥了太多学术性的 ...

  5. JavaScript闭包只学这篇就够了

    闭包不是魔法 这篇文章使用一些简单的代码例子来解释JavaScript闭包的概念,即使新手也可以轻松参透闭包的含义. 其实只要理解了核心概念,闭包并不是那么的难于理解.但是,网上充斥了太多学术性的文章 ...

  6. javascript深入理解js闭包(转载)

    此篇文章来源于http://www.jb51.net/article/24101.htm 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全 ...

  7. javascript闭包—围观大神如何解释闭包

    闭包的概念已经出来很长时间了,网上资源一大把,本着拿来主意的方法来看看. 这一篇文章 学习Javascript闭包(Closure) 是大神阮一峰的博文,作者循序渐进,讲的很透彻.下面一一剖析. 1. ...

  8. 最简明的JavaScript闭包解释

    最简明的JavaScript闭包解释 JavaScript是这几年最火的编程语言之一,从前端到服务器端,再到脚本,好像没有一个地方没有JavaScript的身影.这个世界上任何的一种事物的存在必然有其 ...

  9. 上一篇括号配对让人联想起catalan数,顺便转载一篇归纳的还不错的文章

    转载请注明来自souldak,微博:@evagle 怎么样才是合法的组合? 只要每一时刻保证左括号的数目>=右括号的数目即可. 直接递归就行,每次递归加一个括号,左括号只要还有就能加,右括号要保 ...

随机推荐

  1. Windows下搭建kafka

    安装JDK,跳过 kafka依赖zookeeper,此处zookeeper安装跳过 http://kafka.apache.org/downloadskafka下载地址 解压文件(我的目录是D:\mi ...

  2. mysql slave节点多线程复制

    线上一个mysql主备延迟很大,master节点写入频繁,slave节点积累大量relay-log无法即使写入. 参考:https://www.cnblogs.com/conanwang/p/6006 ...

  3. Java使用SSH远程访问Windows并执行命令

    转载于:http://blog.csdn.net/carolzhang8406/article/details/6760430   https://blog.csdn.net/angel_xiaa/a ...

  4. typedef 复杂函数指针

    下面是三个变量的声明,我想使用typedef分别给它们定义一个别名,请问该如何做? >1:int *(*a[5])(int, char*); >2:void (*b[10]) (void ...

  5. python3 实现简单ftp服务功能(客户端)

    转载请注明出处! 可执行的命令: lspwdcd put rm get mkdir 上传下载,显示进度百分比以及平均上传下载速度 客户端 main代码: #Author by Andy #_*_ co ...

  6. Java 基础 - 自动装箱,valueOf装箱,new -->使用 == 和 equals比较

    总结 关于equals 比较  记住:equals方法比较的是真正的值 两个包装类比较,比较的是包装的基本数据类型的值 基本数据类型和包装类型比较时,会先把基本数据类型包装后再比较 (但是因为equa ...

  7. while循环语句基础

    while循环语句基础 一while循环语句介绍 循环语句命令常用于重复执行一条指令或一组指令,直到条件不再满足时停止,   Shell脚本语言的循环语句常见的有while, until, for及s ...

  8. 绝不划水队Alpha冲刺阶段博客目录

    绝不划水队Alpha冲刺阶段博客目录 一.Scrum Meeting 第六周会议记录 第七周会议记录 二.测试报告 alpha阶段测试报告 三.习得的软工原理/方法/技能? 李浩杰: (1)一开始分工 ...

  9. sql server 建表,增删改练习

    use master --drop database Class create database Class on primary( name='Class', filename='D:\SQLTes ...

  10. c#将数据导出到excel中

    DataTable   dt = new BLL.T_Expiry().GetAllList().Tables[0];//查询数据 string FileName = "T_Users.xl ...