• 1. 所谓闭包张立理 otaksutay@gmail.comfunction infiniteSequence() { var i = 0; return function() { return i++; } } var increment = infiniteSequence(); console.log(increment()); console.log(increment()); console.log(increment()); // …
  • 2. WARNING!!
  • 3. WARNING非常非常的学术性 大量术语词汇出没 也许永远都用不上 内容并非标准,有所删减 逻辑不是那么清晰 只谈函数,不提eval,不提new Function
  • 4. Summary引言 什么是变量 闭包之表象 闭包之内在 关于垃圾回收
  • 5. (本页无文本内容)
  • 6. (本页无文本内容)
  • 7. 作用域的历史ECMAScript v3Scope ChainVariable ObjectIdentifier ResolutionECMAScript v5Lexical EnvironmentVariable EnvironmentGetIdentifierReference
  • 8. 什么是变量变量是一种关联关系(association) 关联的目标: 符号名(symbolic name) 值(value) 关联是单向的 – 永远不可能根据值找到变量A symbolic name associated with a value and whose associated value may be changed. -- WikipediaIdentifier nameValue ‘GrayZhang’
  • 9. 什么是变量Variable Statement var Identifier = AssignmentExpression FunctionDeclaration function Identifier(FormalParameterList) { FunctionBody } FormalParameterList Identifier, Identifier[, …]
  • 10. 什么是变量var name = ‘GrayZhang’; function add(x, y) { return x + y; }KeywordIdentifierValue (String Literal)
  • 11. 闭包之表象内层函数可以使用外层函数作用域内的变量function outer() { var name = ‘GrayZhang’; function inner() { console.log(‘Hello ‘ + name); } inner(); }外层内层
  • 12. 闭包之内在Q:为什么javascript会有闭包? A:因为ECMAScript中变量解析是一个查找过程,而非绑定过程。 Q:变量存放在哪里? A:Execution Context中的VariableEnvironment。 Q:从哪里查找变量? A:Execution Context中的LexicalEnvironment。 Q:如何查找变量? A:自内向外。
  • 13. ?
  • 14. 可执行代码(Executable Code)Global CodeFunction CodeEval Codevar source = ‘var x = 3;’ + ‘console.log(x);’ eval(source);function sayHello(name) { var prefix = ‘Hello’; var phrases = [prefix, name]; console.log(phrases.join(‘ ‘)); } function getName() { var input = $(‘#name’); return input.val(); } getName();
  • 15. 执行环境(Execution Context)当进入(开始执行)一段可执行代码时,生成一个执行环境对象。 执行环境对象通过栈(Stack)维护。 新建的执行环境对象称为“当前运行的执行环境对象”。function enterCode(code) { var ec = new ExecutionContext(); control.ecStack.push(ec); control.runningEC = ec; control.execute(code); }
  • 16. 执行环境(Execution Context)一个执行环境对象包括: 词法环境 – LexicalEnvironment 变量环境 – VariableEnvironment This绑定 - ThisBindingExecutionContext: { LexicalEnvironment, VariableEnvironment, ThisBinding }
  • 17. 词法环境(LexicalEnvironment)既是一个属性,又是一个类型。 每个执行环境对象都有且仅有一个关联的词法环境对象。 在代码执行过程中,需要解析变量时,通过词法环境对象进行解析,从其环境数据中得到值。 一个词法环境对象包括: 环境数据 – environement records 外层环境 – outer environment
  • 18. 词法环境(LexicalEnvironment)存在2种词法环境的实现类型 DeclarativeEnvironment ObjectEnvironment 区别是ObjectEnvironment可接受指定的对象作为环境数据属性的值?什么情况会出现ObjectEnvironment
  • 19. 变量环境(VariableEnvironment)每个执行环境对象都有且仅有一个关联的变量环境对象。 变量环境仅仅是一个名字,变量环境对象的类型是词法环境(LexicalEnvironment)。 在进入(开始执行)代码时,所有的变量标识符(Identifier)会存放在当前的变量环境对象中。 变量环境中有环境数据属性,但不使用外层环境属性。
  • 20. 环境数据(environment records)存在于词法环境或变量环境中。 包含一个binding object,简单地认为binding object是一个Map对象,保存变量标签符(Identifier)和变量值(Value)的关系。 常用方法: hadBinding(name) – 查看是否有变量绑定 createBinding(name) – 创建一个变量绑定 setBinding(name, value) – 修改变量绑定的值 getValue(name) – 获取变量绑定的值 deleteBinding(name) – 删除一个变量绑定
  • 21. 环境数据(environment records)EnvironmentRecords: { bindingObject: {}, hasBinding: function(name) { return (name in this.bindingObject); }, createBinding: function(name) { this.bindingObject[name] = undefined; }, setBinding: function(name, value) { this.bindingObject[name] = value; }, // … }
  • 22. 环境数据(environment records)存在2种环境数据的实现类型 DeclarativeEnvironmentRecords ObjectEnvironmentRecordsLexicalEnvironmentEnvironmentRecordsObjectEnvironmentDeclaractiveEnvironmentObjectEnvironmentRecordsDeclaractiveEnvironmentRecords
  • 23. 创建词法环境NewDeclarativeEnvironment(e) 用于创建一个DeclarativeEnvironment 进入函数时 执行catch表达式时 NewObjectEnvironment(o, e) 用于创建一个ObjectEnvironment 执行with表达式时
  • 24. 创建词法环境function NewDeclarativeEnvironment(e) { var env = new LexicalEnvironment(); var envRec = new EnvironmentRecords(); envRec.bindingObject = {}; env.environmentRecords = envRec; env.outerEnvironment = e; return env; }function NewObjectEnvironment(o, e) { var env = new LexicalEnvironment(); var envRec = new EnvironmentRecords(); envRec.bindingObject = o; env.environmentRecords = envRec; env.outerEnvironment = e; return env; }
  • 25. 消化一下名词解释完了吗?NO
  • 26. 总结ExecutionContext: { LexicalEnvironment, VariableEnvironment, ThisBinding }EnvironmentRecords: { hasBinding(name), createBinding(name), setBinding(name, value), getValue(name), deleteBinding(name) }LexicalEnvironment: { environmentRecords, outerEnvironment }
  • 27. 函数(Function)是一个对象(Object)。 包含几个特殊的属性 [[Construct]] – new SomeFunction() [[Call]] – someFunction() [[HasInstance]] – o instanceof SomeFunction [[Scope]] – 闭包 [[FormalParameters]] – 参数列表 [[Code]] – 可执行代码 包含可执行代码(Executable Code)和执行状态(State)。
  • 28. 创建函数(Create Function Object)新建一个普通对象(new Object()) 将[[Class]]设为”function” 将[[Prototype]]指向Function.prototype 根据默认的规则,设置[[Call]]、[[Contruct]]及[[HasInstance]]属性 将[[Scope]]设置为当前的LexicalEnvironment对象 设置[[Code]]、[[FormalParameterList]]及name、length、prototype属性
  • 29. 创建函数(Create Function Object)var fn = new Object(); // [[DefaultValue]], [[HasProperty]], etc... initializeAsObject(fn); fn.[[Class]] = 'function'; fn.[[Prototype]] = Function.prototype; fn.[[Call]] = function() { /* ... */ }; fn.[[Construct]] = function() { /* ... */ }; fn.[[HasInstance]] = function() { /* ... */ }; fn.[[Scope]] = control.runningEC.lexicalEnvironment; fn.[[Code]] = functionBody; fn.[[FormalParameterList]] = parameterList; fn.name = functionName; fn.length = parameterList.length; fn.prototype = { constructor: fn };
  • 30. 创建函数(Create Function Object)作用域([[Scope]])是函数对象的一个属性function hello() { var o = {}; o.name = ‘GrayZhang’; return o; } var person = hello(); console.log(person.name);function outer() { var name = ‘GrayZhang’; function say() { alert(name); } return say; } var inner = outer(); // inner.[[Scope]] inner();
  • 31. 进入函数(Entering Function Code)新建一个执行环境。 根据规则设置this绑定。 如果thisArg是null或undefined,设置为global。 如果thisArg不是Object,设置为ToObject(thisArg)。 以函数的[[Scope]]属性为参数,NewDeclarativeEnvironment创建一个LexicalEnvironment对象。 将当前LexicalEnvironment设置为该值。 将当前VariableEnvironment设置为该值。 开始初始化参数及函数内声明的变量。同一对象
  • 32. 进入函数(Entering Function Code)var ec = new ExecutionContext(); if (thisArg == null) { thisArg = global; } if (typeof thisArg !== 'object') { thisArg = ToObject(thisArg); } ec.thisBinding = thisArg; var localEnv = NewDeclarativeEnvironment(fn.[[Scope]]); ec.lexicalEnvironment = localEnv; ec.variableEnvironment = localEnv; initializeBinding(fn.[[Code]], fn.[[FormalParameterList]]);
  • 33. 进入函数(Entering Function Code)执行函数时,在作用域([[Scope]])的基础上添加词法环境(LexicalEnvironment)Global EnvironmentOuter EnvironmentCurrent Environment// Global Environment function outer() { // Outer Environment function inner() { // Current Environment } } var inner = outer(); // [[Scope]] === Outer Environment inner();
  • 34. 定义绑定初始化 (Declaration Binding Instantiation)从Hosting Behavior说起……function sayHello(name) { if (!name) { throw new Error(); } else { var prefix = 'Hello '; alert(prefix + name); } }function sayHello(name) { var prefix; if (!name) { throw new Error(); } else { prefix = 'Hello '; alert(prefix + name); } }
  • 35. 定义绑定初始化 (Declaration Binding Instantiation)遍历FormalParameterList(参数列表),对每一项(参数),如果VariableEnvironment中不存在,则添加并赋值。 依次遍历源码中每个FunctionDeclaration(函数声明),对每一项(函数),如果VariableEnvironment中不存在,则添加并赋值。 如果VariableEnvironment中不存在arguments,则添加并赋值。 依次遍历源码中每个VariableDeclaration(变量声明),对每一项(变量),如果VariableEnvironment中不存在,则添加并赋值为undefined。
  • 36. 定义绑定初始化 (Declaration Binding Instantiation)function format(template, data) { var regex = /\{(\w+)\:(\w+)\}/g; function replacer(match, name, type) { var value = data[name]; switch (type) { case 'boolean': value = !!value; break; case 'html': value = encodeHTML(value); break; } return value; } var html = template.replace(regex, replacer); return html; }Variable Environmentarguments
  • 37. 消化一下还有完没完了!NO
  • 38. 变量查找GetIdentifierReference(lex, name) 从给定的LexicalEnvironment中查找是否存在该变量 如果不存在,则从LexicalEnvironment的Outer Environment中查找 依次进行,直到Outer Environment为null,则返回undefined 返回一个Reference对象,通过GetValue进一步获取变量的值
  • 39. 变量查找function GetIdentifierReference(lex, name) { if (lex == null) { return new Reference(name, undefined); } var envRec = lex.environmentRecords; if (envRec.hasBinding(name)) { return new Reference(name /* name */, envRec /* base */); } return GetIdentifierReference(lex.outerEnvironment, name); } function GetValue(reference) { var envRec = reference.base; return envRec.getValue(reference.name); }
  • 40. 大串烧进入全局环境 创建全局执行环境并入栈 创建全局环境对象 全局的词法环境对象指向该对象 全局的变量环境对象指向该对象 在变量环境中添加outer绑定并赋值 在变量环境中添加prefix绑定 在变量环境中添加inner绑定// script… var prefix = ‘Hello ‘; function outer() { var name = ‘GrayZhang’; function say() { var message = prefix + name; alert(message); } return say; } var inner = outer(); // inner.[[Scope]] inner();
  • 41. 大串烧创建outer函数 创建一个对象 设置[[Call]]、[[Construct]]、[[HasInstance]]等 设置[[Scope]]为当前词法环境 – 全局环境 设置[[Code]]、[[FormalParameterList]]等 设置length、prototype等// script… var prefix = ‘Hello ‘; function outer() { var name = ‘GrayZhang’; function say() { var message = prefix + name; alert(message); } return say; } var inner = outer(); // inner.[[Scope]] inner();
  • 42. 大串烧Global Environmentouter: { [[Scope]] } inner: undefined prefix: undefinedGlobal Execution ContextVariableEnvironmentLexicalEnvironment
  • 43. 大串烧为prefix变量赋值 在全局环境中寻找name绑定 – 找到 得到上一步返回的Reference的base – 即全局环境的环境数据对象 调用其setBinding(‘prefix’, ‘Hello ’)// script… var prefix = ‘Hello ‘; function outer() { var name = ‘GrayZhang’; function say() { var message = prefix + name; alert(message); } return say; } var inner = outer(); // inner.[[Scope]] inner();
  • 44. 大串烧Global Environmentouter: { [[Scope]] } inner: undefined prefix: ‘Hello ‘Global Execution ContextVariableEnvironmentLexicalEnvironment
  • 45. 大串烧执行outer函数 创建执行环境并入栈 创建一个词法环境 – DeclarativeEnvironment outer的词法环境对象指向该对象 outer的变量环境对象指向该对象 在变量环境中添加say绑定并赋值 在变量环境中添加name绑定 // script… var prefix = ‘Hello ‘; function outer() { var name = ‘GrayZhang’; function say() { var message = prefix + name; alert(message); } return say; } var inner = outer(); // inner.[[Scope]] inner();
  • 46. 大串烧创建say函数 创建一个对象 设置[[Call]]、[[Construct]]、[[HasInstance]]等 设置[[Scope]]为当前词法环境 – outer的词法环境 设置[[Code]]、[[FormalParameterList]]等 设置length、prototype等 // script… var prefix = ‘Hello ‘; function outer() { var name = ‘GrayZhang’; function say() { var message = prefix + name; alert(message); } return say; } var inner = outer(); // inner.[[Scope]] inner();
  • 47. 大串烧Global Execution ContextGlobal Environmentouter: { [[Scope]] } inner: undefined prefix: ‘Hello ‘VariableEnvironmentLexicalEnvironmentOuter Environmentsay: { [[Scope]] } name: undefinedOuter Execution ContextVariableEnvironmentLexicalEnvironment
  • 48. 大串烧为name变量赋值 在outer的词法环境中寻找name绑定 – 找到 得到上一步返回的Reference的base – 即outer的词法环境的环境数据对象 调用其setBinding(‘name’, ‘GrayZhang’)// script… var prefix = ‘Hello ‘; function outer() { var name = ‘GrayZhang’; function say() { var message = prefix + name; alert(message); } return say; } var inner = outer(); // inner.[[Scope]] inner();
  • 49. 大串烧Global Environmentouter: { [[Scope]] } inner: { [[Scope]] } prefix: ‘Hello ‘Outer Environmentsay: { [[Scope]] } name: ‘Gray Zhang’Global Execution ContextVariableEnvironmentLexicalEnvironmentOuter Execution ContextVariableEnvironmentLexicalEnvironment
  • 50. 大串烧返回并赋值给inner变量 将outer的ExecutionContext出栈 在全局环境下寻找inner绑定 – 找到 得到上一步返回的Reference的base – 即全局环境的环境数据对象 调用其setBinding(‘inner’, &say);// script… var prefix = ‘Hello ‘; function outer() { var name = ‘GrayZhang’; function say() { var message = prefix + name; alert(message); } return say; } var inner = outer(); // inner.[[Scope]] inner();
  • 51. 大串烧Global Environmentouter: { [[Scope]] } inner: { [[Scope]] } prefix: ‘Hello ‘Outer Environmentsay: { [[Scope]] } name: ‘GrayZhang’Global Execution ContextVariableEnvironmentLexicalEnvironment
  • 52. 大串烧执行inner函数 创建执行环境并入栈 创建一个词法环境 – DeclarativeEnvironment inner的词法环境对象指向该对象 inner的变量环境对象指向该对象 在变量环境中添加message绑定 // script… var prefix = ‘Hello ‘; function outer() { var name = ‘GrayZhang’; function say() { var message = prefix + name; alert(message); } return say; } var inner = outer(); // inner.[[Scope]] inner();
  • 53. 大串烧Global Environmentouter: { [[Scope]] } inner: { [[Scope]] } prefix: ‘Hello ‘Outer Environmentsay: { [[Scope]] } name: ‘GrayZhang’Inner Environmentmessage: undefinedGlobal Execution ContextVariableEnvironmentLexicalEnvironmentInner Execution ContextVariableEnvironmentLexicalEnvironment
  • 54. 大串烧为message变量赋值 查找prefix变量的值 在inner的词法环境中寻找prefix绑定 – 没有 在outer的词法环境中寻找prefix绑定 – 没有 在全局环境中寻找prefix绑定 – 找到 取得prefix的值 查找name变量的值 … 在inner的词法环境中寻找message 给message绑定赋值// script… var prefix = ‘Hello ‘; function outer() { var name = ‘GrayZhang’; function say() { var message = prefix + name; alert(message); } return say; } var inner = outer(); // inner.[[Scope]] inner();
  • 55. 大串烧Global Environmentouter: { [[Scope]] } inner: { [[Scope]] } prefix: ‘Hello ‘Outer Environmentsay: { [[Scope]] } name: ‘GrayZhang’Inner Environmentmessage: ‘Hello GrayZhang’Global Execution ContextVariableEnvironmentLexicalEnvironmentInner Execution ContextVariableEnvironmentLexicalEnvironment
  • 56. 大串烧获取inner的值 在inner的词法环境中寻找message绑定 – 找到 得到上一步返回的Reference的base – 即inner的词法环境的环境数据对象 调用该对象的getValue(‘message’) 获取alert的值 … 将inner作为参数,调用alert函数// script… var prefix = ‘Hello ‘; function outer() { var name = ‘GrayZhang’; function say() { var message = prefix + name; alert(message); } return say; } var inner = outer(); // inner.[[Scope]] inner();alert从何而来?
  • 57. 大串烧function born() { var name = 'unknown'; var age = 1; return { setName: function(value) { name = value; }, grow: function() { age++; }, print: function() { var parts = [name, age]; var joint = ' is now '; alert(parts.join(joint)); } }; } var god = born(); god.setName(‘leeight’); god.grow(); god.grow(); god.print();
  • 58. 总结相关概念 可执行代码 – Executable Code 执行环境 – Execution Context 词法环境 – LexicalEnvironment 变量环境 – VariableEnvironment 环境数据 – Environment Records
  • 59. 总结过程 创建函数 – [[Scope]] [[Scope]]在创建时决定且不会变化 进入函数 – 执行环境 + 词法环境 + 变量环境 执行时在最内层增加词法环境 定义绑定初始化 – 参数 + 函数声明 + 变量声明 变量环境和词法环境是同一个对象 变量查找 – GetIdentifierReference 延词法环境自内向外查找
  • 60. 继续消化我以为我懂了,直到…… How with works How catch works How let works When code meets eval When code meets new Function When there is strict mode
  • 61. 从代码说起function outer() { var o = LargetObject.fromSize('400MB'); return function() { console.log('inner'); }; } var inner = outer(); // 对象图此时对象之间的引用关系?GlobalFunctionLexical EnvironmentEnvironment RecordsBinding ObjectLarge Objectinner[[Scope]]environmentRecordsbindingObjectoGlobal和o有间接引用,无法回收o
  • 62. 但是事实上……function outer() { var i = 3; return function() { debugger; }; } var inner = outer(); inner();javascript引擎有能力回收i
  • 63. 如果你是计算机……function outer() { var i = 3; var j = 4; var k = 5; function prepare() { i = i + k; } function help() { i = i + j; } prepare(); return function() { help(); console.log(i); }; } var inner = outer(); inner();i: 不可回收 j: 不可回收 k: 可回收 prepare: 可回收 help: 不可回收人类的智商计算机的智商
  • 64. 压力好大 ~
  • 65. 测试方法用断点! Chrome / Firefox 看内存! IE / Opera
  • 66. 一些基本结果IE6 – 8没有回收闭包内变量的机制 Opera没有回收闭包内变量的机制 Chrome回收闭包内变量后,再次访问该变量将抛出ReferenceError Firefox回收闭包内变量后,再次访问该变量会得到undefined Chrome、Firefox和IE9回收闭包内变量的策略基本相同
  • 67. 试问!有哪些因素可能导致变量无法回收? 变量被返回的函数直接引用。 变量被返回的函数间接引用(通过嵌套函数)。 返回的函数中有eval。 返回的函数在with表达式建立的作用域中。 返回的函数在catch表达式中。 只谈结果,不谈过程!
  • 68. 直接引用EngineCollectableChrome – V8NOFirefox – SpiderMonkeyNOIE9 - ChakraNOfunction outer() { var i = 3; return function() { i; }; } var inner = outer();
  • 69. 间接引用EngineCollectableChrome – V8NOFirefox – SpiderMonkeyNOIE9 - ChakraNOfunction outer() { var i = 3; function help() { i; } return function() { help(); }; } var inner = outer();
  • 70. 嵌套函数的平衡function outer() { var i = 0; function help() { i++; } help(); return function() { console.log('nothing'); } } var inner = outer();function outer() { var i = 0; function help() { i++; return inner(); } function inner() { return i > 3 ? i : help(); } return inner(); } var inner = outer();需要图的遍历需要处理环引用高成本 + 低效
  • 71. 嵌套函数的平衡EngineCollectableChrome – V8NOFirefox – SpiderMonkeyNOIE9 - ChakraNOfunction outer() { var i = 3; function help() { i; } return function() { }; } var inner = outer();
  • 72. 大恶魔evalfunction outer() { var i = 3; return function() { return eval(‘i’); } } var inner = outer(); var result = inner(); console.log(result); // 3?由字符串从词法环境中获取对象的唯一途径可变性特殊性
  • 73. 大恶魔evalvar reference = eval(‘someObject’); 字符串分析var reference = eval(‘some’ + ‘Object’); 常量预计算var s = ‘some’; var reference = eval(s + ‘Object’); 变量->常量替换var array = [‘some’, ‘ject’]; var reference = eval(array.join(‘Ob’));
  • 74. 大恶魔evalfunction outer() { var i = 3; return function(variableName) { return eval(variableName); } } var inner = outer(); var input = document.getElementById(‘variable_name’); var name = input.value.trim(); var result = inner(name); console.log(result); // 3
  • 75. 囧
  • 76. Too Simple, Sometimes Native.
  • 77. 大恶魔evalEngineCollectableChrome – V8NOFirefox – SpiderMonkeyNOIE9 - ChakraNOfunction outer() { var i = 3; return function() { eval(‘’); // 无论eval的内容是什么 }; } var inner = outer();
  • 78. 间接eval和new Function间接eval window.eval(coe) | (1, eval)(code) | (true && eval)(code) In Edition 5, indirect calls to the eval function use the global environment as both the variable environment and lexical environment for the eval code. new Function Return a new Function object created as specified in 13.2 passing P as the FormalParameterList and body as the FunctionBody. Pass in the Global Environment as the Scope parameter and strict as the Strict flag.
  • 79. 间接eval和new Functionvar i = 3; function outer() { var i = 4; return function() { return window.eval('i'); /* * var fn = new Function('return i;'); * return fn(); */ } } var inner = outer(); var result = inner(); console.log(result); // 3X
  • 80. 间接eval和new FunctionEngineCollectableChrome – V8YESFirefox – SpiderMonkeyYESIE9 - ChakraYESfunction outer() { var i = 3; return function() { window.eval(‘i’); }; } var inner = outer();
  • 81. 关于with的分歧function outer() { var scope = { i: 3, j: 4 }; var m = 4; var n = 5; with (scope) { return function() { i++; m++; }; }; } var inner = outer(); inner();??
  • 82. 关于with的分歧EngineCollectableChrome – V8NOFirefox – SpiderMonkey回收k, scope,不回收i, jIE9 - Chakra回收k,不回收i, j,scope未知function outer() { var scope = { i: 3, j: 4 }; var k = 4; with (scope) { return function() { i; }; } } var inner = outer();
  • 83. 不被重视的catchEngineCollectableChrome – V8回收i,不回收exFirefox – SpiderMonkey回收i,不回收exIE9 - Chakra回收i和exfunction outer() { var i = 3; try { throw { j: 4 }; } catch (ex) { return function() {}; } } var inner = outer();
  • 84. 你能骗过引擎吗?function outer() { var i = 3; var j = 4; return function(i) { var j = 5; console.log(i + j); }; } var inner = outer(); inner(6);?EngineCollectableChrome – V8YESFirefox – SpiderMonkeyYESIE9 - ChakraYES
  • 85. 总结outer声明的 – c1 = (i, j, k, m) inner声明的 – c2 = (i, j) inner用到的 – c3 = (i, j, k) help声明的 – c4 = () help用到的 – c5 = (j, k) 可回收的 = c1- (c3 – c2) – (c5 – c4) 遇上eval则不回收任何变量 注意with和catch的影响function outer() { var i = 3; var j = 4; var k = 5; var m = 6; function help() { console.log(j + k); } return function(i) { var j = 5; console.log(i + j + k); }; } var inner = outer(); inner(6);
  • 86. 谢谢
  • 87. 知识要点变量声明在变量环境中,从词法环境中获取,通常2者是同一个对象。 作用域在函数创建时生成,是函数对象的不变的属性 – 静。 执行函数时,在作用域上增加一个词法环境对象 – 动。 动静结合即闭包的本质。 闭包对垃圾回收会有一定的影响。
  • 88. 参考资料Annotated ES5 http://es5.github.com/ ECMA-262-5 in detail http://dmitrysoshnikov.com/tag/es-5/ 关于闭包及变量回收问题 http://www.otakustay.com/about-closure-and-gc/ Discussion on reference & scope - digipedia? http://www.digipedia.pl/usenet/thread/14438/704/ Discussion on V8 variable allocation - twitter http://twitter.com/#!/erikcorry/status/53901976865476608