作用域链
概念: 代码在环境中执行的时候,会创建变量对象的一个作用域链。这个作用域链保证了对执行环境中所有的变量和函数的有序访问。当所需要的变量在当前的作用域中没有找到时,它就会一层一层向上查找,直到最后在全局作用域中查找。
执行环境
概念: 执行环境定义了变量和函数有权访问的其他数据,并决定了他们的行为。每个执行环境都有一个相关联的变量对象,环境中定义的所有变量和函数都保存在这个变量对象中。
全局作用域
概念: 全局作用域是最外层的一个执行环境,在浏览器中这个全局执行环境被看成是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
- 假设JavaScript采用
静态/词法作用域
,让我们分析下上面代码的执行过程:
执行 foo
函数,先从 foo
函数内部查找是否有局部变量 value
,
如果没有,就根据书写的位置,查找上面一层的代码,也就是 value
等于 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
块级作用域
概念
概念:
块级作用域
通过 let
和 const
声明,声明后的变量无法在块级作用域外被访问到。
块级作用域的创建方式:在函数内部和在代码块内部。
实现:
for
循环中定义的的 i
特点:
- 没有变量提升
- 不能重复声明
- 可以用于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