变量提升
因为var不管写在哪里,都会被提升到作用域顶端。
var声明变量会导致变量提升。变量声明后赋值前都是undefined。
console.log(aa);//undefined
var aa = "11"
console.log(aa)//报错
let aa = "11"
函数变量提升
在函数内部的变量也存在变量提升。
function(){
console.log(aa)
var aa = "11";
}
等价于
function() {
var aa
console.log(aa)
aa = "11"
}
函数提升
fn()// fn is not function
// 变量式声明
var fn = function() {
console.log("11")
}
//函数式声明
fn();// 11
function fn() {
console.log("11")
}
变量式声明方法和声明变量一样fn=undefined, 函数式声明方法会将方法和其中的内容提升到作用域最前面。
为什么会有变量提升
javascript要经历编译和执行两个阶段,编译阶段会搜寻收集所有的声明,剩下的语句在执行阶段会逐句生效。所以就有了var声明的变体会首先被赋值undefined。
想了解变量提升首先要知道什么是作用域。作用域是函数和变量的可访问范围,作用域控制着变量和函数的范围和生命周期。
es6之前作用域分为两种,全局作用域 函数作用域。全局作用域作用域全局,其生命周期随着页面的生命周期,函数作用域作用域函数内部。函数被访问时访问。函数执行结束后函数内部定义的变量会被销毁。
变量提升的好处
性能优化;javascript在执行之前,首先会进行语法检查和预编译(这个操作只会执行一次),预编译的时候会收集所有的声明函数和声明变量,这样就不会每次执行前去重新解析变量和函数(变量和函数一旦声明就不会改变,多次解析影响性能)。
Js在预编译的时候会给统计声明的变量和函数,同时会压缩函数,去除注释及空白,使的每次执行时可以直接给函数分配栈空间。不需要重复执行。
同时也会提高容错行比如下面的代码就不会报错,且能正常 执行。
a=“1”;
var a;
console.log(a)// “1”;
变量提升的弊端
变量名冲突案例:
Var name = “javascript”
Function showName() {
console.log(name)
If(0) {
Var name = “abc"
}
}
showName()//undefined
为什么是undefined?
当有两个同名的变量声明在同一个作用域中执行最下面的变量,当变量在不同作用域中,变量执行当前作用域里的变量,所以上面打印的是函数中的变量name但因为函数中的name存在变量提升,所以name的值undefined。(如果是C语言的话这里输出的是全局变量)
变量未被销毁的案例
function foo() {
For(var i=0; i<5; i++){}
console.log(i);
}
Foo()// 5
Foo结束的时候i并没有被销毁,同时i的变量被提升,所以输出是i的最终结果5.
禁止变量提升
为什么禁止变量提升
避免项目中因为忘记声明的变量以undefined的形式存在,防止undefined带来的不可预知的问题。
es6中引入了let和const语法,let和const声明的变量只作用于块级作用域中。块级作用域内声明的变量不会影响块外面的变量。
js如何支持块级作用域
预编译阶段统计所有声明var声明的变量执行比变量环境,即为给var声明的变量赋值undefined。块级作用域声明的变量执行词法环境,词法环境下声明的变量没有默认值,且在赋值前调用回报错。(在函数作用域中块级作用域中声明的变量并不走词法环境)
词法环境回根据不同的块级作用域生成对应栈,块级作用域就是通过词法环境的栈来实现的,变量提升是变量环境实现的。所以通过词法环境的栈和环境变量混合使用就同时支持了var声明和块级作用域。
暂时性死区
使用let或const声明变量后变量会在块级作用域中被声明,到变量被赋值之前,这段时间叫暂时性死区,在变量属于暂时性死区是调用变量,就回报错。这时候的typeOf是不准确的。