所谓闭包

656 views
618 views

Published on

Published in: Technology, Business
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
656
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
6
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

所谓闭包

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

×