Shyno
Don't be shy,no problem!

变量提升

变量提升是一种现象,即"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]

 

posted on 2021-09-27 17:37  Shyno  阅读(154)  评论(0编辑  收藏  举报