作用域和作用域链
javascript中的作用域只有两种,一种是全局的,一种是函数的。即全局作用域和局部作用域。两者是包含关系,也就说局部作用域可以访问全局作用域,但是全局作用域不能访问局部作用域。 当函数执行时变量查找,会优先查找当前作用域,然后逐级向上查找,直到全局window。这样的链式称为作用域链。
预处理和变量解析
举个例子:
var name1 = '全局名称';
function print() {
console.log(name1 + '内层');
var name1 = '局部名称';
return function() {
console.log(name1 + '最内层');
};
}
console.log(name1 + '外层');
var print1 = print();
print1();
输出结果:
全局名称外层 undefined内层 分析:
1 预解析全局作用域
// 全局词法环境
// 第一行 遇到var关键字,变量提升 解析到全局的头部
name1 = undefined
// 第二行 遇到function关键字, 解析到全局的头部
print = function print() {
console.log(name1 + '内层');
var name1 = '局部名称';
return function() {
console.log(name1 + '最内层');
};
}
// 第十行 遇到var 关键字,变量提升 解析到全局的头部
print1 = nudefined;
// 没有关键字的行 不做解析
2 执行代码
// 第1行 遇到表达式 name1 = '全局名称'.name1被复制为全局名称
// 第10行 遇到函数print的调用print(),方法被调用开始对局部作用域中的代码进行解析执行
// 第11行 遇到函数prints的调用, 方法被调用 开始对局部作用域中的代码进行解析执行
3 预解析局部作用(函数词法环境)
// 第 3 行 没有遇到关键字,不解析
// 第 4 行 遇到var关键字,解析到局部作用域头部
name1 = undefined ---> 这也是为什么print执行之后打印的结果是 undefined内层
// 第 6 行 没有遇到关键字 不解析
4 执行局部代码
// 第 4 行 遇到表达式 name1 = '局部名称', name1 被复制为 局部名称
// 第 6 行 遇到表大师 name1 + '最内层', name1通过作用域链的逐级查找,在print中第二行找到对应的值 输出为 ‘局部名称’
5 局部执行完成,继续执行全局
所以最终结果:
// 全局名称外层
// undefined内层
// 局部名称最内层
常见案例
1 var 和 let 在for循环中的执行结果区别解析
// var 和 let 在for循环中的应用
for (var index = 0; index < 5; index++) {
setTimeout(function() {
console.log(index)
})
}
console.log(index + 'aaa')
// 结果为 5 5 5 5 5
for (let index = 0; index < 5; index++) {
setTimeout(function() {
console.log(index)
})
}
// 结果为 0 1 2 3 4
结果输出不同的原因是:
var js的事件执行机制导致最终输出5 5 5 5 5;js首先执行for循环,等for循环执行完成之后才会去执行setTimeout的异步任务
// 展开执行顺序如下
var index;
{
var index = 0;
}
{
var index = 0; index++;
}
{
var index = 0; index++; index++;
}
{
var index = 0; index++; index++; index++;
}
{
var index = 0; index++; index++; index++; index++;
setTimeout(function() {
console.log(index);
}, 0);
}
let 声明属于块级作用域,导致每个for循环都是一个单独的块
// 展开执行顺序如下
let index;
{
let index = 0;
setTimeout(function() {
console.log(index);
}, 0);
}
{
let index = 0; index++;
setTimeout(function() {
console.log(index);
}, 0);
}
{
let index = 0; index++; index++;
setTimeout(function() {
console.log(index);
}, 0);
}
{
let index = 0; index++; index++; index++;
setTimeout(function() {
console.log(index);
}, 0);
}
{
let index = 0; index++; index++; index++; index++;
setTimeout(function() {
console.log(index);
}, 0);
}