8. 什么是变量
A symbolic name associated with a value and whose
associated value may be changed.
-- Wikipedia
� 变量是一种关联关系(association)
� 关联的目标:
� 符号名(symbolic name)
� 值(value)
� 关联是单向的 – 永远不可能根据值找到变量
Identifier Value
name ‘GrayZhang’
14. 可执行代码(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);
15. 15
执行环境(Execution Context)
� 当进入(开始执行)一段可执行代码时,生成
一个执行环境对象。
� 执行环境对象通过栈(Stack)维护。
� 新建的执行环境对象称为“当前运行的执行环
境对象”。
function enterCode(code) {
var ec = new ExecutionContext();
control.ecStack.push(ec);
control.runningEC = ec;
control.execute(code);
}
30. 创建函数(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();
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 Environment
function outer() { Global Environment
// Outer Environment
function inner() { Outer Environment
// Current Environment
} Current Environment
}
var inner = outer();
// [[Scope]] === Outer Environment
inner();
34. 定义绑定初始化 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);
} }
}
36. 定义绑定初始化
(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;
}
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绑定并赋值
// 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();
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 Environment
outer: { [[Scope]] }
inner: undefined
prefix: undefined
Global Execution Context
VariableEnvironmen
t
LexicalEnvironment
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 Environment
outer: { [[Scope]] }
inner: undefined
prefix: ‘Hello ‘
Global Execution Context
VariableEnvironmen
t
LexicalEnvironment
45. 大串烧
� 执行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();
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();
56. 大串烧
� 获取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();
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();
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();
// 对象图 此时对象之间的引用关
系?
Lexical
Global Function
inner [[Scope]] Environment
Global o有间接引用,无法回收o
Global和o o environmentRecords
Large o Binding bindingObject Environment
Object Object Records
62. 但是事实上……
function outer() {
var i = 3;
return function() {
debugger;
};
}
var inner = outer();
inner();
javascript引擎有能力回收i
63. 如果你是计算机……
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();
68. 直接引用
Engine Collectable
Chrome – V8 NO
Firefox – SpiderMonkey NO
IE9 - Chakra NO
function outer() {
var i = 3;
return function() {
i;
};
}
var inner = outer();
69. 间接引用
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();
70. 嵌套函数的平衡
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();
需要图的遍历
需要处理环引用
高成本 + 低效
71. 71
嵌套函数的平衡
Engine Collectable
Chrome – V8 NO
Firefox – SpiderMonkey NO
IE9 - Chakra NO
function outer() {
var i = 3;
function help() {
i;
}
return function() {
};
}
var inner = outer();
72. 大恶魔eval
function outer() {
var i = 3;
?
return function() {
return eval(‘i’);
}
}
var inner = outer();
var result = inner();
console.log(result); // 3
由字符串从词法环境中获取对象的唯一途径
可变性 特殊性
73. 大恶魔eval
var 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. 大恶魔eval
function 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
76. ..
.
.
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
So
To
To
To
To
77. 大恶魔eval
Engine Collectable
Chrome – V8 NO
Firefox – SpiderMonkey NO
IE9 - Chakra NO
function 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 Function
var 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
80. 间接eval和new Function
Engine Collectable
Chrome – V8 YES
Firefox – SpiderMonkey YES
IE9 - Chakra YES
function 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的分歧
Engine Collectable
Chrome – V8 NO
Firefox – SpiderMonkey 回收k, scope
k, scope,不回收i, j
i
IE9 - 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();
83. 不被重视的catch
Engine Collectable
Chrome – V8 回收i,不回收ex
i ex
Firefox – SpiderMonkey 回收i,不回收ex
i ex
IE9 - Chakra 回收i和ex
i ex
function 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);
Engine Collectable
Chrome – V8 YES
Firefox – SpiderMonkey YES
IE9 - Chakra YES
85. 总结
� 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);