作用域,作用域链,和词法作用域

685 阅读3分钟

作用域链

概念: 代码在环境中执行的时候,会创建变量对象的一个作用域链。这个作用域链保证了对执行环境中所有的变量和函数的有序访问。当所需要的变量在当前的作用域中没有找到时,它就会一层一层向上查找,直到最后在全局作用域中查找。

执行环境

概念: 执行环境定义了变量和函数有权访问的其他数据,并决定了他们的行为。每个执行环境都有一个相关联的变量对象,环境中定义的所有变量和函数都保存在这个变量对象中。

全局作用域

概念: 全局作用域是最外层的一个执行环境,在浏览器中这个全局执行环境被看成是window对象,所有全局变量和函数都是作为window对象的属性和方法。

函数作用域

概念: 函数作用域指的是声明在函数内部的变量。在函数作用域中可以访问到外层作用域中的变量,但是外层作用域访问不到内层作用域中的变量。

实现:

var a = 1;
function check() {
    var b = 2;
    console.log(a);
}

check(); // 1
console.log(b); // ReferenceError

词法作用域 和 动态作用域

概念

词法作用域指的是函数的作用域在函数定义时候就决定了。

动态作用域指的是函数的作用域在函数调用的时候才决定。

let value = 1;

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

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

bar(); // 1

  1. 假设JavaScript采用 静态/词法作用域 ,让我们分析下上面代码的执行过程:

执行 foo 函数,先从 foo 函数内部查找是否有局部变量 value

如果没有,就根据书写的位置,查找上面一层的代码,也就是 value 等于 1

所以结果会打印 1

  1. 假设JavaScript采用 动态作用域 ,让我们分析下上面代码的执行过程:

执行 foo 函数,依然是从 foo 函数内部查找是否有局部变量 value

如果没有,就从调用函数的作用域,也就是 bar 函数内部查找 value 变量,

所以结果会打印 2

代码实测

inner = 'window';

function say() {
    console.log(inner);
    console.log(this.inner);
}

var obj1 = (function() {
    var inner = '1-1';
    return {
        inner: '1-2',
        say: function() {
            console.log(inner);
            console.log(this.inner);
        }
    }
})();

var obj2 = (function() {
    var inner = '2-1';
    return {
        inner: '2-2',
        say: function() {
            console.log(inner);
            console.log(this.inner);
        }
    }
})();


say();               // window window

obj1.say();          // 1-1 1-2

obj2.say();          // 2-1 2-2

obj1.say = say;

obj1.say();          // window 1-2

obj1.say = obj2.say;

obj1.say();          // 2-1 1-2

块级作用域

概念

概念:

块级作用域 通过 letconst 声明,声明后的变量无法在块级作用域外被访问到。

块级作用域的创建方式:在函数内部和在代码块内部。

实现:

for 循环中定义的的 i

特点:

  1. 没有变量提升
  2. 不能重复声明
  3. 可以用于for循环中定义i

代码实测

使用 let 声明

let sum = 0;
for(let i=0; i<5; i++) {
    sum += i;
}

console.log(sum);   // 10
console.log(i);     // Uncaught ReferenceError: i is not defined

使用 var 声明

let sum = 0;
for(var i=0; i<5; i++) {
    sum += i;
}

console.log(sum);   // 10
console.log(i);     // 5