说说 JavaScript 执行环境以及作用域

  • 全局执行环境是最外围的执行环境。
  • 在 web 浏览器中,全局执行环境是 window 对象。
  • 全局执行环境直到应用程序退出才会被销毁。
  • 每个函数都有自己的执行环境。在执行一个函数时,函数的环境会被推入环境栈中,函数执行后,函数的环境会从当前栈中弹出,把控制权返回给之前的执行环境。
  • 当代码在一个环境中执行时,会创建变量对象的作用域链。它的用途是保证执行环境有权访问所有变量和函数。
  • 全局执行环境的变量对象是作用域链的最后一个对象。
  • 标识符的解析是沿着作用域链一级一级搜索,搜索过程从作用域链的前端开始,然后逐级向后回溯,直到找到标识符为止(找不到会报错):
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>函数内部可以在作用域链中找到全局环境的变量</title>
</head>
<body>

<script type="text/javascript">
    var color = "blue";

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

    changeColor();
    console.log("Color is now " + color);//green
</script>
</body>
</html>
  • 函数内部可以在作用域链中找到祖先环境下的变量:
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>函数内部可以在作用域链中找到祖先环境下的变量</title>
</head>
<body>

<script type="text/javascript">
    var color = "blue";

    function changeColor(){
        var anotherColor = "green";

        function swapColors(){
            var tempColor = anotherColor;
            anotherColor = color;
            color =tempColor;
            // 可以访问 color、anotherColor 和 tempColor
        }

        swapColors();
        // 可以访问 color、anotherColor
    }

    changeColor();
    console.log("Color is now " + color);//green
    // 可以访问 color
</script>
</body>
</html>
  • 作用域链示意图:

  • 函数的内部环境可以通过作用域链访问所有的外部环境,这些环境之间的联系是线性、有次序的。
  • 每个环境都可以向上搜索作用域链。
  • 函数参数也被当做变量来对待,所以其访问规则与执行环境中的其他变量相同。

1 延长作用域链

当执行流进入下列语句时,作用域链会延长:
* try-catch 语句中的 catch 块
* with 语句

这些语句会在作用域的前端临时增加一个变量对象,该变量对象会在代码执行后被移除。

  • 对于 catch 语句,会创建一个新的变量对象,其中包含被抛出的错误对象的声明。
  • 对于 with 语句:
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>延长作用域链</title>
</head>
<body>

<script type="text/javascript">
    function buildUrl(){
        var qs = "?debug=true";

        with (location){
            var url = href + qs;
        }
        return url;
    }

    console.log(buildUrl());
</script>
</body>
</html>
  • 例子中的 with 语句接收的是 location 对象,所以其变量对象就包含了 location 对象的所有属性和方法。

在 IE8及之前的版本中, catch 语句捕获的错误对象会被添加到执行环境的变量对象,而不是 catch 语句的变量对象,IE9 修复了这个问题

2 没有块级作用域

  • 其他类 C 的语言中,由花括号封闭的代码块都有块级作用域,但 JavaScript 没有!
if (true){
    var color = "blue";
}

console.log(color); //"blue"
  • if 语句中声明的变量会将变量添加到当前的执行环境中!!在使用 for 语句时尤其要牢记这一点:
for (var i=0; i < 10; i++){
    doSomething(i);
}

alert(i);//10

2.1 声明变量

  • 使用 var 声明的变量会被自动添加到最接近的环境中。在函数内部,到最接近的环境是函数局部环境;在 with 语句中,是函数环境。
  • 函数的局部变量,在函数外部无法访问:
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>函数的局部变量,在函数外部无法访问</title>
</head>
<body>

<script type="text/javascript">
    function add(num1, num2){
        var sum = num1 + num2;
        return sum;
    }

    console.log(add(10,20));//30
    console.log(sum);//错误
</script>
</body>
</html>
  • 如果初始化变量没有使用 var 声明,变量会被自动添加到全局环境中:
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>函数内定义的变量(省略了 var),在函数外部可以访问</title>
</head>
<body>

<script type="text/javascript">
    function add(num1, num2){
        sum = num1 + num2;
        return sum;
    }

    console.log(add(10,20));//30
    console.log(sum);//30(全局变量)
</script>
</body>
</html>
  • 建议在初始化变量之前,一定要先声明,这样可以避免这些问题。
  • 在严格模式下,初始化未经声明的变量会报错。

2.2 查询标识符

  • 搜索会从作用域链的前端开始,向上逐级查询标识符。如果在全局环境下也没有找到,即意味着这个变量还未声明:
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>沿作用域链向上搜索标识符定义</title>
</head>
<body>

<script type="text/javascript">
    var color = "blue";

    function getColor(){
        return color;
    }

    console.log(getColor());//blue
</script>
</body>
</html>
  • 如果局部环境中存在同名的标识符,就不会使用父环境中的标识符:
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>沿作用域链向上搜索标识符定义,找到立即停止搜索</title>
</head>
<body>

<script type="text/javascript">
    var color = "blue";

    function getColor(){
        var color = "green";
        return color;
    }

    console.log(getColor());//green
</script>
</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值