Scope & Closure
JavaScript IS a compile language
Compile
- Tokenizing / Lexing(tokens)
- Parsing(AST)
- Code Generation(machine codes)
- Declare `a` at compile time
- Find `a` and assign `2` to it at run time
var a = 2;
LHS & RHS Lookup
- LHS: 给赋值操作找目标
- RHS: 找到一个变量并把值取回来
function foo(a) {
var b = a;
return a + b;
}
var c = foo( 2 );
Figure out RHS and LHS
Exception
- LHS: 找不到就创建
- RHS: 找不到的时候 ReferenceError
Scope Model
- Dynamic Scope
- Lexical Scope
function foo() {
console.log( a ); // 2
}
function bar() {
var a = 3;
foo();
}
var a = 2;
bar();
Dynamic Scope: 关注在何处被调用
Lexical Scope: 关注在何处被声明
JavaScript has lexical scope model
Scope Chain
eval()
function foo(str, a) {
eval( str );
console.log( a, b );
}
var b = 2;
foo( "var b = 3;", 1 );
with()
function foo(obj) {
with (obj) {
a = 2;
}
}
var o1 = {
a: 3
};
var o2 = {
b: 3
};
foo( o1 );
console.log( o1.a );
foo( o2 );
console.log( o2.a );
console.log( a );
尽管`with`块可以将一个对象处理为词法作用域,但是这个块内部正常的`var`声明并不会被限制在这个块的作用域中,而是被添加到`with`所处的函数作用域中。
引擎在代码中发现了`eval(..)`或`with`,会**假设**关于标识符位置的判断都是无效的,因为无法在词法分析阶段明确的知道`eval(..)`会接收到什么代码,这些代码会如何对作用域进行修改,也无法知道传递给`with`的用来创建新的词法作用域的对象的内容到底是什么。
所有的优化**可能**都是无意义的,因此最简单的做法就是**完全不做**任何的优化。
DO NOT use them!
Function Scope
var a = 2;
(function IIFE( global, undefined ) {
var a = 3;
console.log( a ); // 3
console.log( global.a ); // 2
console.log( undefined );
})( window );
console.log( a ); // 2
隐藏内部实现
尽量使用具名函数
传递引用
Block Scope
with()
try/catch
try {
undefined();
} catch (err) {
console.log( err );
}
console.log( err ); // ReferenceError: `err` not found
let
var foo = true;
if (foo) {
let bar = foo * 2;
bar = something( bar );
console.log( bar );
}
console.log( bar ); // ReferenceError
var foo = true;
if (foo) {
{
let bar = foo * 2;
bar = something( bar );
console.log( bar );
}
}
console.log( bar ); // ReferenceError
显示声明更清晰,易维护
{
console.log( bar ); // ReferenceError!
let bar = 2;
}
NO hoisting!
function process(data) {
}
var someReallyBigData = { .. };
process( someReallyBigData );
var btn = document.getElementById( "my_button" );
btn.addEventListener( "click", function click(evt) {
console.log("button clicked");
} );
How to polish performance?
for Loop
for (let i=0; i<10; i++) {
console.log( i );
}
console.log( i ); // ReferenceError
const
Hoisting
foo(); // ?
bar(); // ?
zoo(); // ?
console.log( a ); // ?
var a = 1;
var foo = function bar() {
// ...
};
function bar () {
// ...
}
Closure
闭包的作用:函数在其所定义的词法作用域以外的地方被调用,并能正常访问词法作用域中的内容。
必要条件:函数的引用被传递到外部
这个 reference 就叫「闭包」
var a = 2;
(function IIFE() {
console.log( a );
})();
?
function foo() {
var a = 2;
function baz() {
console.log( a );
}
bar( baz );
}
function bar(fn) {
fn();
}
var fn;
function foo() {
var a = 2;
function baz() {
console.log( a );
}
fn = baz;
}
function bar() {
fn();
}
foo();
bar();
function wait(message) {
setTimeout( function timer() {
console.log( message );
}, 1000 );
}
wait( "Hello, closure!" );
Scope & Closure
By Wangye Zhao
Scope & Closure
- 2,001