花十分钟理解变量提升

155 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

大家好,我是小草。本菜鸟是来写基础文章的。仅供大家参考,欢迎指正。

科技发展或改变世界或毁灭世界

变量提升

理解变量提升前先了解引擎编译的四个步骤

js代码从编译到执行分为四个步骤

  • 词法分析
  • 语法分析
  • 预编译
  • 解释执行

词法分析

词法分析就是将我们写的代码块分解成词法单元;将字符组成的代码字符串分解成对程序有意义的代码块。这些代码块称为词法单元(token);例如:var a = 2;被分解成“var,a,=,2”。

语法分析

  1. 语法分析是将词法单元流转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树;这个树被称为抽象语法树(Abstract Syntax Tree,AST)。
  2. 检查代码是否有低级语法错误,如果有引擎会停止执行,并抛出异常。

预编译

预编译分为全局编译和函数预编译。全局预编译发生在页面加载完时执行,而函数预编译发生在函数执行的前一刻(几微秒甚至更少)执行。

全局预编译执行步骤
  1. 创建全局(global或window全局执行上下文)对象
  2. 寻找var变量声明,并赋值undefined
  3. 寻找函数声明,并赋值函数体
  4. 执行代码
函数预编译执行步骤
  1. 创建函数作用域的执行上下文对象
  2. 寻找函数的形参和var变量声明,将他们作为“函数执行上下文对象”的属性,并赋值undefined
  3. 将实参赋值给形参,即将形参的值undefined赋值为真实传入参数的值
  4. 寻找函数中的函数声明,如有同名变量,则覆盖。将值替换为函数体

理解完以上四个步骤后分析以下代码:

案例1

console.log(a); // undefined 
a=12;

上面代码不存在变量提升;会抛出作用域解析错误:ReferenceError: a is not defined

案例2

var a = 1;
function fn(a){
    console.log(a) // 1
    var a = 2;
}
fn(a);

可以根据上文:函数预编译执行步骤来分析,很多人会把第三步和第四步的顺序弄反了;上面代码执行到第三步将实参赋值,所以输出1 再看下面的代码:

var a = 1;
function fn(a){
    console.log(a) // [Function: a]
    var a = 2;
    function a(){}
}
fn(a);

上面代码执行到第四步,所以a被赋值了函数体

案例3

console.log(a); // undefined
var a=12; 
function fn(){
    console.log(a); // undefined
    var a=13;   
}
fn();   
console.log(a);// 12

上面代码,如果当前函数作用域有a的变量声明,那么就不会去找全局的变量声明

案例4

console.log(a); // undefined
var a=12;
function fn(){
    console.log(a); // 12
    a=13;
}
fn();
console.log(a);// 13jieyu 

上面代码函数体没有var 变量声明,所以找的全局的变量a

案例5

var foo='hello'; 
(function(foo){
   console.log(foo); // hello
   var foo=foo||'world';
   console.log(foo); // hello
})(foo);
console.log(foo);//hello

结语

码字不易,欢迎star,欢迎指正。

任何足够先进的技术都和魔法无异,而其背后都有不可撼动的基石。