深刻理解javascript做用域系列第二篇——词法做用域和动态做用域

前面的话

  大多数时候,咱们对做用域产生混乱的主要缘由是分不清楚应该按照函数位置的嵌套顺序,仍是按照函数的调用顺序进行变量查找。再加上this机制的干扰,使得变量查找极易出错。这其实是由两种做用域工做模型致使的,做用域分为词法做用域和动态做用域,分清这两种做用域模型就可以对变量查找过程有清晰的认识。本文是深刻理解javascript做用域系列第二篇——词法做用域和动态做用域javascript

 

词法做用域

  第一篇介绍过,编译器的第一个工做阶段叫做分词,就是把由字符组成的字符串分解成词法单元。这个概念是理解词法做用域的基础html

  简单地说,词法做用域就是定义在词法阶段的做用域,是由写代码时将变量和块做用域写在哪里来决定的,所以当词法分析器处理代码时会保持做用域不变java

关系函数

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

function foo(a) {
    var b = a * 2;
    function bar(c) {
        console.log( a, b, c );
    }
    bar(b * 3);
}
foo( 2 ); // 2 4 12

  在这个例子中有三个逐级嵌套的做用域。为了帮助理解,能够将它们想象成几个逐级包含的气泡spa

  做用域气泡由其对应的做用域块代码写在哪里决定,它们是逐级包含的code

  气泡1包含着整个全局做用域,其中只有一个标识符:foohtm

  气泡2包含着foo所建立的做用域,其中有三个标识符:a、bar和b对象

  气泡3包含着bar所建立的做用域,其中只有一个标识符:cblog

查找

  做用域气泡的结构和互相之间的位置关系给引擎提供了足够的位置信息,引擎用这些信息来查找标识符的位置

  在代码片断中,引擎执行console.log(...)声明,并查找a、b和c三个变量的引用。它首先从最内部的做用域,也就是bar(...)函数的做用域开始查找。引擎没法在这里找到a,所以会去上一级到所嵌套的foo(...)的做用域中继续查找。在这里找到了a,所以引擎使用了这个引用。对b来说也同样。而对c来讲,引擎在bar(...)中找到了它

  [注意]词法做用域查找只会查找一级标识符,若是代码引用了foo.bar.baz,词法做用域查找只会试图查找foo标识符,找到这个变量后,对象属性访问规则分别接管对bar和baz属性的访问

foo = {
    bar:{
        baz: 1
    }
};
console.log(foo.bar.baz);//1

遮蔽

  做用域查找从运行时所处的最内部做用域开始,逐级向外或者说向上进行,直到碰见第一个匹配的标识符为止

  在多层的嵌套做用域中能够定义同名的标识符,这叫做“遮蔽效应”,内部的标识符“遮蔽”了外部的标识符

var a = 0;
function test(){
    var a = 1;
    console.log(a);//1
}
test();

  全局变量会自动为全局对象的属性,所以能够不直接经过全局对象的词法名称,而是间接地经过对全局对象属性的引用来对其进行访问

var a = 0;
function test(){
    var a = 1;
    console.log(window.a);//0
}
test();

  经过这种技术能够访问那些被同名变量所遮蔽的全局变量。但非全局的变量若是被遮蔽了,不管如何都没法被访问到

 

动态做用域

  javascript使用的是词法做用域,它最重要的特征是它的定义过程发生在代码的书写阶段

  那为何要介绍动态做用域呢?实际上动态做用域是javascript另外一个重要机制this的表亲。做用域混乱多数是由于词法做用域和this机制相混淆,傻傻分不清楚

  动态做用域并不关心函数和做用域是如何声明以及在任何处声明的,只关心它们从何处调用。换句话说,做用域链是基于调用栈的,而不是代码中的做用域嵌套

var a = 2;
function foo() {
    console.log( a );
}
function bar() {
    var a = 3;
    foo();
}
bar();

  【1】若是处于词法做用域,也就是如今的javascript环境。变量a首先在foo()函数中查找,没有找到。因而顺着做用域链到全局做用域中查找,找到并赋值为2。因此控制台输出2

  【2】若是处于动态做用域,一样地,变量a首先在foo()中查找,没有找到。这里会顺着调用栈在调用foo()函数的地方,也就是bar()函数中查找,找到并赋值为3。因此控制台输出3

  两种做用域的区别,简而言之,词法做用域是在定义时肯定的,而动态做用域是在运行时肯定的

相关文章
相关标签/搜索