We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
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 采用词法作用域(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。
foo
foo()
a
bar()
前面我们已经说了,JavaScript采用的是静态作用域,所以这个例子的结果是 2。
动态作用域是基于调用栈的,而不是代码中的作用域嵌套。
也许你会好奇什么语言是动态作用域?
bash 就是动态作用域,不信的话,把下面的脚本存成例如 scope.bash,然后进入相应的目录,用命令行执行 bash ./scope.bash,看看打印的值是多少。
value=1 function foo () { echo $value; } function bar () { local value=2; foo; } bar
理解 LHS 还是 RHS 之前,我们需要完全理解JavaScript的工作原理。
JavaScript
引擎
从头到尾负责整个JavaScript程序的编译及执行过程。
编译器
引擎的好朋友之一,负责语法分析及代码生成等
作用域
引擎的另一个好朋友,负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。
引擎对一个变量进行LHS查询还是RHS查询,取决于这个变量是出现在赋值操作的左侧还是右侧。
LHS
RHS
如果引擎执行LHS查询无法找到目标变量,在非严格模式下会自动创建一个具有该名称的变量,并将其返还给引擎。严格模式禁止自动或隐式地创建全局变量,因此会抛出ReferenceError异常。
ReferenceError
如果RHS查询在所有嵌套的作用域中遍寻不到所需的变量,引擎就会抛出ReferenceError异常。
如果RHS查询找到一个变量,但是你尝试对这个变量的值进行不合理的操作,会抛出TypeError异常。
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() 时依然有效。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
作用域
作用域是根据名称查找变量的一套规则。
作用域规则用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量查找。
任何声明在某个作用域内的变量,都将附属于这个作用域。
JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。
词法作用域与动态作用域
词法作用域只由函数被声明时所处的位置决定,不论函数在哪里被调用,也不论它如何被调用。
让我们认真看个例子就能明白之间的区别:
假设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,看看打印的值是多少。
LHS 查询还是 RHS 查询
理解 LHS 还是 RHS 之前,我们需要完全理解
JavaScript
的工作原理。引擎
从头到尾负责整个
JavaScript
程序的编译及执行过程。编译器
引擎的好朋友之一,负责语法分析及代码生成等
作用域
引擎的另一个好朋友,负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。
引擎对一个变量进行
LHS
查询还是RHS
查询,取决于这个变量是出现在赋值操作的左侧还是右侧。如果引擎执行
LHS
查询无法找到目标变量,在非严格模式下会自动创建一个具有该名称的变量,并将其返还给引擎。严格模式禁止自动或隐式地创建全局变量,因此会抛出ReferenceError
异常。如果
RHS
查询在所有嵌套的作用域中遍寻不到所需的变量,引擎就会抛出ReferenceError
异常。如果
RHS
查询找到一个变量,但是你尝试对这个变量的值进行不合理的操作,会抛出TypeError
异常。ReferenceError
同作用域判别失败相关,而TypeError
则代表作用域判别成功了,但是对结果的操作是非法或不合理的。思考题
最后,让我们看一个《JavaScript权威指南》中的例子:
猜猜两段代码各自的执行结果是多少?
这里直接告诉大家结果,两段代码都会打印:local scope。
原因也很简单,因为JavaScript采用的是词法作用域,函数的作用域基于函数创建的位置。
而引用《JavaScript权威指南》的回答就是:
JavaScript 函数的执行用到了作用域链,这个作用域链是在函数定义的时候创建的。嵌套的函数 f() 定义在这个作用域链里,其中的变量 scope 一定是局部变量,不管何时何地执行函数 f(),这种绑定在执行 f() 时依然有效。
The text was updated successfully, but these errors were encountered: