V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
darluc
V2EX  ›  JavaScript

搞不明白的 javascript 闭包

  •  
  •   darluc · 2016-09-03 23:39:38 +08:00 · 2804 次点击
    这是一个创建于 3036 天前的主题,其中的信息可能已经有所发展或是发生改变。

    去我的博客读一读

    在 Javascript 语言中,闭包就是一个函数,只是它上下文中的变量以引用的形式与其绑定在了一起。

    function getMeAClosure() {
      var canYouSeeMe = "here I am";
      return (function theClosure() {
        return {canYouSeeIt: canYouSeeMe ? "yes" : "no"};
      });
    }
    
    var closure = getMeAClosure();
    closure().canYouSeeIt; //"yes"
    

    实际上每个 Javascript 函数在生成的时候都形成了闭包。稍后我会给大家解释闭包的产生原因和过程,然后纠正一些关于闭包的错误概念,最后再给出一些闭包的实际用例。不过首先简单介绍一下闭包相关的基础概念: Javascript 的闭包是通过 词法域 (lexical scope)变量环境 (VariableEnvironment) 实现的。

    词法域( Lexical Scope )

    “词法”这个词一般都是语言相关。所以函数的词法域是静态的,是由函数代码在源代码中的位置决定的。

    参考以下代码:

    var x = "global";
    function outer() {
      var y = "outer";
      function inner() {
        var x = "inner";
      }
    }
    

    函数 inner 在代码中被函数 outer 包裹着,而 outer 又被全局上下文包含在内。这样就形成了一个词法继承关系:

    global

    — outer

    —— inner

    每个函数的外部词法域都是由词法继承关系中它的祖先决定的。因此,inner 函数的外部词法域就是由全局对象和函数 outer 组成的。

    变量环境( VariableEnvironment )

    全局对象有一个相关的执行上下文。而且每一次函数调用也会建立并进入一个新的执行上下文。这个执行上下文相对于静态的词法域是动态生成的。每一个执行上下文都确定了一个变量环境,它是在该上下文中所声明变量的容器。(ES 5 10.4 , 10.5 )

    注意,在 EcmaScript 3 中,函数的变量环境( VariableEnvironment )被称为活动对象( ActivationObject )

    以下伪代码可以用来描述变量环境

    //variableEnvironment: {x: undefined, etc.};
    var x = "global"
    //variableEnvironment: {x: "global", etc.};
    
    function outer() {
      //variableEnvironment: {y: undefined};
      var y = "outer";
      //variableEnvironment: {y: "outer"};
      
      function inner() {
        //variableEnviroment: {x: undefined};
        var x = "inner";
        //variableEnvironment: {x: "inner"};
      }
    }
    

    不过,这只描述了整个图景中的一部分。每个变量环境都会继承它所属词法域的变量环境。

    [[scope]]属性

    当一个函数定义过程发生在某个执行上下文环境中时,会生成一个新的函数对象,此函数对象会包含一个名为 [[scope]] 的内部属性引用当前的变量环境。(ES 5 13.0-2 )

    每个函数都有这样一个 [[scope]] 属性,而且当函数被调用时,这个 [[scope]] 属性会被赋值给变量环境的 outerLex 属性,该属性是对外层词法环境的引用( outer lexical environment reference 简写为 outerLex )。这样一来,每个变量环境都继承了它父级的变量环境。这个 [[scope]] 链会一直延伸至全局对象,与词法继承的长度一样。

    现在让我们再来看一下伪代码:

    继续阅读

    7 条回复    2016-09-05 17:45:07 +08:00
    ericls
        1
    ericls  
       2016-09-04 00:30:54 +08:00 via iPhone
    不是有 let 吗
    darluc
        2
    darluc  
    OP
       2016-09-04 10:25:24 +08:00
    @ericls 文章比较老了,我这是翻译过来的
    wensonsmith
        3
    wensonsmith  
       2016-09-04 11:39:10 +08:00
    这个写的太复杂,不通俗易懂,举得列子也不好。

    阮一峰写的挺好 http://es6.ruanyifeng.com/#docs/let

    还有这个教程里面的 https://segmentfault.com/a/1190000004365693
    wshcdr
        4
    wshcdr  
       2016-09-04 14:13:25 +08:00
    mark 一下
    sahrechiiz
        5
    sahrechiiz  
       2016-09-04 20:40:44 +08:00
    博客蛮好看的 是什么程序?
    aitaii
        6
    aitaii  
       2016-09-05 10:08:58 +08:00
    看样子像 Hexo 。
    darluc
        7
    darluc  
    OP
       2016-09-05 17:45:07 +08:00
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3685 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 05:06 · PVG 13:06 · LAX 21:06 · JFK 00:06
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.