Successfully reported this slideshow.
Upcoming SlideShare
×

# 所谓闭包

1,380 views

Published on

• Full Name
Comment goes here.

Are you sure you want to Yes No
• Be the first to comment

• Be the first to like this

### 所谓闭包

1. 1. function infiniteSequence() { var i = 0; return function() { return i++; }}var increment = infiniteSequence();console.log(increment());console.log(increment()); 所谓闭包console.log(increment()); 张立理// … otaksutay@gmail.com
2. 2. WARNING!! 2
3. 3. WARNING� 非常非常的学术性� 大量术语词汇出没� 也许永远都用不上� 内容并非标准，有所删减� 逻辑不是那么清晰� 只谈函数，不提eval，不提new Function
4. 4. Summary� 引言� 什么是变量� 闭包之表象� 闭包之内在� 关于垃圾回收
5. 5. 作用域的历史 Scope Chain ECMAScript Variable Object v3 Identifier Resolution Lexical Environment ECMAScript v5 Variable Environment GetIdentifierReference
6. 6. 什么是变量A symbolic name associated with a value and whoseassociated value may be changed. -- Wikipedia� 变量是一种关联关系（association）� 关联的目标： � 符号名（symbolic name) � 值（value）� 关联是单向的 – 永远不可能根据值找到变量 Identifier Value name ‘GrayZhang’
7. 7. 什么是变量� Variable Statement � var Identifier = AssignmentExpression� FunctionDeclaration � function Identifier(FormalParameterList) { FunctionBody } � FormalParameterList � Identifier, Identifier[, …]
8. 8. 什么是变量 Value (String Literal) var name = ‘GrayZhang’; IdentifierKeyword function add(x, y) { return x + y; }
9. 9. 11闭包之表象� 内层函数可以使用外层函数作用域内的变量function outer() { 外层 var name = ‘GrayZhang’; function inner() { console.log(‘Hello ‘ + name); 内层 } inner();}
10. 10. 闭包之内在� Q：为什么javascript 会有闭包？� A：因为ECMAScript 中变量解析是一个查找过程，而非绑定 过程。� Q：变量存放在哪里？� A：Execution Context Execution Context中的VariableEnvironment VariableEnvironment VariableEnvironment。� Q：从哪里查找变量？� A：Execution Context Execution Context中的LexicalEnvironment LexicalEnvironment LexicalEnvironment。� Q：如何查找变量？� A：自内向外。
11. 11.
12. 12. 可执行代码（Executable Code） Global Code Function Code<script> function sayHello(name) {var name = ‘GrayZhang’; var prefix = ‘Hello’;var prefix = ‘Hello‘; var phrases = [prefix, name];var phrases = [prefix, name]; console.log(phrases.join(‘ ‘));console.log(phrases.join(‘ ‘)); }</script> function getName() { var input = \$(‘#name’); Eval Code return input.val(); var source = } ‘var x = 3;’ + ‘console.log(x);’ getName(); eval(source);
13. 13. 15执行环境（Execution Context）� 当进入（开始执行）一段可执行代码时，生成 一个执行环境对象。� 执行环境对象通过栈（Stack）维护。� 新建的执行环境对象称为“当前运行的执行环 境对象”。function enterCode(code) { var ec = new ExecutionContext(); control.ecStack.push(ec); control.runningEC = ec; control.execute(code);}
14. 14. 执行环境（Execution Context）� 一个执行环境对象包括： � 词法环境 – LexicalEnvironment � 变量环境 – VariableEnvironment � This绑定 - ThisBindingExecutionContext: { LexicalEnvironment, VariableEnvironment, ThisBinding}
15. 15. 词法环境（LexicalEnvironment）� 既是一个属性，又是一个类型。� 每个执行环境对象都有且仅有一个关联的词法 环境对象。� 在代码执行过程中，需要解析变量时，通过词 法环境对象进行解析，从其环境数据中得到值。� 一个词法环境对象包括： � 环境数据 – environement records � 外层环境 – outer environment
16. 16. 词法环境（LexicalEnvironment）� 存在2种词法环境的实现类型 � DeclarativeEnvironment � ObjectEnvironment� 区别是ObjectEnvironment可接受指定的对 象作为环境数据属性的值 ？什么情况会出现 ObjectEnvironment
17. 17. 变量环境（VariableEnvironment）� 每个执行环境对象都有且仅有一个关联的变量 环境对象。� 变量环境仅仅是一个名字，变量环境对象的类 型是词法环境（LexicalEnvironment）。� 在进入（开始执行）代码时，所有的变量标识 符（Identifier）会存放在当前的变量环境对 象中。� 变量环境中有环境数据属性，但不使用外层环 境属性。
18. 18. 环境数据（environment records）� 存在于词法环境或变量环境中。� 包含一个binding object，简单地认为binding object 是一个Map对象，保存变量标签符（Identifier）和变 量值（Value）的关系。� 常用方法： � hadBinding(name) – 查看是否有变量绑定 � createBinding(name) – 创建一个变量绑定 � setBinding(name, value) – 修改变量绑定的值 � getValue(name) – 获取变量绑定的值 � deleteBinding(name) – 删除一个变量绑定
19. 19. 环境数据（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; }, // …}
20. 20. 环境数据（environment records）� 存在2种环境数据的实现类型 � DeclarativeEnvironmentRecords � ObjectEnvironmentRecordsLexicalEnvironment EnvironmentRecords DeclaractiveEnvironmen DeclaractiveEnvironmentRecord t s ObjectEnvironment ObjectEnvironmentRecords
21. 21. 创建词法环境� NewDeclarativeEnvironment(e) � 用于创建一个DeclarativeEnvironment � 进入函数时 � 执行catch表达式时� NewObjectEnvironment(o, e) � 用于创建一个ObjectEnvironment � 执行with表达式时
22. 22. 创建词法环境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;}
23. 23. 消化一下• 名词解释完了吗？ NO
24. 24. 总结ExecutionContext: { LexicalEnvironment, VariableEnvironment, EnvironmentRecords: { ThisBinding hasBinding(name),} createBinding(name), setBinding(name, value), getValue(name),LexicalEnvironment: { deleteBinding(name) environmentRecords, } outerEnvironment}
25. 25. 函数（Function）� 是一个对象（Object）。� 包含几个特殊的属性 � [[Construct]] – new SomeFunction() � [[Call]] – someFunction() � [[HasInstance]] – o instanceof SomeFunction � [[Scope]] – 闭包 � [[FormalParameters]] – 参数列表 � [[Code]] – 可执行代码� 包含可执行代码（Executable Code）和执行状态 （State）。
26. 26. 创建函数（Create Function Object）� 新建一个普通对象（new Object()）� 将[[Class]]设为”function”� 将[[Prototype]]指向Function.prototype� 根据默认的规则，设置[[Call]]、[[Contruct]] 及[[HasInstance]]属性� 将[[Scope]]设置为当前的 LexicalEnvironment对象� 设置[[Code]]、[[FormalParameterList]] 及name、length、prototype属性
27. 27. 创建函数（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 };
28. 28. 创建函数（Create Function Object）� 作用域（[[Scope]]）是函数对象的一个属性function hello() { function outer() { var o = {}; var name = ‘GrayZhang’; o.name = ‘GrayZhang’; function say() { return o; alert(name);} }var person = hello(); return say;console.log(person.name); } var inner = outer(); // inner.[[Scope]] inner();
29. 29. 进入函数（Entering Function Code）� 新建一个执行环境。� 根据规则设置this绑定。 � 如果thisArg是null或undefined，设置为global。 � 如果thisArg不是Object，设置为ToObject(thisArg)。� 以函数的[[Scope]]属性为参数， NewDeclarativeEnvironment创建一个 LexicalEnvironment对象。� 将当前LexicalEnvironment设置为该值。 同一对象� 将当前VariableEnvironment设置为该值。� 开始初始化参数及函数内声明的变量。
30. 30. 进入函数（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]]);
31. 31. 进入函数（Entering Function Code）� 执行函数时，在作用域（[[Scope]]）的基础 上添加词法环境（LexicalEnvironment）// Global Environmentfunction outer() { Global Environment // Outer Environment function inner() { Outer Environment // Current Environment } Current Environment}var inner = outer();// [[Scope]] === Outer Environmentinner();
32. 32. 定义绑定初始化 34 （Declaration Binding Instantiation）� 从Hosting Behavior说起……function sayHello(name) { function sayHello(name) { if (!name) { var prefix; throw new Error(); if (!name) { } throw new Error(); else { } var prefix = Hello ; else { alert(prefix + name); prefix = Hello ; } alert(prefix + name);} } }
33. 33. 定义绑定初始化 （Declaration Binding Instantiation ）� 遍历FormalParameterList（参数列表），对每一项 （参数），如果VariableEnvironment中不存在，则 添加并赋值。� 依次遍历源码中每个FunctionDeclaration（函数声 明），对每一项（函数），如果 VariableEnvironment中不存在，则添加并赋值。� 如果VariableEnvironment中不存在arguments arguments arguments，则 添加并赋值。� 依次遍历源码中每个VariableDeclaration（变量声 明），对每一项（变量），如果 VariableEnvironment中不存在，则添加并赋值为 undefined undefined。
34. 34. 定义绑定初始化 （Declaration Binding Instantiation）function format(template, data) { arguments var regex = /{(w+):(w+)}/g; function replacer(match, name, type) { var value = data[name]; switch (type) { case boolean: Variable Environment value = !!value; break; case html: value = encodeHTML(value); break; } return value; } var html = template.replace(regex, replacer); return html;}
35. 35. 消化一下• 还有完没完了！ NO
36. 36. 变量查找� GetIdentifierReference(lex, name)� 从给定的LexicalEnvironment中查找是否存 在该变量� 如果不存在，则从LexicalEnvironment的 Outer Environment中查找� 依次进行，直到Outer Environment为 null，则返回undefined� 返回一个Reference对象，通过GetValue进一 步获取变量的值
37. 37. 变量查找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);}
38. 38. 大串烧� 进入全局环境 � 创建全局执行环境并入栈 � 创建全局环境对象 � 全局的词法环境对象指向该对象 � 全局的变量环境对象指向该对象 � 在变量环境中添加outer绑定并赋值 // script… � 在变量环境中添加prefix绑定 var prefix = ‘Hello ‘; function outer() { � 在变量环境中添加inner绑定 var name = ‘GrayZhang’; function say() { var message = prefix + name; alert(message); } return say; } var inner = outer(); // inner.[[Scope]] inner();
39. 39. 大串烧� 创建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();
40. 40. 大串烧 Global Environment outer: { [[Scope]] } inner: undefined prefix: undefinedGlobal Execution Context VariableEnvironmen t LexicalEnvironment
41. 41. 大串烧� 为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();
42. 42. 大串烧 Global Environment outer: { [[Scope]] } inner: undefined prefix: ‘Hello ‘Global Execution Context VariableEnvironmen t LexicalEnvironment
43. 43. 大串烧� 执行outer函数 � 创建执行环境并入栈 � 创建一个词法环境 – DeclarativeEnvironment � outer的词法环境对象指向该对象 � outer的变量环境对象指向该对象 � 在变量环境中添加say绑定并赋值 // script… � 在变量环境中添加name绑定 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. 44. 大串烧� 创建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();
45. 45. 大串烧 Global Environment outer: { [[Scope]] } inner: undefined prefix: ‘Hello ‘ Outer EnvironmentOuter Execution Context say: { [[Scope]] } name: undefined VariableEnvironment LexicalEnvironmentGlobal Execution Context VariableEnvironmen t LexicalEnvironment
46. 46. 大串烧� 为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();
47. 47. 大串烧 Global Environment outer: { [[Scope]] } inner: { [[Scope]] } prefix: ‘Hello ‘ Outer EnvironmentOuter Execution Context say: { [[Scope]] } name: ‘Gray Zhang’ VariableEnvironment LexicalEnvironmentGlobal Execution Context VariableEnvironmen t LexicalEnvironment
48. 48. 大串烧� 返回并赋值给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();
49. 49. 大串烧 Global Environment outer: { [[Scope]] } inner: { [[Scope]] } prefix: ‘Hello ‘ Outer Environment say: { [[Scope]] } name: ‘GrayZhang’Global Execution Context VariableEnvironmen t LexicalEnvironment
50. 50. 大串烧� 执行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();
51. 51. 大串烧 Global Environment outer: { [[Scope]] } inner: { [[Scope]] } prefix: ‘Hello ‘ Outer EnvironmentInner Execution Context say: { [[Scope]] } name: ‘GrayZhang’ VariableEnvironment LexicalEnvironment Inner Environment message: undefinedGlobal Execution Context VariableEnvironmen t LexicalEnvironment
52. 52. 大串烧� 为message变量赋值 � 查找prefix变量的值 � 在inner的词法环境中寻找prefix绑定 – 没有 � 在outer的词法环境中寻找prefix绑定 – 没有 � 在全局环境中寻找prefix绑定 – 找到 � 取得prefix的值 � 查找name变量的值 // script… �… var prefix = ‘Hello ‘; function outer() { � 在inner的词法环境中寻找message var name = ‘GrayZhang’; function say() { var message = prefix + name; � 给message绑定赋值 alert(message); } return say; } var inner = outer(); // inner.[[Scope]] inner();
53. 53. 大串烧 Global Environment outer: { [[Scope]] } inner: { [[Scope]] } prefix: ‘Hello ‘ Outer EnvironmentInner Execution Context say: { [[Scope]] } name: ‘GrayZhang’ VariableEnvironment LexicalEnvironment Inner Environment message: ‘Hello GrayZhang’Global Execution Context VariableEnvironmen t LexicalEnvironment
54. 54. 大串烧� 获取inner的值 � 在inner的词法环境中寻找message绑定 – 找到 � 得到上一步返回的Reference的base – 即inner的词法环 境的环境数据对象 � 调用该对象的getValue(‘message’)� 获取alert的值 � … // script… var prefix = ‘Hello ‘;� 将inner作为参数，调用alert函数 function outer() { var name = ‘GrayZhang’; function say() { var message = prefix + name; alert alert从何而来？ } alert(message); return say; } var inner = outer(); // inner.[[Scope]] inner();
55. 55. 大串烧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();
56. 56. 总结� 相关概念 � 可执行代码 – Executable Code � 执行环境 – Execution Context � 词法环境 – LexicalEnvironment � 变量环境 – VariableEnvironment � 环境数据 – Environment Records
57. 57. 总结� 过程 � 创建函数 – [[Scope]] � [[Scope]]在创建时决定且不会变化 � 进入函数 – 执行环境 + 词法环境 + 变量环境 � 执行时在最内层增加词法环境 � 定义绑定初始化 – 参数 + 函数声明 + 变量声 明 � 变量环境和词法环境是同一个对象 � 变量查找 – GetIdentifierReference � 延词法环境自内向外查找
58. 58. 继续消化• 我以为我懂了，直到…… – How with works – How catch works – How let works – When code meets eval – When code meets new Function – When there is strict mode
59. 59. 从代码说起function outer() { var o = LargetObject.fromSize(400MB); return function() { console.log(inner); };}var inner = outer();// 对象图 此时对象之间的引用关 系？ Lexical Global Function inner [[Scope]] Environment Global o有间接引用，无法回收o Global和o o environmentRecords Large o Binding bindingObject Environment Object Object Records
60. 60. 但是事实上……function outer() { var i = 3; return function() { debugger; };}var inner = outer();inner();javascript引擎有能力回收i
61. 61. 如果你是计算机……function outer() { var i = 3; i: 不可回收 var j = 4; var k = 5; j: 不可回收 function prepare() { k: 可回收 } i = i + k; prepare: 可回收 function help() { help: 不可回收 i = i + j; } prepare(); 人类的智商 return function() { help(); console.log(i); }; 计算机的智商}var inner = outer();inner();
62. 62. ~ ~ ~ ~ 大大 大 大 好好 好 好 力力 力 力压压压压
63. 63. 测试方法� 用断点！ � Chrome / Firefox� 看内存！ � IE / Opera
64. 64. 一些基本结果� IE6 – 8没有回收闭包内变量的机制� Opera没有回收闭包内变量的机制� Chrome回收闭包内变量后，再次访问该变量 将抛出ReferenceError ReferenceError� Firefox回收闭包内变量后，再次访问该变量会 得到undefined undefined� Chrome、Firefox和IE9回收闭包内变量的策 略基本相同
65. 65. 试问！� 有哪些因素可能导致变量无法回收？ � 变量被返回的函数直接引用。 � 变量被返回的函数间接引用（通过嵌套函数）。 � 返回的函数中有eval eval eval。 � 返回的函数在with with with表达式建立的作用域中。 � 返回的函数在catch catch catch表达式中。� 只谈结果，不谈过程！
66. 66. 直接引用Engine CollectableChrome – V8 NOFirefox – SpiderMonkey NOIE9 - Chakra NOfunction outer() { var i = 3; return function() { i; };}var inner = outer();
67. 67. 间接引用Engine CollectableChrome – V8 NOFirefox – SpiderMonkey NOIE9 - Chakra NOfunction outer() { var i = 3; function help() { i; } return function() { help(); };}var inner = outer();
68. 68. 嵌套函数的平衡function outer() { function outer() { var i = 0; var i = 0; function help() { function help() { i++; i++; } return inner(); } help(); function inner() { return function() { return i > 3 ? i : help(); console.log(nothing); } }} return inner(); }var inner = outer(); var inner = outer();需要图的遍历 需要处理环引用 高成本 + 低效
69. 69. 71嵌套函数的平衡Engine CollectableChrome – V8 NOFirefox – SpiderMonkey NOIE9 - Chakra NOfunction outer() { var i = 3; function help() { i; } return function() { };}var inner = outer();
70. 70. 大恶魔evalfunction outer() { var i = 3; ? return function() { return eval(‘i’); }}var inner = outer();var result = inner();console.log(result); // 3由字符串从词法环境中获取对象的唯一途径 可变性 特殊性
71. 71. 大恶魔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’));
72. 72. 大恶魔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
73. 73.
74. 74. .. . . ve ve ve ve ti ti ti ti , s Na , s Na , , Na Na le le le le me s s mp time mp time mp mp ti ti me Si Si Si Si me me me me oo o o So So So SoToToToTo
75. 75. 大恶魔evalEngine CollectableChrome – V8 NOFirefox – SpiderMonkey NOIE9 - Chakra NOfunction outer() { var i = 3; return function() { eval(‘’); // 无论eval的内容是什么 };}var inner = outer();
76. 76. 间接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.
77. 77. 间接eval和new Functionvar i = 3;function outer() { var i = 4; return function() { X return window.eval(i); /* * var fn = new Function(return i;); * return fn(); */ }}var inner = outer();var result = inner();console.log(result); // 3
78. 78. 间接eval和new FunctionEngine CollectableChrome – V8 YESFirefox – SpiderMonkey YESIE9 - Chakra YESfunction outer() { var i = 3; return function() { window.eval(‘i’); };}var inner = outer();
79. 79. 关于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();
80. 80. 关于with的分歧Engine CollectableChrome – V8 NOFirefox – SpiderMonkey 回收k, scope k, scope，不回收i, j iIE9 - Chakra 回收k，不回收i, j scope k i j，scope scope未知function outer() { var scope = { i: 3, j: 4 }; var k = 4; with (scope) { return function() { i; }; }}var inner = outer();
81. 81. 不被重视的catchEngine CollectableChrome – V8 回收i，不回收ex i exFirefox – SpiderMonkey 回收i，不回收ex i exIE9 - Chakra 回收i和ex i exfunction outer() { var i = 3; try { throw { j: 4 }; } catch (ex) { return function() {}; }}var inner = outer();
82. 82. 你能骗过引擎吗？function outer() { var i = 3; var j = 4; ? return function( i) { var j = 5; console.log( i + j); };}var inner = outer();inner(6);Engine CollectableChrome – V8 YESFirefox – SpiderMonkey YESIE9 - Chakra YES
83. 83. 总结� outer声明的 – c1 = (i, j, k, m)� inner声明的 – c2 = (i, j)� inner用到的 – c3 = (i, j, k) function outer() {� help声明的 – c4 = () var i = 3; var j = 4;� help用到的 – c5 = (j, k) var k = 5; var m = 6;� 可回收的 = c1- (c3 – c2) – (c5 – c4) function help() { console.log(j + k);� 遇上eval eval eval则不回收任何变量 }� 注意with catch with catch的影响 with和catch return function( i) { var j = 5; console.log( i + j + k); }; } var inner = outer(); inner(6);
84. 84. 谢 谢
85. 85. 知识要点• 变量声明在变量环境中，从词法环境中获 取，通常2者是同一个对象。• 作用域在函数创建时生成，是函数对象的 不变的属性 – 静。• 执行函数时，在作用域上增加一个词法环 境对象 – 动。• 动静结合即闭包的本质。• 闭包对垃圾回收会有一定的影响。
86. 86. 参考资料• Annotated ES5 – http://es5.github.com/ 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