You-Dont-Know-JS - 词法做用域

原文: 原文1 | 原文2javascript

Lexical Scope - 词法做用域java

做用域有两种常见的模型,一种叫作 词法做用域 Lexical Scope,一种叫作动态做用域 Dynamic Scope。其中词法做用域更常见,被大多数语言采用,包括javascript。git

词法分析

词法分析过程Lex-time,是指系统讲源码字符串解读成有含义的token的过程。词法做用域就是说在词法分析过程当中指派的做用域,词法做用域在词法解析过程当中就已经定死了。github

依然有一些手段能在词法解析以后改变词法做用域,但这些作法并不推荐。使用关键词eval, with,会产生性能问题。浏览器

考虑以下代码:
fig2.png函数

  • 气泡(做用域) 1 :即全局做用域,包含变量foo
  • 气泡(做用域) 2 :foo的做用域,包含变量a,bar,b
  • 气泡(做用域) 3 :bar的做用域,包含变量c

bar 做用域里完整的包含了foo 的做用域, 由于bar 是在foo中定义的,产生嵌套做用域。值得注意的是,一个函数做用域只有可能存在于一个父级做用域中,不会同时存在两个父级做用域。性能

过程:this

语句console.log寻找变量a,b,c 其中c在本身的做用域中找到,a,b在本身的做用域中找不到,因而向上级做用域中查找,在foo的做用域中找到,而且调用。做用域向上查找的过程当中,匹配第一次查找到的变量,也就是说若是foo的做用域中也定义了c,但bar函数只调用本身做用域里的c。spa

做用域的查找一直会找到全局做用域的全局对象,好比浏览器中的window,你能够定义window.a来确保变量a能够被获取。code

词法做用域向上查找只查找第一级变量,好比变量foo.bar.baz, 查找的是对象foo,查找到了再从对象里获取bar以及baz,查找自己与bar,baz无关。

动态做用域 Dynamic Scope

动态做用域,javascript并没有采用,可是与js的 this机制很是类似,看以下代码:
动态做用域是在代码运行时定义的,而非代码解析时。

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

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

var a = 2;

bar();

bar调用,bar里面foo被调用,foo函数须要查找变量a,因为javascript采用词法做用域,foo被解析的时候是在全局做用域,因此a是全局做用域中的2,而非bar里面的a。假设js采用的是动态做用域,foo是在bar中被调用的,因此a查找到了bar做用域里的3。

做为对照,动态做用域不关心它自己是怎样在哪里声明的,只关心它在哪里调用的,动态做用域的域链基于调用栈,而不是代码中的嵌套关系。

相反,词法做用域关心的是函数在哪里声明的,动态做用域的概念和js中的this相同,this也关心函数在哪里调用的。

相关文章
相关标签/搜索