1

执行环境

执行环境就是“环境”,是js中最重要的概念。执行环境定义了变量或函数有权访问的其他数据。每个执行环境都有一个与之相关的变量对象(我们编写的代码无法访问这个对象)

  • 全局执行环境(是最外围的执行环境。在Web浏览器中,全局执行环境是Window对象。某个执行环境中的所有代码执行完毕后,该环境会被销毁;全局执行环境则直到应用程序退出,如关闭网页或浏览器时才会被销毁。环境被销毁时,保存在其中的所有变量和函数定义也都会被销毁。

  • 执行环境(当执行流进入一个函数时,函数的环境会被推入一个环境栈中。当函数执行之后,环境栈将其弹出,把控制权返回给之前的执行环境。)

  • 作用域链(当代码在一个环境中执行时,会创建变量对象的一个作用域链。其用途是保证对执行环境有权访问的所有变量和函数的有序访问。一个包含环境的变量对象到另一个包含环境的变量对象,最后到全局执行环境的变量对象。所以,全局执行环境的变量对象始终都是作用域链中的最后一个对象。

标识符解析

标识符解析是沿着作用域链一级一级地搜索标识符的过程。搜索过程从作用域链的最前端开始,逐级向后回溯,直到找到标识符为止。如:

var color = "blue";

function changeColor(){
    if (color === "blue"){
        color = "red";
    }else {
        color = "blue";
    }
}

changeColor();

document.write("Color is now " + color);

在上面的例子中,函数changeColor()的作用域链包含两个对象:changeColor()函数自己的变量对象和全局环境的变量对象。(可以在函数内部访问变量color,就是因为可以在这个作用域链中找到它。)在看另一个例子:

var color = "blue";

function changeColor(){
    var anotherColor = "red";
    
    function swapColors(){
        var tempColor = anotherColor;
        anotherColor = color;
        color = tempColor;
        //这里可以访问tempColor anotherColor color
    }
    
    //这里可以访问anotherColor color
}

//这里可以访问color
changeColor();
  • 执行环境:3个,全局环境,changeColor()的局部环境和swapColors()的局部环境。

  • 全局环境:一个变量color 和一个函数changeColor()。

  • changeColor()环境:一个变量anotherColor 和一个swapColors()函数。(可以访问全局变量中的color)

  • swapColors()环境:一个变量tempColor。(可以访问全局环境中的color 和changeColor()环境中的anotherColor变量。)

swapColors()内部可以访问到其他两个环境中的所有变量,因为其他两个环境是它的父执行环境*

上面的例子可以用作用域链表示出来:(话说有没有网友可以推荐下好用的图床呀?纯文本画画还真麻烦。)

window(全局环境)
    |
    |-color(变量)
    |
    |-changeColor()(局部环境)
        |
        |-anotherColor(变量)
        |
        |-swapColors()(局部环境)
            |
            |-tempColor(变量)
            

内部环境可以通过作用连访问所有的外部环境,但外部环境不能访问内部环境的任何变量和函数。就是说,每个环境都可以向上搜索作用域链,但任何环境都不能向下搜索作用域链。

延长作用域链

虽然执行环境的类型总共只有全局和局部(函数)两种,但是还是可以使用下面两种办法来延长作用域链。

  • try-catch 语句的catch 块;

  • with 语句

上面这种方法就是当执行流进入下列任何一个语句时,作用域链就会得到加长。如:

function log(){
    var string = "the location is: "
    
    with(location){
        var url = string + href;
    }
    
    return url;
}

document.write(log());
//the location is: ... Volumes/TOSHIBA/project/tester/repetition.html

这里面使用with 语句,可以看到return url 语句可以访问到with 语句中的url 变量。再看下面的例子:

function log(){
    var string = "the location is: "
    function href(){
        var url = string + location.href;
    }
    return url;
}
document.write(log());
// ReferenceError: Can't find variable: url

如果是普通的function 函数则不能访问url 变量。

另外,在IE8 中,即使是在catch 块的外部也可以访问到错误对象。IE9 修复了这个问题。

没有块级作用域

JavaScript 不像C 语言中,由花括号封闭的代码块都有自己的作用域。JavaScript 没有块级作用域。如:

if (true){
    var color = "blue";
}
document.write(color); //"blue"

花括号以外的地方仍然可以访问话括号内的变量。

尤其应该在for 循环中注意这个特性:

for (var i = 0; i < 5; i ++){
    var count = i + i;
}
document.write(count); //8

声明变量

如果初始化变量时没有使用var 声明,该变量会被添加到全局环境中去。如:

function count(x,y){
    var result = x + y;
}
count(1,2);
document.write(result); // ReferenceError: Can't find variable: result

function count(x,y){
    result = x + y;
}
count(1,2);
document.write(result); //3

在编写代码的过程中,不声明而直接初始化变量是一个常见的错误做法。很可能会导致以外。建议在初始化变量之前,一定要先声明。在严格模式下,初始化未经声明的变量会导致错误。

查询标识符

搜索过程就是沿作用域链向上查询的过程。如:


var result = 1;
function func(){
    document.write(result);
}
func(); //1

如果局部环境中存在着同名标识符,就不会使用位于父环境中的标识符。如:


var result = 2;
function func(){
    var result = 1;
    document.write(result);
}
func(); //1
document.write(window.result); //2 或者直接用下面的代码返回最初的result
document.write(result);

JS菌
6.4k 声望2k 粉丝