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