Javascript:对作用域链的认识

作用域链

作用域:提高了程序逻辑的局部性,增强程序的可靠性,减少名字冲突。
看一个例子:

var s1 = 'sharpxiajun';

function ftn(){
  var s2 = 'xtq';
  
  console.log(this); //window
  console.log('s1:' + this.s1 + ';s2:' + this.s2); // s1:sharpxiajun;s2:undefined
  console.log('s1:'+this.s1+';s2:' + s2); //s1:sharpxiajun;s2:xtq
}
ftn();

在javascript世界里有一个大的作用域环境,这个环境就是window,window环境不需要我们自己使用什么方式构建,页面加载时候页面会自动构造的,上面代码里有一个大括号,这个大括号是对函数的定义,运行时,我们发现函数作用域内部定义的s2变量是不能被window对象访问的,因此s2变量是被{}保护起来了,它的生命周期和这个函数的生命周期有关。

在javascript里作用域有一个专门的定义execution context,叫执行上下文或执行环境,我们来想想javascript里那些情况是执行:
情况一:当页面加载时候在script标签下的javascript代码会按顺序执行,而这些能被执行的代码都是属于window的变量或函数;
情况二:当函数的名字后面加上小括号(),例如ftn(),这也是在执行,不过它执行的是函数。

如此说来,javascript里的执行环境有两类一类是全局执行环境,即window代表的全局环境,一类是函数代表的函数执行环境,这也就是我们常说的局部作用域。

执行环境在javascript语言里并非是一个抽象的概念,而是有具体的实现,这个实现其实是个对象,这个对象也有个名字叫做variable object,称为上下文变量, 上下文变量存储的是上下文变量所处执行环境里定义的所有的变量和函数。

全局执行环境的上下文变量是可以访问到的,它就是window对象,所以我们说window能代表全局作用域是有道理的,但是局部作用域即函数的执行环境里的上下文变量是代码不能访问到的,不过javascript引擎在处理数据时候会使用到它。

在javascript语言里还有一个概念,它的名字叫做execution context stack,就是执行环境栈,每个要被执行的函数都会先把函数的执行环境压入到执行环境栈里,函数执行完毕后,这个函数的执行环境就会被执行环境栈弹出,例如上面的例子:函数执行时候函数的执行环境会被压入到执行环境栈里,函数执行完毕,执行环境栈会把这个环境弹出,执行环境栈的控制权就会交由全局环境,如果函数后面还有代码,那么代码就是接着执行。如果函数里嵌套了函数,那么嵌套函数执行完毕后,执行环境栈的控制权就交由了外部函数,然后依次类推,最后就是全局执行环境了。

讲到这里我们大名鼎鼎的作用域链要登场了,函数的执行环境被压入到执行环境栈里后,函数就要执行了,函数执行的第一步不是执行函数里的第一行代码而是在上下文变量里构造一个作用域链,作用域链的英文名字叫做scope chain,作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,这个概念里有两个关键意思:有权访问和有序,我们看看下面的代码:

var b1 = 'b1';
function ftn1(){

  var b2 = 'b2';
  var b1 = 'bbb';

  function ftn2(){
    var b3 = 'b3';
    b2 = b1;
    b1 = b3;

    console.log('b1:'+ b1 +';b2:' + b2 +';b3' + b3); 
    // b1:b3; b2:bbb; b3:b3
  }
  
  ftn2();
}

ftn1();
console.log(b1);

这个例子我们发现,ftn2函数可以访问变量b1,b2,这个体现了有权访问的概念,当ftn1作用域里改变了b1的值并且把b1变量重新定义为ftn1的局部变量,那么ftn2访问到的b1就是ftn1的,ftn2访问到b1后就不会在全局作用域里查找b1了,这个体现了有序性。

广大程序员对作用域链的理解有两块一块是作用域,而作用域在javascript语言里指的是执行环境execution context,执行环境在javascript引擎里是通过上下文变量体现的variable object,javascript引擎里还有一个概念就是执行环境栈execution context stack,当某一个函数的执行环境压入到了执行环境栈里,这个时候就会在上下文变量里构造一个对象,这个对象就是作用域链scope chain,而这个作用域链就是广大程序员理解的第二块知识,作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的。

很多人常常认为作用域链是理解this指针的关键,这个理解是不正确的,this指针构造是和作用域链同时发生的,也就是说在上下文变量构造作用域链的同时还会构造出一个this对象,this对象是属于上下文变量,而this变量的值就是当前执行环境外部的上下文变量的一份拷贝,这份拷贝里没有作用域链变量的

function show () {
  alert(this);
  function show2 () {
    alert(this);
  }
  show2();
}
show();

我们看到函数show 和 show2里的shis指针都是指向window,这是为什么了?因为在JavaScript我们定义函数方式是通过function xxx() {}形式,那么这个函数不管定义在哪里(包括匿名函数),它都是属于全局对象window,所以他们的执行环境的外部的执行上下文都是指向window。

var a = 10;
function test(){
  a = 100;
  alert(a);
  alert(this.a);
  var a;
  alert(a);
} 
test();
//正确答案:100 10 100
var a = 100;
function test(){
  alert(a);
  var a = 10;
  alert(a);
}
test();
//正确答案: undefined 10
var a = 100;
function test(){
  alert(a);
  a = 10;
  alert(a);
}
test();
alert(a);
//正确答案:100 10 10;

参考文献:http://www.cnblogs.com/sharpxiajun/p/4133984.html

你可能感兴趣的:(Javascript:对作用域链的认识)