About closure

Preview:

DESCRIPTION

Talk about javascript closure in detail.

Citation preview

所谓闭包张立理otaksutay@gmail.com

function infiniteSequence() { var i = 0; return function() { return i++; }}

var increment = infiniteSequence();console.log(increment()); console.log(increment());console.log(increment());// …

WARNING!!

WARNING 非常非常的学术性 大量术语词汇出没 也许永远都用不上 内容并非标准,有所删减 逻辑不是那么清晰

只谈函数,不提 eval ,不提 new Function

Summary 引言 什么是变量 闭包之表象 闭包之内在 关于垃圾回收

作用域的历史

ECMAScript v3

Scope Chain

Variable Object

Identifier Resolution

ECMAScript v5

Lexical Environment

Variable Environment

GetIdentifierReference

什么是变量

变量是一种关联关系( association ) 关联的目标:

符号名( symbolic name) 值( value )

关联是单向的 – 永远不可能根据值找到变量

A symbolic name associated with a value and whose associated value may be changed.

-- Wikipedia

Identifiername

Value‘GrayZhang’

什么是变量 Variable Statement

var Identifier = AssignmentExpression FunctionDeclaration

function Identifier(FormalParameterList) { FunctionBody}

FormalParameterList Identifier, Identifier[, …]

什么是变量

var name = ‘GrayZhang’;

function add(x, y) {

return x + y;

}

Keyword

Identifier

Value (String Literal)

闭包之表象 内层函数可以使用外层函数作用域内的变量

function outer() { var name = ‘GrayZhang’; function inner() { console.log(‘Hello ‘ + name); } inner();}

外层

内层

闭包之内在 Q:为什么javascript会有闭包? A:因为ECMAScript中变量解析是一个查找过程,而非绑定过程。

Q:变量存放在哪里? A:Execution Context中的VariableEnvironment。

Q:从哪里查找变量? A:Execution Context中的LexicalEnvironment。

Q:如何查找变量? A:自内向外。

可执行代码( Executable Code )

Global Code Function Code

Eval Code

<script>var name = ‘GrayZhang’;var prefix = ‘Hello‘;var phrases = [prefix, name];console.log(phrases.join(‘ ‘));</script>

var 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();

执行环境( Execution Context ) 当进入(开始执行)一段可执行代码时,生成

一个执行环境对象。 执行环境对象通过栈( Stack )维护。 新建的执行环境对象称为“当前运行的执行环

境对象”。function enterCode(code) { var ec = new ExecutionContext(); control.ecStack.push(ec); control.runningEC = ec; control.execute(code);}

执行环境( Execution Context ) 一个执行环境对象包括:

词法环境 – LexicalEnvironment 变量环境 – VariableEnvironment This 绑定 - ThisBinding

ExecutionContext: { LexicalEnvironment, VariableEnvironment, ThisBinding}

词法环境( LexicalEnvironment ) 既是一个属性,又是一个类型。 每个执行环境对象都有且仅有一个关联的词法

环境对象。 在代码执行过程中,需要解析变量时,通过词

法环境对象进行解析,从其环境数据中得到值。 一个词法环境对象包括:

环境数据 – environement records 外层环境 – outer environment

词法环境( LexicalEnvironment ) 存在 2 种词法环境的实现类型

DeclarativeEnvironment ObjectEnvironment

区别是 ObjectEnvironment 可接受指定的对象作为环境数据属性的值

?什么情况会出现ObjectEnvironment

变量环境( VariableEnvironment ) 每个执行环境对象都有且仅有一个关联的变量

环境对象。 变量环境仅仅是一个名字,变量环境对象的类

型是词法环境( LexicalEnvironment )。 在进入(开始执行)代码时,所有的变量标识

符( Identifier )会存放在当前的变量环境对象中。

变量环境中有环境数据属性,但不使用外层环境属性。

环境数据( environment records ) 存在于词法环境或变量环境中。 包含一个 binding object ,简单地认为 binding

object 是一个 Map 对象,保存变量标签符( Identifier )和变量值( Value )的关系。

常用方法: hadBinding(name) – 查看是否有变量绑定 createBinding(name) – 创建一个变量绑定 setBinding(name, value) – 修改变量绑定的值 getValue(name) – 获取变量绑定的值 deleteBinding(name) – 删除一个变量绑定

环境数据( 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; }, // …}

环境数据( environment records ) 存在 2 种环境数据的实现类型

DeclarativeEnvironmentRecords ObjectEnvironmentRecords

LexicalEnvironment EnvironmentRecords

ObjectEnvironment

DeclaractiveEnvironment

ObjectEnvironmentRecords

DeclaractiveEnvironmentRecords

创建词法环境 NewDeclarativeEnvironment(e)

用于创建一个 DeclarativeEnvironment 进入函数时 执行 catch 表达式时

NewObjectEnvironment(o, e) 用于创建一个 ObjectEnvironment 执行 with 表达式时

创建词法环境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;}

消化一下• 名词解释完了吗?

NO

总结ExecutionContext: { LexicalEnvironment, VariableEnvironment, ThisBinding}

EnvironmentRecords: { hasBinding(name), createBinding(name), setBinding(name, value), getValue(name), deleteBinding(name)}

LexicalEnvironment: { environmentRecords, outerEnvironment}

函数( Function ) 是一个对象( Object )。 包含几个特殊的属性

[[Construct]] – new SomeFunction() [[Call]] – someFunction() [[HasInstance]] – o instanceof SomeFunction [[Scope]] – 闭包 [[FormalParameters]] – 参数列表 [[Code]] – 可执行代码

包含可执行代码( Executable Code )和执行状态( State )。

创建函数( Create Function Object ) 新建一个普通对象( new Object() ) 将 [[Class]] 设为” function” 将 [[Prototype]] 指向 Function.prototype 根据默认的规则,设置 [[Call]] 、 [[Contruct]]

及 [[HasInstance]] 属性 将 [[Scope]] 设置为当前的 LexicalEnvironment

或 VariableEnvironment 对象 设置 [[Code]] 、 [[FormalParameterList]] 及

name 、 length 、 prototype 属性

创建函数( 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 };

创建函数( 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();

进入函数( Entering Function Code ) 新建一个执行环境。 根据规则设置 this 绑定。

如果 thisArg 是 null或 undefined,设置为 global。 如果 thisArg 不是 Object,设置为ToObject(thisArg) 。

以函数的 [[Scope]] 属性为参数, NewDeclarativeEnvironment 创建一个LexicalEnvironment 对象。

将当前 LexicalEnvironment 设置为该值。 将当前 VariableEnvironment 设置为该值。 开始初始化参数及函数内声明的变量。

同一对象

进入函数( 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]]);

进入函数( Entering Function Code ) 执行函数时,在作用域( [[Scope]] )的基础

上添加词法环境( LexicalEnvironment )

Global Environment

Outer Environment

Current Environment

// Global Environmentfunction outer() { // Outer Environment function inner() { // Current Environment }}var inner = outer();// [[Scope]] === Outer Environmentinner();

定义绑定初始化 ( 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); }}

定义绑定初始化 ( Declaration Binding Instantiation ) 遍历 FormalParameterList (参数列表),对每一项

(参数),如果 VariableEnvironment 中不存在,则添加并赋值。

依次遍历源码中每个 FunctionDeclaration (函数声明),对每一项(函数),如果 VariableEnvironment中不存在,则添加,随后赋值。

如果 VariableEnvironment 中不存在 arguments ,则添加并赋值。

依次遍历源码中每个 VariableDeclaration (变量声明),对每一项(变量),如果 VariableEnvironment 中不存在,则添加并赋值为 undefined 。

定义绑定初始化 ( 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 Environment

arguments

消化一下• 还有完没完了!

NO

变量查找 GetIdentifierReference(lex, name) 从给定的 LexicalEnvironment 中查找是否存

在该变量 如果不存在,则从 LexicalEnvironment 的

Outer Environment 中查找 依次进行,直到 Outer Environment 为

null ,则返回 undefined 返回一个 Reference 对象,通过 GetValue

进一步获取变量的值

变量查找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);}

大串烧 进入全局环境

创建全局执行环境并入栈 创建全局环境对象 全局的词法环境对象指向该对象 全局的变量环境对象指向该对象 在变量环境中添加 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();

大串烧 创建 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();

大串烧Global Environment

outer: { [[Scope]] }inner: undefinedprefix: undefined

Global Execution Context

VariableEnvironment

LexicalEnvironment

大串烧 为 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();

大串烧Global Environment

outer: { [[Scope]] }inner: undefinedprefix: ‘Hello ‘

Global Execution Context

VariableEnvironment

LexicalEnvironment

大串烧 执行 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();

大串烧 创建 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();

大串烧

Global Execution Context

Global Environment

outer: { [[Scope]] }inner: undefinedprefix: ‘Hello ‘

VariableEnvironment

LexicalEnvironment

Outer Environmentsay: { [[Scope]] }name: undefined

Outer Execution Context

VariableEnvironment

LexicalEnvironment

大串烧 为 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();

大串烧Global Environment

outer: { [[Scope]] }inner: { [[Scope]] }prefix: ‘Hello ‘

Outer Environmentsay: { [[Scope]] }name: ‘Gray Zhang’

Global Execution Context

VariableEnvironment

LexicalEnvironment

Outer Execution Context

VariableEnvironment

LexicalEnvironment

大串烧 返回并赋值给 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();

大串烧Global Environment

outer: { [[Scope]] }inner: { [[Scope]] }prefix: ‘Hello ‘

Outer Environmentsay: { [[Scope]] }name: ‘GrayZhang’

Global Execution Context

VariableEnvironment

LexicalEnvironment

大串烧 执行 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();

大串烧Global Environment

outer: { [[Scope]] }inner: { [[Scope]] }prefix: ‘Hello ‘

Outer Environmentsay: { [[Scope]] }name: ‘GrayZhang’

Inner Environment

message: undefined

Global Execution Context

VariableEnvironment

LexicalEnvironment

Inner Execution Context

VariableEnvironment

LexicalEnvironment

大串烧 为 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();

大串烧Global Environment

outer: { [[Scope]] }inner: { [[Scope]] }prefix: ‘Hello ‘

Outer Environmentsay: { [[Scope]] }name: ‘GrayZhang’

Inner Environment

message: ‘Hello GrayZhang’

Global Execution Context

VariableEnvironment

LexicalEnvironment

Inner Execution Context

VariableEnvironment

LexicalEnvironment

大串烧 获取 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 从何而来?

大串烧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();

总结 相关概念

可执行代码 – Executable Code 执行环境 – Execution Context 词法环境 – LexicalEnvironment 变量环境 – VariableEnvironment 环境数据 – Environment Records

总结 过程

创建函数 – [[Scope]] [[Scope]] 在创建时决定且不会变化

进入函数 – 执行环境 + 词法环境 + 变量环境 执行时在最内层增加词法环境

定义绑定初始化 – 参数 + 函数声明 + 变量声明 变量环境和词法环境是同一个对象

变量查找 – GetIdentifierReference 延词法环境自内向外查找

继续消化• 我以为我懂了,直到……– How with works– How catch works– How let works

– When code meets eval– When code meets new Function

– When there is strict mode

从代码说起function outer() { var o = LargetObject.fromSize('400MB');

return function() { console.log('inner'); };}var inner = outer();// 对象图 此时对象之间的引用关系?

Global

Function

Lexical Environmen

t

Environment Records

Binding Object

Large Object

inner [[Scope]]

environmentRecords

bindingObject

o

Global 和 o 有间接引用,无法回收 o

但是事实上……function outer() { var i = 3; return function() { debugger; };}

var inner = outer();inner();

javascript 引擎有能力回收 i

如果你是计算机……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: 不可回收

人类的智商

计算机的智商

压力

好大

~

测试方法 用断点!

Chrome / Firefox

看内存! IE / Opera

一些基本结果 IE6 – 8 没有回收闭包内变量的机制 Opera 没有回收闭包内变量的机制 Chrome 回收闭包内变量后,再次访问该变量

将抛出 ReferenceError Firefox 回收闭包内变量后,再次访问该变量

会得到 undefined Chrome 、 Firefox 和 IE9 回收闭包内变量的策略基本相同

试问! 有哪些因素可能导致变量无法回收?

变量被返回的函数直接引用。 变量被返回的函数间接引用(通过嵌套函数)。 返回的函数中有 eval 。 返回的函数在 with 表达式建立的作用域中。 返回的函数在 catch 表达式中。

只谈结果,不谈过程!

直接引用Engine Collectable

Chrome – V8 NO

Firefox – SpiderMonkey NO

IE9 - Chakra NO

function outer() { var i = 3; return function() { i; };}var inner = outer();

间接引用Engine Collectable

Chrome – V8 NO

Firefox – SpiderMonkey NO

IE9 - Chakra NO

function outer() { var i = 3; function help() { i; } return function() { help(); };}var inner = outer();

嵌套函数的平衡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();需要图的遍历需要处理环引用高成本 + 低

嵌套函数的平衡Engine Collectable

Chrome – V8 NO

Firefox – SpiderMonkey NO

IE9 - Chakra NO

function outer() { var i = 3; function help() { i; }

return function() { };}var inner = outer();

大恶魔 evalfunction outer() { var i = 3;

return function() { return eval(‘i’); }}

var inner = outer();var result = inner();console.log(result); // 3

?

由字符串从词法环境中获取对象的唯一途径

可变性 特殊性

大恶魔 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’));

大恶魔 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

Too Simple,

Sometimes Native.

大恶魔 evalEngine Collectable

Chrome – V8 NO

Firefox – SpiderMonkey NO

IE9 - Chakra NO

function outer() { var i = 3;

return function() { eval(‘’); // 无论 eval 的内容是什么 };}var inner = outer();

间接 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.

间接 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); // 3

X

间接 eval 和 new FunctionEngine Collectable

Chrome – V8 YES

Firefox – SpiderMonkey YES

IE9 - Chakra YES

function outer() { var i = 3;

return function() { window.eval(‘i’); };}var inner = outer();

关于 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();

??

关于 with 的分歧Engine Collectable

Chrome – V8 NO

Firefox – SpiderMonkey 回收 k, scope ,不回收 i, j

IE9 - Chakra 回收 k ,不回收 i, j , scope未知function outer() {

var scope = { i: 3, j: 4 }; var k = 4; with (scope) { return function() { i; }; }}var inner = outer();

不被重视的 catchEngine Collectable

Chrome – V8 回收 i ,不回收 ex

Firefox – SpiderMonkey 回收 i ,不回收 ex

IE9 - Chakra 回收 i 和 exfunction outer() { var i = 3; try { throw { j: 4 }; } catch (ex) { return function() {}; }}var inner = outer();

你能骗过引擎吗?function outer() { var i = 3; var j = 4;

return function(i) { var j = 5; console.log(i + j); };}var inner = outer();inner(6);

?

Engine Collectable

Chrome – V8 YES

Firefox – SpiderMonkey YES

IE9 - Chakra YES

总结 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);

谢谢

知识要点• 变量声明在变量环境中,从词法环境中获

取,通常 2者是同一个对象。• 作用域在函数创建时生成,是函数对象的

不变的属性 – 静。• 执行函数时,在作用域上增加一个词法环

境对象 – 动。• 动静结合即闭包的本质。• 闭包对垃圾回收会有一定的影响。

参考资料• 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

Recommended