主要知识点有:var变量提高、let声明、const声明、let和const的比较、块级绑定的应用场景
![]()
JavaScript中,咱们一般说的做用域是函数做用域,使用var声明的变量,不管是在代码的哪一个地方声明的,都会提高到当前做用域的最顶部,这种行为叫作变量提高(Hoisting)segmentfault
function test() { var a //a声明没有赋值 console.log('1: ', a) //undefined if (false) { a = 1 } //a声明没有赋值 console.log('3: ', a) //undefined }
test()闭包
若是a没有声明,那么就会报错,没有声明和声明后没有赋值是不同的,这点必定要区分开,有助于咱们找bug。函数
//b提高到函数a顶部,但不会提高到函数test。 function test() { function a() { if (false) { var b = 2 } } console.log('b: ', b) } test() //b is not defined
let和const都可以声明块级做用域,用法和var是相似的,let的特色是不会变量提高,而是被锁在当前块中。spa
function test() { if(true) { console.log(a)//TDZ,俗称临时死区,用来描述变量不提高的现象 let a = 1 } } test() // a is not defined function test() { if(true) { let a = 1 } console.log(a) } test() // a is not defined
惟一正确的使用方法:先声明,再访问。code
function test() { if(true) { let a = 1 console.log(a) } } test() // 1
声明常量,一旦声明,不可更改,并且常量必须初始化赋值。对象
const type = "ACTION"
从新赋值会报错blog
const type = "ACTION" type = 1 console.log(type) //"type" is read-only const type = "ACTION" let type = 1 console.log(type) //Duplicate declaration "type"
const虽然是常量,不容许修改默认赋值,但若是定义的是对象Object,那么能够修改对象内部的属性值。图片
const type = { a: 1 } type.a = 2 //没有直接修改type的值,而是修改type.a的属性值,这是容许的。 console.log(type) // {a: 2}
const声明不容许修改绑定,但容许修改绑定的值ip
const和let的异同点:
相同点:const和let都是在当前块内有效,执行到块外会被销毁,也不存在变量提高(TDZ),不能重复声明。
不一样点:const不能再赋值,let声明的变量能够重复赋值。
临时死区的意思是在当前做用域的块内,在声明变量前的区域叫作临时死区。
if (true) { //这块区域是TDZ,访问a会报错 let a = 1 }
访问TDZ中的变量会触发运行时错误
但下面这种不会报错
console.log(typeof a) //undefined,由于此时a不在TDZ中 if (true) { //这块区域是TDZ,访问a会报错 let a = 1 }
除了上面提到的经常使用声明方式,咱们还能够在循环中使用,最出名的一道面试题:循环中定时器闭包的考题
在for循环中使用var声明的循环变量,会跳出循环体污染当前的函数。
for(var i = 0; i < 5; i++) { setTimeout(() => { console.log(i) //5, 5, 5, 5, 5 }, 0) } console.log(i) //5 i跳出循环体污染外部函数 //将var改为let以后 for(let i = 0; i < 5; i++) { setTimeout(() => { console.log(i) // 0,1,2,3,4 }, 0) } console.log(i)//i is not defined i没法污染外部函数
循环内const声明
在普通的for循环中使用const变量,因为const变量不可修改,所以会报错。而在for-in或者for-of循环中可使用const变量。
由于循环为每次迭代建立了一个新的变量绑定,而不是试图去修改已绑定的变量的值
let arr = [1,2,3,4]; for(const item of arr){ console.log(item); //输出1,2,3,4 }
用var做用域全局做用域时声明的全局变量会覆盖一个已经存在的全局属性:
var reg = "hello"; console.log(window.reg) //"hello"
而若是在全局做用域使用let或者const声明,当声明的变量自己就是全局属性,好比closed。会建立一个新的绑定,但不会添加全局属性;就是说,用let或const不能覆盖全局变量,只能遮蔽它;
window.closed = false let closed = true closed // true window.closed // false
在实际开发中,咱们选择使用var、let仍是const,取决于咱们的变量是否是须要更新,一般咱们但愿变量保证不被恶意修改,而使用大量的const,声明一个对象的时候,也推荐使用const,当你须要修改声明的变量值时,使用let,var能用的场景均可以使用let替代。