变量提升
变量提升是一种现象,即"js是从上往下执行的"和实际结果有所出入.
console.log('b',b) var b =1 //undefined
这里有两个问题.
1.未声明的变量为啥能使用.
2.为啥补声明的变量值为undefined
首先,我们要明白"var b = 1",实际上是好几行代码
var b b =undefined //系统补充,所有变量用命令声明时会被赋予一个默认值undefined b=1
然后,是提升的机制.js代码在被执行之前会被解析,而js代码的解析是会分为"执行上下文"和"可执行代码".而执行上下文又分为"变量环境"和"词法环境".
而变量的声明和可执行代码分开解析,并且为确保代码执行的正确性.变量的声明会被优先解析.也就是说,就是你再js的最后一行声明了一个变量,实际上它的解析过程也是优先于其他可执行代码的.所以上面的代码实际执行顺序是
/**执行上下文-start */ var b b=undefined//系统默认值 /**执行上下文-end */ /***可执行代码-start */ console.log('b',b)//undefined b =1 /***可执行代码-end */
相同变量声明前者会被后者覆盖(对结果无影响),但是赋值语法是可执行代码,并不会提升(容易误导).
因为解析的优先级问题,导致我们直观的感受就是声明变量的语法会被提升到"变量所在作用域的顶端".如下
a console.log('a',a) var b =1 var b =function(){ var a = 2 console.log('ab',a,b) //每个函数作用域有自己的执行上下文也可用全局的,当前作用域没找到,就去找全局的 } var a = 3 b() console.log('a',a ,b)
大概可以理解为下面的顺序
var b var b var a a console.log('a',a) //undefined b=1 b = function(){ var a a=2 console.log('ab',a,b) //2 function(){} } a=3 b() console.log('a',a ,b) //3 function(){}
隐式声明不会提升
console.log('a',a) //a is not defined a=1
块级作用域
let和const也是声明命令,var声明的变量作用域是全局变量和函数作用域,而let声明的作用域是块级作用域.
那块级作用域有什么特点?
1.变量名唯一
2.先声明后使用(let和const声明本身可能就是可执行代码)
首先,变量名提升会导致"不允许重复声明",这并不是let声明的变量和let声明的变量冲突,而是let声明变量时变量名不能是已存在的
var a =1 let b =3 console.log('a',a) let a =2 //报错 let b =4 //报错 console.log('a',a)
所以,let和const的声明,在声明变量前会查询一遍已存在的变量名,如果和即将要声明的变量一样,就报错
其次,因为let和const是可执行代码,所以它们并不会被解析到变量环境中.也就是说,它们并不会"变量提升".和其他可执行代码就是"谁在前面谁执行"
console.log('a',a)//undefined console.log('b',b) //报错 var a =1 let b =2
接下来就是所谓的"暂时性锁区",假如现在有个函数,我在内部想用let声明一个变量a,但是全局变量也有个a,咋办?我们知道在函数变量中,函数变量名是可以覆盖掉全局变量名的.这块儿,块级作用域也是
var a =1 var b =function(){ let a =2 console.log('a',a) //2 } b()
那么,假如我在let 前打印a呢?这个a是全局变量吗?
var a =1 var b =function(){ console.log('a',a) //Cannot access 'a' before initialization let a =2 } b()
我们可以发现报错了,难道是块级作用域拿不到全局变量吗?并不是
var a =1 var c =2 var b =function(){ console.log('c',c) //2 console.log('a',a) //Cannot access 'a' before initialization let a =2 } b()
是在函数作用域中,如果有let或者声明变量,它会把这个函数作用域中的同名的全局变量给屏蔽掉(变量名唯一),同时不能在声明前使用变量.所以会出现报错.
所以线上很多文章总结的块级作用域的特点为三点
1.变量名唯一 2.变量不提示 3.暂时性锁区
另外const的规则和let基本一样,除了一点.const声明的变量没有"setting"方法,或者setting方法受到了拦截.简而言之就是,const声明的变量本身不能通过直接赋值的方式去修改.
let a = {name:'小红'} const b ={name:'小红'} a ={name:'小明'} b={name:'小蓝'} //Assignment to constant variable
不过不能通过直接赋值修改,不代表完全不能修改.毕竟还可以用Object或者Array等原型上的方法去修改对应的值
const b ="123" console.log(b.slice(0,1)) //'1' const c ={name:'123'} c.name='345' console.log(c) //{name:'345'} const d = ['1'] d.push('2') console.log(d) //['1','2]