【js基础】js的作用域

177 阅读2分钟

js作用域

把js的作用域说得通俗一点就是,作用域确定当前执行代码对变量的访问权限

JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。

静态作用域与动态作用域

静态作用域/词法作用域:函数的作用域在函数定义的时候就决定了。

动态作用域:词法作用域相对的是动态作用域,函数的作用域是在函数调用的时候才决定的。

例子

我们来简单看一个栗子:

 var value = 1;

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

function bar() {
    var value = 2;
    foo();
}

bar();

// 结果是 ???

静态作用域的分析:执行 foo 函数,先从 foo 函数内部查找是否有局部变量 value,如果没有,就根据书写的位置,查找上面一层的代码,也就是 value 等于 1,所以结果会打印 1。

动态作用域的分析:执行 foo 函数,依然是从 foo 函数内部查找是否有局部变量 value。如果没有,就从调用函数的作用域,也就是 bar 函数内部查找 value 变量,所以结果会打印 2。

因为JavaScript采用的是静态作用域,所以这个例子的结果是 1。

bash 就是动态作用域

还有《js权威指南》的两个例子,可以看一下:

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f();
}
checkscope();

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();

看看这两端代码输出的是什么?

因为JavaScript采用的是词法作用域,函数的作用域基于函数创建的位置。

所以两个答案都是 "local scope";

那么我们再深入思考一下,这两端代码有什么不一样吗?

结合上篇文章里所说的执行上下文的知识,其实这两段代码的执行上下文不一样。

让我们模拟第一段代码:(两个函数依次压入执行栈后才执行)

ECStack.push(<checkscope> functionContext);
ECStack.push(<f> functionContext);
ECStack.pop();
ECStack.pop();

让我们模拟第二段代码:(第一个函数压入执行栈后执行完毕弹出该执行上下文后再执行第二个函数)

ECStack.push(<checkscope> functionContext);
ECStack.pop();
ECStack.push(<f> functionContext);
ECStack.pop();

引用《JavaScript权威指南》的回答就是:

JavaScript 函数的执行用到了作用域链,这个作用域链是在函数定义的时候创建的。嵌套的函数 f() 定义在这个作用域链里,其中的变量 scope 一定是局部变量,不管何时何地执行函数 f(),这种绑定在执行 f() 时依然有效。

本文引用于以下链接,如有侵权联系作者可删

github.com/mqyqingfeng… github.com/mqyqingfeng…