JS进阶作用域声明提升

声明提升(hoisting)分为变量声明提升和函数声明提升。声明从它们在代码中出现的位置被“移动”到最上面的过程,被称作声明提升。每个作用域都会有声明提升。

在介绍作用域的内部原理时有提过,引擎在解释JS代码前首先进行编译,编译过程中会找到所有的声明,并用合适的作用域将它们关联起来。

var a = 1;说明,这行代码包含了两步操作:var aa = 1。定义声明是在编译阶段进行的,赋值操作是在执行阶段进行的。

// 示例1
var a = 1;
var a;
console.log(a) // 1;

// 示例2
console.log(a) // undefined
var a = 1;
//等价于
var a;
console.log(a);
a = 1;

上面两个示例的奇怪行为,如果从声明提升的角度去看就很好理解了。

示例1声明了两次变量a,它们都在编译阶段完成声明,JS允许声明同名变量,到了执行代码时,对a进行赋值操作,执行到第3不自然打印结果就是1。

示例2也是同理,声明完成后a的值默认是undefined,到了执行阶段,赋值操作是在打印操作前完成的,所以结果就是undefined。

注意: 每个作用域都会进行声明提升。

console.log(a);
var a = 1;
function foo() {
  console.log(b);
  var b = 2;
  function foo2() {
   console.log(c);
   var c = 3;
  }
  foo2();
}
foo(); // undefined undefined undefined

声明提升后是下面这样

var a;
console.log(a);
a = 1;
function foo() {
  var b;
  console.log(b);
  b = 2;
  function foo2() {
   var c;
   console.log(c);
   c = 3;
  }
  foo2();
}
foo(); // undefined undefined undefined

函数声明提升

// 提升前
foo();
function foo(){
  console.log(1);//1
}

// 提升后
function foo(){
  console.log(1);// 1
}
foo();

函数的调用在函数声明前也可以正常执行,就是因为函数声明提升的原因。

函数声明可以提升,但是函数表达式不会提升。

foo();
var foo = function() {
  console.log(1); // foo is not a function
}

// 等价于
var foo;
foo();
foo = function() {
  console.log(1); // foo is not a function
}

函数覆盖

既然函数声明和变量声明都会提升,如果出现了同名的函数或变量,谁的优先级更高呢?以下是我个人总结的一套简单的记忆规则,仅供参考。

把函数的权重默认为1,只声明未赋值的变量权重为1,声明且赋值的变量权重为2。高权重覆盖低权重,同权重后者覆盖前者。

// 测试1 权重相等,后者覆盖前者
var a;
function a() {};
console.log(a); function a(){}'

// 测试2 变量权重大于函数,输出变量值
var a = 1;
function a(){}
console.log(a);//1

// 测试3 第一行权重大于第二行,输出1
var a = 1;
var a;
console.log(a);//1

// 测试4 权重相等,后者覆盖前者
a();//2
function a(){
  console.log(1);
}
function a(){
  console.log(2);
}

养成良好的开发习惯,避免在同一作用域中定义同名的变量或函数。

优秀文章首发于聚享小站,欢迎关注!
原文地址:https://www.cnblogs.com/yesyes/p/15351949.html