JavaScript中的闭包(Closure)详细介绍

闭包是JavaScript中一个重要的特性,其最大的作用在于保存函数运行过程中的信息。在JavaScript中,闭包的诸多特性源自函数调用过程中的作用域链上。

函数调用对象与变量的作用域链

对于JavaScript中的每一次函数调用,JavaScript都会创建一个局部对象以储存在该函数中定义的局部变量;如果在该函数内部还有一个嵌套定义的函数(nested function),那么JavaScript会在已经定义的局部对象之上再定义一个嵌套局部对象。对于一个函数,其内部有多少层的嵌套函数定义,也就有多少层的嵌套局部对象。该局部对象称为“函数调用对象”(ECMAScript 3中的“call object”,ECMAScript 5中改名为“declarative environment record”,但个人认为还是ECMAScript 3中的名称更容易理解一些)。以下面的函数调用为例:

代码如下:

function f(x){
  var a = 10;
  return a*x;
}
console.log(f(6));//60

在这个简单的例子中,当调用f()函数时,JavaScript会创建一个f()函数的调用对象(姑且称之为f_invokeObj),在f_invokeObj对象内部有两个属性:a和x;运行f()时,a值为10而x值为6,因此最后的返回结果为60。图示如下:

当存在函数嵌套时,JavaScript将创建多个函数调用对象:

代码如下:

function f(x){
  var a = 10;
  return a*g(x);
  function g(b){
    return b*b;
  }
}
console.log(f(6));//360

在这个例子中,当调用f()函数时,JavaScript会创建一个f()函数的调用对象(f_invokeObj),其内部有两个属性a和x,a值为10而x值为6;运行f()时,JavaScript会对f()函数中的g()函数进行解析定义,并创建g()的调用对象(g_invokeObj),其内部有一个属性b,b值与传入参数x相同为6,因此最后的返回结果为360。图示如下:

可以看到,函数调用对象形成了一条链。当内嵌函数g()运行,需要获取变量值的时候,会从最近的函数调用对象中开始进行搜索,如果无法搜索到,则沿函数调用对象链在更远的调用对象中进行搜寻,此即所谓的“变量的作用域链”。如果两个函数调用对象中出现相同的变量,则函数会取离自己最近的那个调用对象中的变量值:

代码如下:

function f(x){
  var a = 10;
  return a*g(x);
  function g(b){
    var a = 1;
    return b*b*a;
  }
}
console.log(f(6));//360, not 3600

在上面的例子中,g()函数的调用对象(g_invokeObj)和f()函数的调用对象(f_invokeObj)中均存在变量a且a的值不同,当运行g()函数时,在g()函数内部所使用的a值为1,而在g()函数外部所使用的a值则为10。图示此时的函数调用对象链如下:

什么是闭包?

在JavaScript中所有的函数(function)都是对象,而定义函数时都会产生相应的函数调用对象链,一次函数定义对应一个函数调用对象链。只要函数对象存在,相应的函数调用对象就存在;一旦某函数不再被使用,相应的函数调用对象就会被垃圾回收掉;而这种函数对象和函数调用对象链之间的一一组合,就称之为“闭包”。在上面f()函数和g()函数的例子中,就存在两个闭包:f()函数对象和f_invokeObj对象组成了一个闭包,而g()函数对象和g_invokeObj-f_invokeObj对象链一起组成了第二个闭包。当g()函数执行完毕后,由于g()函数不再被使用,因此g()闭包被垃圾回收了;之后,当f()函数执行完毕后,由于同样的原因,f()闭包也被垃圾回收了。

从闭包的定义可以得出结论:所有的JavaScript函数在定义后都是闭包 – 因为所有的函数都是对象,所有的函数在执行后也都有其对应的调用对象链。

不过,令闭包真正发挥作用的是嵌套函数的情况。由于内嵌函数是在外部函数运行的时候才开始定义的,因此内嵌函数的闭包中所保存的变量值(尤其是外部函数的局部变量值)是这次运行过程中的值。只要内嵌函数对象依然存在,那么其闭包就依然存在(闭包中的变量值不会发生任何改变),从而也就实现了保存函数运行过程的信息这个目的。考虑以下这个例子:

代码如下:

var a = "outside";
function f(){
  var a = "inside";
  function g(){return a;}
  return g;
}
var result = f();
console.log(result());//inside

在这个例子中,当运行f()函数时,g()函数被定义,同时创建了g()函数的闭包,g()闭包包含了g_invokeObj-f_invokeObj对象链,因此保存了f()函数执行过程中的变量a的值。当执行console.log()语句时,由于g函数对象仍然存在,因此g()闭包也依然存在;当运行这个仍然存在的g函数对象时,JavaScript会使用依然存在的g()闭包并从中获取变量a的值(“inside”)。

(0)

相关推荐

  • 理解javascript函数式编程中的闭包(closure)

    闭包(closure)是函数式编程中的概念,出现于 20 世纪 60 年代,最早实现闭包的语言是 Scheme,它是 LISP 的一种方言.之后闭包特性被其他语言广泛吸纳. 闭包的严格定义是"由函数(环境)及其封闭的自由变量组成的集合体."这个定义对于大家来说有些晦涩难懂,所以让我们先通过例子和不那么严格的解释来说明什么是闭包,然后再举例说明一些闭包的经典用途. 什么是闭包 通俗地讲, JavaScript 中每个的函数都是一个闭包,但通常意义上嵌套的函数更能够体 现出闭包的特性,请看

  • javascript中闭包(Closure)详解

    在javascript中,函数可看作是一种数据,可以赋值给变量,可以嵌套在另一个函数中. var fun = function(){ console.log("平底斜"); } function fun(){ var n=10; function son(){ n++; } son(); console.log(n); } fun(); //11 fun(); //11 我们把上面第二段代码稍微修改下: var n=10; function fun(){ function son(){

  • 学习Javascript闭包(Closure)知识

    一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. function f1(){ var n=999; } alert(n); // error 这里有一个地方需要注意,函数内部声明变量的时候,

  • javascript闭包(Closure)用法实例简析

    本文实例讲述了javascript闭包(Closure)用法.分享给大家供大家参考,具体如下: closure被翻译成"闭包",感觉这东西被包装的太学术化.下面参考书本和网上资源简单探讨一下(理解不当之处务请留意). 1.什么是闭包 官方的回答:所谓"闭包",指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 看了上面的定义,如果你不是高手,我坚信你会和我一样愤怒的质问:这tmd是人话吗? 要理解闭包,还是代码

  • 深入理解JavaScript系列(16) 闭包(Closures)

    介绍 本章我们将介绍在JavaScript里大家经常来讨论的话题 -- 闭包(closure).闭包其实大家都已经谈烂了.尽管如此,这里还是要试着从理论角度来讨论下闭包,看看ECMAScript中的闭包内部究竟是如何工作的. 正如在前面的文章中提到的,这些文章都是系列文章,相互之间都是有关联的.因此,为了更好的理解本文要介绍的内容,建议先去阅读第14章作用域链和第12章变量对象. 英文原文:http://dmitrysoshnikov.com/ecmascript/chapter-6-closu

  • JavaScript 匿名函数(anonymous function)与闭包(closure)

    引入 匿名函数 闭包 变量作用域 函数外部访问函数内部的局部变量 用闭包实现私有成员 引入 闭包是用匿名函数来实现.闭包就是一个受到保护的变量空间,由内嵌函数生成."保护变量"的思想在几乎所有的编程语言中都能看到. 先看下 JavaScript 作用域: JavaScript 具有函数级的作用域.这意味着,不能在函数外部访问定义在函数内部的变量. JavaScript 的作用域又是词法性质的(lexically scoped).这意味着,函数运行在定义它的作用域中,而不是在调用它的作用

  • JavaScript 闭包深入理解(closure)

    一.什么是闭包? "官方"的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.相信很少有人能直接看懂这句话,因为他描述的太学术.其实这句话通俗的来说就是:JavaScript中所有的function都是一个闭包.不过一般来说,嵌套的function所产生的闭包更为强大,也是大部分时候我们所谓的"闭包".看下面这段代码: function a() { var i = 0; function b() { a

  • Javascript闭包(Closure)详解

    下面就是我的学习笔记,对于Javascript初学者应该是很有用的. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. function f1(){ var n=999; } alert(n)

  • JavaScript中的闭包(Closure)详细介绍

    闭包是JavaScript中一个重要的特性,其最大的作用在于保存函数运行过程中的信息.在JavaScript中,闭包的诸多特性源自函数调用过程中的作用域链上. 函数调用对象与变量的作用域链 对于JavaScript中的每一次函数调用,JavaScript都会创建一个局部对象以储存在该函数中定义的局部变量:如果在该函数内部还有一个嵌套定义的函数(nested function),那么JavaScript会在已经定义的局部对象之上再定义一个嵌套局部对象.对于一个函数,其内部有多少层的嵌套函数定义,也

  • 学习JavaScript中的闭包closure应该注意什么

    目录 闭包简述 1.闭包使得内部函数可以访问外部函数的属性(变量或方法) 2.闭包的广阔应用场景 3.用闭包模拟私有方法 4.从性能角度考虑,非必要不使用闭包 闭包简述 Mozilla 上这样解释闭包:一个函数和对其周围状态(lexical environment,词法环境) 的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure). 也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域.在 JavaScript 中, 每当创建一个函数, 闭包就会在函数创建的同时

  • JavaScript中的方法调用详细介绍

    JavaScript中,如果function属于一个对象,那么通过对象来访问该function的行为称之为"方法调用".与普通的函数调用不同的是,在进行方法调用时,function中的this指代将发生变化 - this将指代用于调用该function的对象(该对象将成为方法调用的invocation context): 复制代码 代码如下: var x = 99; var sample = {   x:1,   act:function(a){     this.x = a*a;//

  • Javascript中的delete操作符详细介绍

    一.变量 说到javascript中的delete操作符,还是首先要搞清楚javascript中的变量和属性之间的关系. javascript中,变量和对象属性关系非常微妙,甚至可以很多时候会被等同起来,因为 javascript 在执行脚本之前会创建一个global对象,在浏览器中就是window对象,所有的全局变量都是这个global对象的属性,执行函数时也会创建一个activation对象,所有的局部变量都是这个activation对象的属性.这些可以大家可以去了解一下javascript

  • JavaScript中的类(Class)详细介绍

    在JavaScript中,可以使用类(Class)来实现面向对象编程(Object Oriented Programming).不过,JavaScript中的类与Java中的有所不同,其相应的定义和使用也不一样. JavaScript中类的定义 在JavaScript中,所有从同一个原型对象(prototype)处衍生出来的对象组成了一个类:也就是说,JavaScript中的类是一个对象集合的概念,如果两个对象它们的prototype相同,那么它们就属于同一个类:JavaScript中的类甚至都

  • JavaScript中的值类型详细介绍

    计算机程序的实质很大程度上可以说是机器对各种信息(值)的操作与读写.在JavaScript中,存在多种类型的值,这些值分成两大类:Primitive(基本类型)和Object(对象). Primitive JavaScript中Primitive有5种类型: 1.Number.所有的数字,无论是整数还是小数,均为Number类型. 2.String.字符串类型. 3.Boolean.布尔类型,true或者false. 4.null.此类型只有null一个值. 5.undefined.此类型只有u

  • canvas 画布在主流浏览器中的尺寸限制详细介绍

    canvas 画布在主流浏览器中的尺寸限制详细介绍 通过测试发现,canvas在不同浏览器下面有不同的最大尺寸限制. 大家都知道,canvas有自身的width,height属性来控制尺寸,用css的width,height,控制显示的大小.可以理解为canvas就是一个img,属性的width,height就是这个img的原图像素大小.但在各浏览器下,设置canvas尺寸时发现有最大尺寸限制.测试一下与大家分享. 测试代码 <!DOCTYPE html> <html> <h

  • JavaScript中的闭包原理分析

    我们来看一个定义: Closure 所谓"闭包",指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 这说明了,JavaScript中的闭包是包含了上下文的函数,也就是说,这个函数的作用基础,是它所处的环境,这是不能超越的,跟线性代数是不是有一点似曾相识的感觉呢? 换个角度看,闭包的作用是为了实现OO.JavaScript中,没有像C++那样的public.private.protect属性标识, 建立起类比较困难."类

  • JavaScript中setter和getter方法介绍

    javascript中的setter.getter是平时接触比较少的方法,其本身也并不是标准方法,只在非ie浏览器里支持(ie内核也许有其他方法可以做到呢?暂时不知其解),但是加以利用可以做许多事情,比如: 1.对数据的访问限制: a.value是对value变量的getter方法调用,如果在getter方法实现中抛出异常,可以阻止对value变量的访问 2.对dom变量进行监听: window.name是一个跨域非常好用的dom属性(大名鼎鼎,详见百度),如果覆盖window.name的set

  • JavaScript中的闭包

    1.什么是闭包 闭包,官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 简单的说,Javascript允许使用内部函数---即函数定义和函数表达式位于另一个函数的函数体内.而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量.参数和声明的其他内部函数.当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包. 闭包的特点 1 函数嵌套函数 2 函数内部可以引用外部的参数和变量 3 参数和变量不会被垃

随机推荐