和其他大多数现代编程语言一样,JS也采用词法作用域,也就是说,函数的执行依赖于变量作用域,这个作用域是在函数定义时决定的,而不是函数调用时决定的。为了实现这种词法作用域,JS函数对象的内部状态不仅包含函数的代码逻辑,还必须引用当前的作用域链。函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学中称为“闭包”。

  理解闭包首先要了解嵌套函数的词法作用域规则。看一下下面的代码:

var scope="global scope";
function checkscope(){
var scope="local scope";
function f(){return scope;}
return f();
}
checkscope()

  checkscope()函数声明了一个局部变量,并定义了一个函数f(),函数f()返回了这个变量的值,最后将其执行结果返回。明显返回结果将是“local scope”。现在对代码做一些改动:

var scope="global scope";
function checkscope(){
var scope="local scope";
function f(){return scope;}
return f;
}
checkscope()()

  在这段代码中,将函数内的一对圆括号移动到了checkscope()之后,checkscope()仅仅返回函数内嵌套的一个函数对象,而不是直接返回结果。在定义函数的作用域外调用这个嵌套的函数会发生什么?

  注意:函数的执行依赖于变量作用域,这个作用域是在函数定义时决定的,而不是函数调用时决定的。嵌套的函数f()定义在这个作用域链里,scope一定是局部变量,无论何时何地调用f(),返回都是“local scope”。

  闭包的特性:它们可以捕捉到局部变量和参数,并一直保存下来,看起来像这些变量绑定到了其中定义它们的外部函数。

为什么在闭包中,外部函数定义的局部变量在函数返回后依然存在?

每次调用JS函数的时候,都会为之创建一个新的对象用来保存局部变量,把这个对象添加到作用域链中。当函数返回的时候,就从作用域链中将这个绑定变量的对象删除。如果不存在嵌套的函数,也没有其他引用指向这个绑定对象,他就会被当作垃圾回收掉。如果定义了嵌套的函数,每个嵌套的函数都各自对应一个作用域链,并且这个作用域链指向一个变量绑定对象。但如果这些嵌套的函数对象在外部函数中保存下来,那么它们也会和所指向的变量绑定对象一样当作垃圾回收。但是如果这个函数定义了嵌套的函数,并将它作为返回值返回或者存储在某处的属性里,这时就会有一个外部引用指向这个嵌套的函数,它就不会被当作垃圾回收,并且它所指向的变量绑定对象也不会被当作垃圾回收。

  

  使用闭包技术可以实现私有状态的共享。

//这个函数给对象o增加了属性存储器方法
//方法的名称为get<name>和set<name>。如果提供了一个判定函数,
//setter方法就会用它来检测参数的合法性,然后再存储它,
//如果判定函数返回false,setter方法抛出一个异常。
//
//这个函数有一个非同寻常之处,就是getter和setter函数
//所操作的属性值并没有存储在对象o中,
//相反,这个值仅仅是保存在函数中的局部变量中
//getter和setter方法同样是局部函数,因此可以访问这个局部变量
//也就是说,对于两个存储方法来说,这个变量是私有的,
//没有办法绕过存储器方法来设置或修改这个值。
function addPrivateProperty(o,name,predicate){
var value;//这是一个属性值 //getter方法简单地将其返回
o["get"+name]=function(){return value;}; //setter方法首先检查值是否合法,若不合法就抛出异常
//否则就将其存储起来
o["set"+name]=function(v){
if(predicate&&!predicate(v))
throw Error("set"+name+":invalid value "+v);
else
value=v;
};
} //下面的代码展示了addPrivateProperty()方法
var o={};//设置一个空对象 //增加属性存储器方法getName()和setName()
//确保只允许字符串值
addPrivateProperty(o,"Name",function(x){return typeof x=="string";}); o.setName("Frank");//设置属性值
console.log(o.getName());//得到属性值
o.setName(1);//试图设置一个错误类型的值

  在同一个作用域链中定义两个闭包,这两个闭包共享同样的私有变量或变量,这是一种非常重要的技术。

  

  试图将循环代码移入定义这个闭包的函数之内时要格外小心,看下面的代码:

function constfuncs() {
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs[i] =function () {return i; };
}
return funcs;
}
var funcs = constfuncs();
console.log(funcs[5]());//10

  上面这段代码创建了10个闭包,并将它们存储到一个数组中。这些闭包都是在同一个函数调用中定义的,因此它们可以共享变量i。当constfuncs()返回时,变量i的值是10,所有的闭包都共享这一个值,因此数组中的函数的返回值都是同一个值。关联到闭包的作用域链都是“活动的”,嵌套的函数不会将作用域内的私有成员复制一份,也不会对绑定的变量生成静态快照。

  解决方法可以再使用一个闭包,每次循环都将i的值传入:

function constfuncs() {
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs[i] = (function (i) {
return function () {
return i;
};
})(i);
}
return funcs;
}
var funcs = constfuncs();
console.log(funcs[5]());//5

从一个微信公共号(web前端开发)看到的关于闭包的更通俗的一些解释:

什么是闭包?

基本原理:闭包是由函数引用其周边状态(词法环境)绑在一起形成的(封装)组合结构。在JS中,闭包在每个函数被创建时形成。由于闭包和它的词法环境绑在一起,因此闭包让我们能够从一个函数内部访问外部函数的作用域。要使用闭包,只需要简单的将一个函数定义在另一个函数内部,并将它暴露出来,要暴露一个函数,可以将它返回或者传给其他函数。内部函数将能够访问到外部函数作用域中的变量,即使外部函数已经执行完毕(上文已解释)。

闭包的使用场景:

1、对象的私有数据,如上文。

2、偏函数应用:一个过程,它传给某个函数其中一部分参数,然后返回一个新的函数,该函数等待接受后续参数。

3、柯里化:以后补充。

关于闭包(closure)的一些概念的更多相关文章

  1. 聊一下JS中的作用域scope和闭包closure

    聊一下JS中的作用域scope和闭包closure scope和closure是javascript中两个非常关键的概念,前者JS用多了还比较好理解,closure就不一样了.我就被这个概念困扰了很久 ...

  2. 深入理解JavaScript闭包(closure)

    最近在网上查阅了不少javascript闭包(closure)相关的资料,写的大多是非常的学术和专业.对于初学者来说别说理解闭包了,就连文字叙述都很难看懂.撰写此文的目的就是用最通俗的文字揭开Java ...

  3. [转] Java内部类之闭包(closure)与回调(callback)

    闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域.通过这个定义,可以看出内部类是面向对象的闭包,因为它 不仅包含外围类对象(创建内部类的作用域)的信息,还自动拥 ...

  4. JavaScript 进阶(四)解密闭包closure

    闭包(closure)是什么东西 我面试前端基本都会问一个问题"请描述一下闭包".相当多的应聘者的反应都是断断续续的词,“子函数”“父函数”“变量”,支支吾吾的说不清楚.我提示说如 ...

  5. [转载]学习Javascript闭包(Closure)

    学习Javascript闭包(Closure)     源地址: http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures ...

  6. Swift语言精要-闭包(Closure)

    闭包(Closure)这个概念如果没学过Swift的人应该也不会陌生. 学过Javascript的朋友应该知道,在Javascript中我们经常会讨论闭包,很多前端工程师的面试题也会问到什么是闭包. ...

  7. 闭包(Closure)和匿名函数(Anonymous function)/lambda表达式的区别

    闭包(Closure)和匿名函数(Anonymous function)/lambda表达式的区别 函数最常见的形式是具名函数(named function): function foo(){ con ...

  8. javascript中的闭包closure详解

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

  9. JavaScript闭包(Closure)

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

  10. python 函数对象(函数式编程 lambda、map、filter、reduce)、闭包(closure)

    1.函数对象 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 秉承着一切皆对象的理念,我们再次回头来看函数(function).函 ...

随机推荐

  1. C#工业物联网和集成系统解决方案的技术路线(数据源、数据采集、数据上传与接收、ActiveMQ、Mongodb、WebApi、手机App)

    目       录 工业物联网和集成系统解决方案的技术路线... 1 前言... 1 第一章           系统架构... 3 1.1           硬件构架图... 3 1.2      ...

  2. java web学习总结(二十七) -------------------JSP标签介绍

    一.JSP标签介绍 JSP标签也称之为Jsp Action(JSP动作)元素,它用于在Jsp页面中提供业务逻辑功能,避免在JSP页面中直接编写java代码,造成jsp页面难以维护. 二.JSP常用标签 ...

  3. JAVA的序列化和持久化的区别与联系

      持久化(Persistence) 即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘).持久化的主要应用是将内存中的对象存储在关系型的数据库中,当然也可以存储在磁盘文件中.XML数据文 ...

  4. Entity Framework之IQueryable和list本地集合

    我们来说一下Iqueryable集合和List等本地集合的区别,下面我们通过建立一个简单的例子来学习这个知识点,直接进入主题吧 1.首先对比一下两段代码?看一下有什么结果: (1) 第一段代码如图所示 ...

  5. 转载: 黄聪:C#中 Excel列字母与数字的转换

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  6. JavaScript & PHP模仿C#中string.format效果

    1.JavaScript function stringformat() { var args = Array.prototype.slice.call(arguments); if (args.le ...

  7. Oracle数据库的 增、删、改、查

    有时候数据库的查询语句一时想不起来,或不确定是不是语句写的正确,现在整理了一下标准的基本查询语句,便于以后牢记: .数据操作语言 DML:添加(insert into).修改(update   set ...

  8. Ubuntu 14 Trusty安装hue

    想开始学习一下hive,需要一个使用起来方便的客户端,网上搜了一下发现hue是个很常用的工具.于是,就在自己的ubuntu14系统里,尝试安装hue.下面就是自己的安装步骤,记录如下: 1.先查看自己 ...

  9. 将自己的项目上传到github保管

    一.首先需要生成gey-gen,因为github跟本地计算机要通过ssh传输的,详情见官方教程. 二.本地已有项目的情况:现在本地将项目git innit 初始化好,然后去到github上面初始化一个 ...

  10. 【Linux管理】用户管理

    每次玩linux都会去网上找一些命令,想想应该记录一下,希望方便大家,当然更方便自己. 1.添加用户 useradd username//添加用户 passwd username//设置密码 2.配置 ...