javascript---作用域和作用域链

176 阅读3分钟

作用域和作用域链

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);
}