我相信学过Javascript这门语言的程序员应该都对Closure这个概念有所了解,然而网上以及各种Javascript书籍里面对Closure这个概念的定义有各种说法。我本人觉得很多地方对Closure这个概念的定义都是片面的,目前看到的比较全面准确的定义应该是Wikipedia上面的定义了,但是Wikipedia上面的定义不是很好理解。

我通过网上查阅了些资料后结合Wikipedia的定义,下面给出我自己对Closure这个概念的理解。

要想正确理解闭包必须要先对一些概念有所了解:

非本地变量(non-local variables):

又叫自由变量(free variables),是一个既不在本地作用域也不在全局作用域里的变量,通常存在于嵌套或匿名函数的上下文对象里面。

第一类函数(First-Class Functions):

在编程语言里,如果函数也能够像其他数据类型一样被操纵如在运行时被构造、被赋值给一个变量、能够被当做参数传递或者被其他函数返回,那么这种类型的函数即为第一类函数;通常闭包出现在支持这种类型函数的语言中。如:

var foo = function() {
alert("Hello World!");
};
var bar = function(arg) {
return arg;
};
bar(foo)();

内部函数(Inner Functions):

内部函数也叫做嵌套函数,是一种被定义在另外一个函数体(也叫外部函数)里面的函数。每一次外部函数被调用的时候,就会创建一个内部函数的实例。如:

function outer(info){
  //print is inner function
  function print(msg){
   return "Error:"+msg;  
}
return print(info);
}

内部函数有一个非常重要的特性就是它可以隐式的访问外部函数的作用域,这意味着内部函数能够访问外部函数的参数、本地变量等。

词法环境(Lexical environment):

词法环境是一种特殊的内部对象,在某一代码块里面的所有本地函数、函数参数及本地变量都是该内部对象的属性。在浏览器里最顶层代码块里面的词法环境对象是window,而window对象有一个隐含的属性[[scope]]保存上下文作用域,对window来说[[scope]]应该为null,而对最顶层代码块里面定义的函数来说,当函数被创建时的词法环境里面的[[scope]]属性就是window对象。

如下代码所示:

//这个位置的词法环境是window对象
//在代码执行前window={f:function...,a:undefined,g:undefined} 注:这里省略了其他不相关的属性
var a = 5;
function f(arg){//创建f函数时添加隐藏属性f.[[scope]]=window
  //调用函数时该函数的词法环境对象为{arg:...}
alert("f:"+arg);  
}
var g = function(arg){
  alert("g:"+arg);
}
f();//每次调用时创建自己的词法环境,并通过[[scope]]属性形成作用域链

什么是闭包:

如果不了解词法环境这个概念,就很难理解Wikipedia上面对Closure实现的定义:

Closures are typically implemented with a special data structure that contains a pointer to the function code, plus a representation of the function's lexical environment。

翻译出来后就是:闭包就是一个对象(一种特殊的数据结构),它包含一个指向构成该闭包的函数的指针以及在闭包创建时外部函数的词法环境的引用。同时在闭包里面访问的外部函数词法环境里面属性也叫做非本地变量。

什么时候会创建闭包:

当在一个内部函数被暴露到定义它的外部函数的外面时,也就是说如果一个内部函数能够从定义它的函数的外面被访问时,这个时候闭包就被创建了(这里说创建是因为闭包也是一个对象),同时该闭包函数的[[scope]]属性有对外部函数词法环境的引用,所以在外部函数调用结束后,闭包依然能够访问外部函数的词法环境对象。

闭包大部分的时候都是通过函数调用返回一个内部函数的形式创建,那么下面的代码有产生闭包吗?

function Person(name) {
this._name = name;
this.getName = function() {
return this._name;
};
} var p = new Person();

什么时候使用闭包:

Javascript里面的闭包可以做许多有用的事情,如配置回调函数或者模仿Java语言中的private data等等。

Callback function:

window.addEventListener("load", function() {
var showMessage = getClosure("some message<br />");
window.setInterval(showMessage, 1000);
}); function getClosure(message) {
function showMessage() {
document.getElementById("message").innerHTML += message;
}
return showMessage;
}

Private data:

function Person(name) {
var _name = name;
this.getName = function() {
return _name;
};
}

什么时候不使用闭包或闭包的误用:

对于初学者来说最经典的就是Loop循环:

window.addEventListener("load", function() {
for (var i = 1; i < 4; i++) {
var button = document.getElementById("button" + i); button.addEventListener("click", function() {
alert("Clicked button " + i);
});
}
});

上面代码的错误非常明显,因为在循环里面的事件绑定回调函数都引用了相同的外部函数词法环境,当循环结束后变量i的值为4,所以当事件触发时,弹出来的肯定都是Clicked button 4这条消息了。

使用闭包同样要注意内存泄露问题,在闭包里面一不小心就会形成循环引用的问题如:

function setHandler() {
var elem = document.getElementById('id')
elem.onclick = function() {
...
}
}

在上面的代码,elem通过onclick引用一个函数,同时在函数里面通过[[scope]]引用到外部函数的词法环境。

如果在构造器里面有方法但没用使用到private data,那么最好将其移至prototype里面去,因为每次实例化该构造器的一个对象都会在this对象中创建相应的函数对象,如:

function Person(name) {
var _name = name;
this.getName = function() {
return _name;
};
this.sayHello = function() {
alert("Hello!");
};
} //这里sayHello并有使用private data,为了避免每次new Person都创建sayHello函数,那么可以将其移至prototype里面 function Person(name) {
var _name = name;
this.getName = function() {
return _name;
};
} Person.prototype.sayHello = function() {
alert("Hello!");
};

注意:如果内部函数使用的是new Function()那么它的[[scope]]则不会引用外部函数的词法环境,而是window对象。

window.a = 1
function getFunc() {
var a = 2
var func = new Function('', 'alert(a)')
return func
} getFunc()() // 1, from window

Javascript中Closure及其相关概念的更多相关文章

  1. 在Javascript中闭包(Closure)

    在Javascript中闭包(Closure) 什么是闭包 “官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. ...

  2. JavaScript中的闭包(closure)

    闭包的特性 1.函数嵌套函数 2.函数内部可以引用外部的参数和变量 3.参数和变量不会被垃圾回收机制回收  闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成内存泄露,主要用于私有的方法和变 ...

  3. javascript中的闭包closure详解

    目录 简介 函数中的函数 Closure闭包 使用闭包实现private方法 闭包的Scope Chain 闭包常见的问题 闭包性能的问题 总结 简介 闭包closure是javascript中一个非 ...

  4. 理解JavaScript中的“this”

    对于javascript的初学者来说,一般对“this”关键字都感到非常迷惑.本文的目的旨在让你全面的了解“this”,理解在每一个情景下如何使用“this”,希望通过本文,可以帮助同学们不在害怕“t ...

  5. JavaScript闭包(Closure)

    JavaScript闭包(Closure) 本文收集了多本书里对JavaScript闭包(Closure)的解释,或许会对理解闭包有一定帮助. <你不知道的JavsScript> Java ...

  6. JavaScript中的匿名函数及函数的闭包

    1.匿名函数 函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途.匿名函数:就是没有函数名的函数. 1.1 函数的定义,首先简单介绍一下函数的定义,大致可分为三种方式 第一种: ...

  7. JavaScript中的闭包和匿名函数

    JavaScript中的匿名函数及函数的闭包   1.匿名函数 2.闭包 3.举例 4.注意 1.匿名函数 函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途.匿名函数:就是没 ...

  8. JavaScript中Eval()函数的作用

    这一周感觉没什么写的,不过在研究dwz源码的时候有一个eval()的方法不是很了解,分享出来一起学习 -->首先来个最简单的理解 eval可以将字符串生成语句执行,和SQL的exec()类似. ...

  9. javascript中的闭包解析

    学习javaScript已经有一段时间了,在这段时间里,已经感受到了JavaScript的种种魅力,这是一门神奇的语言,同时也是一门正在逐步完善的语言,相信在大家的逐步修改中,这门语言会逐步的完善下去 ...

随机推荐

  1. Mathematica 迭代函数

    学习Mathematica迭代函数的几个画图例子: 1.三角形沿着某一点旋转 verticse = {{0, 0}, {1, 0}, {1/2, Sqrt[3]/2}}; tri = Line[ver ...

  2. 高并发下linux ulimit优化

    系统性能一直是一个受关注的话题,如何通过最简单的设置来实现最有效的性能调优,如何在有限资源的条件下保证程序的运作,ulimit 是我们在处理这些问题时,经常使用的一种简单手段.ulimit 是一种 l ...

  3. 动态改变UITabBarController的菜单文字

    有时候项目可能涉及到使用多种语言,如简体.繁体.为了适应这种情况我用到了Localizable.strings,然后在不同的语言版本文件内定义相应的内容(这就不说了,可以参考:http://www.c ...

  4. Thumbnailator java图片压缩,加水印,批量生成缩略图

    地址:http://code.google.com/p/thumbnailator/ 1.指定大小进行缩放 //size(宽度, 高度) /* * 若图片横比200小,高比300小,不变 * 若图片横 ...

  5. Oracle Statistic 统计信息 小结

    oraclestatisticstabledatabasesqldictionary   目录(?)[-] 直方图上列的信息说明 直方图类型说明   一.  Statistic 说明 Oracle 官 ...

  6. 有道词典 纯净版 - imsoft.cnblogs

    软件名称:有道词典软件版本:5.4 正式版(5.4.46.5554)软件主页:http://cidian.youdao.com/软件定制:飞扬时空更新日期:2013年8月26日 软件简介: 有道词典是 ...

  7. ZOJ 3432 Find the Lost Sock (异或的运用)

    Alice bought a lot of pairs of socks yesterday. But when she went home, she found that she has lost ...

  8. Balloons Colors

    题目大意:ACMer总觉得题目难度与气球的颜色有关,比如最简单的题目颜色是红色,而最难的题目是黑色的.为了让这个谣言被打破,决定添加一个约束: 气球从1到N编号 题目从1到N编号 接下来给出 N X ...

  9. 一篇文章入门Jmeter性能测试【经典长文】

    孟船长  目录 1.性能测试定义2.为什么要做性能测试3.性能测试指标.性能测试分类4.Jmeter性能测试实战[入门级]5.参考文章链接 1.性能测试定义 百度&知乎 性能测试是通过自动化的 ...

  10. cin和cout详解

    无论输入数字还是字符串,一个回车键是把输入的这个东西送到变量中,可以一次性送到 一个(或者多个)空格键是分隔这些值的 cout <<N; for(int i=0;i<5;i++) { ...