Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript深入系列之词法作用域和动态作用域 #1

Open
archerU opened this issue May 14, 2018 · 0 comments
Open

JavaScript深入系列之词法作用域和动态作用域 #1

archerU opened this issue May 14, 2018 · 0 comments

Comments

@archerU
Copy link
Owner

archerU commented May 14, 2018

作用域

作用域是根据名称查找变量的一套规则。

作用域规则用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量查找。

任何声明在某个作用域内的变量,都将附属于这个作用域。

JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。

词法作用域与动态作用域

词法作用域只由函数被声明时所处的位置决定,不论函数在哪里被调用,也不论它如何被调用。

让我们认真看个例子就能明白之间的区别:

var a = 2;

function foo() {
    console.log(a); 
}

function bar() {
    var a = 3;
    foo();
}

bar();

// 结果是 ???

假设JavaScript采用静态作用域,让我们分析下执行过程:

执行 foo 函数,先从 foo 函数内部查找是否有局部变量 a,如果没有,就根据书写的位置,查找上面一层的代码,也就是 a 等于 2,所以结果会打印 2。

假设JavaScript采用动态作用域,让我们分析下执行过程:

执行 foo 函数,当foo()无法找到a的变量引用时,会顺着调用栈在调用foo()的地方查找a,而不是在嵌套的词法作用域链中向上查找。由于foo()是在bar()中调用的,引擎会检查 bar()的作用域,并在其中找到值为3的变量a

前面我们已经说了,JavaScript采用的是静态作用域,所以这个例子的结果是 2。

动态作用域

动态作用域是基于调用栈的,而不是代码中的作用域嵌套。

也许你会好奇什么语言是动态作用域?

bash 就是动态作用域,不信的话,把下面的脚本存成例如 scope.bash,然后进入相应的目录,用命令行执行 bash ./scope.bash,看看打印的值是多少。

value=1
function foo () {
    echo $value;
}
function bar () {
    local value=2;
    foo;
}
bar

LHS 查询还是 RHS 查询

理解 LHS 还是 RHS 之前,我们需要完全理解JavaScript的工作原理。

  • 引擎

    从头到尾负责整个JavaScript程序的编译及执行过程。

  • 编译器

    引擎的好朋友之一,负责语法分析及代码生成等

  • 作用域

    引擎的另一个好朋友,负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。

引擎对一个变量进行LHS查询还是RHS查询,取决于这个变量是出现在赋值操作的左侧还是右侧。

如果引擎执行LHS查询无法找到目标变量,在非严格模式下会自动创建一个具有该名称的变量,并将其返还给引擎。严格模式禁止自动或隐式地创建全局变量,因此会抛出ReferenceError异常。

如果RHS查询在所有嵌套的作用域中遍寻不到所需的变量,引擎就会抛出ReferenceError异常。

如果RHS查询找到一个变量,但是你尝试对这个变量的值进行不合理的操作,会抛出TypeError异常。

ReferenceError同作用域判别失败相关,而TypeError则代表作用域判别成功了,但是对结果的操作是非法或不合理的。

思考题

最后,让我们看一个《JavaScript权威指南》中的例子:

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f();
}
checkscope();
var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();

猜猜两段代码各自的执行结果是多少?

这里直接告诉大家结果,两段代码都会打印:local scope。

原因也很简单,因为JavaScript采用的是词法作用域,函数的作用域基于函数创建的位置。

而引用《JavaScript权威指南》的回答就是:

JavaScript 函数的执行用到了作用域链,这个作用域链是在函数定义的时候创建的。嵌套的函数 f() 定义在这个作用域链里,其中的变量 scope 一定是局部变量,不管何时何地执行函数 f(),这种绑定在执行 f() 时依然有效。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant