JS -- 变量提升、词法解析、重复声明

266 阅读3分钟

当浏览器开辟出供代码执行的栈内存后,代码并没有自上而下立即执行,而是继续做了一些事情:

1、把当前作用域中所有带 var/function关键字的进行提前的声明和定义 => 变量提升机制

  • 带var的只是提前声明(declare)"var a;". 只声明,没有赋值,默认值为undefined

  • 带function的不仅声明,而且还定义了(defined)"a = 13;",定义其实就是赋值,准确来说就是让变量和某个值进行关联 2、词法解析/词法检测:检测当前即将要执行的代码是否会出现语法错误。如果出现错误,代码将不会执行

console.log(sum) //undefined,因为sum方法是用var声明的。
sum(10, 20) //报错
var sum = function (n , m){
    return n+ m
}

//let sum = (n, m) => n + m  //用箭头函数声明的,且是用let声明,不会变量提升
console.log(1)  //不会执行,let a重复声明,直接报错
let a = 12  // var a = 12 和 let a = 12 也属于重复声明,也会报错
console.log(a)
let a = 13
console.log(a)
console.log(1)  //会执行
console.log(a) //ReferenceError 引用错误,let没有变量提升,但这病不是语法错误,在词法解析阶段不会报错,执行到这个地方才会报错
let a = 12

带var和不带var的区别

  • 不带var的,相当于给全局对象window设置了一个属性

  • a = 13 => window.a = 13

使用let/const声明,浏览器会校验当前作用域中是否已经存在这个变量了,如果已经存在了,则再次基于let等重新声明就会直接报错。

在var和function同时声明某一个变量时,在变量提升阶段,以function声明的为准.(可以不重新声明,但赋值操作是一定要继续进行的)

a()   // 4
var a = 3      //在变量提升阶段,a第一次声明
function a(){  //在变量提升阶段,a已经声明,但未赋值,所以function给a赋值
    console.log(4)
}

b()   // 4
function b(){  //在变量提升阶段,b第一次声明,并给b进行了赋值。 
    console.log(4)
}
var b = 3    //在变量提升阶段,b已经声明,这行就不用再重新声明了
fn()    //5
function fn(){  //在变量提升阶段,fn第一次声明并赋值。执行阶段跳过。
    console.log(1)
}
fn()   //5
function fn(){  //在变量提升阶段,fn第二次赋值。执行阶段跳过。
    console.log(2)
}
fn()  //5
var fn = function(){  //在变量提升阶段,fn已经声明,这里就不用再重新声明了。 但是在执行阶段,这里会给fn重新赋值,也是整段代码中最后一次给fn赋值
    console.log(3)
}
fn()  //3,这里是最后赋值后得出的结果
function fn(){     //在变量提升阶段,fn第三次赋值。执行阶段跳过。
    console.log(4)
}
fn()  //3,这里是最后赋值后得出的结果
function fn(){    //在变量提升阶段,fn第四次赋值。执行阶段跳过。
    console.log(5)
}
fn()  //3,这里是最后赋值后得出的结果

变量提升中关于条件判断的处理: 不管条件十分成立都要进行变量提升。但是function声明的函数在条件判断中有特殊性:在老版本浏览器中,不论条件是否成立,函数会提前声明并赋值,但是再新版本浏览器中,为了兼容es6严谨的语法规范,条件中的函数在变量提升阶段只能提前声明,不会赋值。

console.log(a)  //undefined
if(!('a' in window)){  a是window的属性,'a' in window => true
    var a = 13  //这里会对a进行变量提升的操作
}
console.log(a)  //undefined
console.log(fn)  //undefined
fn()   //报错 TypeError: fn is not a function
if('fn' in window){
    funciton fn(){   //条件中只声明
        //条件成立,进来的第一件事是给fn赋值,然后再执行代码。(把if当作了块作用域)
        console.log('fn')
    }
}
fn()