💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] >[success] # var 、let 、 const ~~~ 1. js有一个特性,'代码块'里面可以读取'代码块'外面的'全局变量' 2. '代码块'外读取不了'代码块里'面的变量值,除了'变量提升',即使是变量提升也是获取到的'undefined' 'var' 和 'let' 为变量'const'为常量 ~~~ <br/> >[success] ## var ~~~ 下面'案例1',在'else'中或者'判断外面区域'也可以读取到'value'这个变量,因为'变量提升'的原因, 即使'if'未开始判断也会定义这个'value'的变量 ~~~ 案例1 ~~~ function getValue(condition) { // value 在此处可访问,值为 undefined if (condition){ var value = "blue" } else { // value 在此处可访问,值为 undefined } } 上面的代码和下面同理: function getValue(condition) { var value // value 在此处可访问,值为 undefined if (condition){ value = "blue" } else { // value 在此处可访问,值为 undefined } } ~~~ ~~~ 'value'变量的声明被提升到了顶部,而'初始化'工作则保留在原处。这意味着在 'else '分支 内'value'变量也是可访问的,此处它的值会是'undefined',因为它并没有被初始化。 ~~~ <br/> >[success] ## let ~~~ 'let'基本上可以像'var'一样使用,它属于'块级作用域',不存在'变量提升',不会像'var'那样,'if'未开始, 就已经定义了'value'的变量,'let'只有在'if'判断为'true'时候才会'定义并且初始化value'这个变量 ~~~ ~~~ function getValue(condition) { // value 在此处不可访问 if (condition){ let value = "blue" } else { // value 在此处不可访问 } } ~~~ <br/> >[success] ## const ~~~ 1. 常量'const'声明与'let'声明一样,都是'块级'声明。这意味着'常量'在声明它们的'语句块外部'是无法访问的,并且'const'定义初始化后也'不会被提升' 2. 'const常量'不可以像'let变量'一样'改变值',并且'常量在定义只会必须初始化'不然会报错 3. 如果'常量的值是个对象','常量'是可以修改的, 但是修改时候'不可以'整个值'全都覆盖修改',只能'修改'对象的某个'值','只能改值,不可改址' ~~~ ~~~ // 正确的常量 const maxItems = 30; // 语法错误:未进行初始化 const name // 错误,因为常量不可以修改 maxItems = 40 // 变量不存在提升 if (condition) { const maxItems = 5 } // maxItems 在此处无法访问 // 变量的值为对象的情况 const a = [1,2,3,4,5,6] a.push(7) console.log(a) // 正常运行 // 修改整个常量对象 const a = [1,2,3,4,5,6] a = [1,2] console.log(a) // 报错 ~~~ <br/> >[success] ## 禁止重复声明 ~~~ 1. 'var'在同一作用域内能'重复声明'一个'已有变量','let'和'const'一样,在'同一个作用域下声明同名变量'都会报错 2. 在'嵌套的作用域'内使用 'let' 声明一个'同名的新变量',不会抛出错误。 ~~~ ~~~ 同一'作用域'下声明'同名变量(常量)'的结果: ~~~ ~~~ // var 变量 var a = 40 var a = 50 console.log(a) // 50 // let变量 let a = 40 let a = 50 console.log(a) // 报错 // const常量 let a = '1' var b = '1' const a = '2' // 报错 const b = '2' // 报错 ~~~ ~~~ '嵌套作用域'中定义并声明'同名'的'变量(常量)'名称的结果: ~~~ ~~~ // var变量 var count = 30 // 不会抛出错误 if (1) { var count = 40 } // let变量 let count = 30 // 不会抛出错误 if (1) { let count = 40 } // const 常量 const a = 1 if(1){ const a = 2 } ~~~ ~~~ 此处的'变量(常量)'声明并没有抛出错误,这是因为它在'if'语句内部创建了一个新的 'count' 变 量,而不是在同一 级别再次创建此变量。在'if'代码块内部,这个新变量会屏蔽全局的'count'变量,'从而在局部阻止对于后者的访问'。 ~~~ <br/> >[success] ## 暂时性死区 ~~~ 'let'和'const'没有'变量提升',在'定义初始化变量(常量)'之前打印他的结果打印不出来, 但是使用了'typeof'后就会出现'类似'于'变量提升'的结果,如下: ~~~ ~~~ console.log(a) // 报错 const a = '123456789' ~~~ 使用`typeof`后`let`和`const`就可以出现`类似`于`变量提升的效果` ~~~ 如果在未定义初始化变量(常量)之前使用'typeof',他的值会为'undefined',大白话就是使用'typeof'时候, 'let'和'cosnt'也会出现类似变量提升的效果,如下: console.log(typeof a) // undefined if(0){ let a = '123456789' }else{ console.log(typeof a) // undefined } ~~~ <br/> >[success] ## 循环中的块级绑定 ~~~ 下面的两个案例中,'案例1'在代码块的外层也可以打印到'i'的'变量值'是因为变量'i'的'变量提升'了,所以 打印出了'i'的最后一次结果'10','案例2'的'let'就不会出现在代码块外层可以打印到'i'值 ~~~ 案例1 ~~~ for(var i = 0; i<10; i++){ console.log(i) // 0 1 2 3 4 5 6 7 8 9 } console.log(i) // 10 ~~~ 案例2 ~~~ for(let i = 0; i<10; i++){ console.log(i) // 0 1 2 3 4 5 6 7 8 9 } console.log(i) // Uncaught ReferenceError: i is not defined ~~~ <br/> >[success] ## 循环内的函数 ~~~ 下面'案例1'我想实现循环时候向'数组'中添加'10'个函数,每个函数依次打印结果为 '0,1,2,3,4,5,6,7,8,9',但是结果却是打印出'10'次结果都为'10',后来才知道是因为'for'中的 'var'变量,出现了'变量提升'在循环结束后,变量 'i' 的值会是 '10' ,因此当 'console.log(i)' 被调用 时, 每次都打印出 '10' ,如果想从'0-9'的打印出来用'ES5'的办法'闭包',也可以用下面的'ES6'的'let' ~~~ 案例1 ~~~ var arr = [] for(var i=0;i<10;i++){ arr.push(function(){ console.log(i) }) } arr.forEach(item => { item() // 数值10,打印10次 }) 上面代码同等于: var i = 0; var arr = [] for(i; i<10; i++){ arr.push(function(){ console.log(i) }) } arr.forEach(item => { item() // 数值10,打印10次 }) ~~~ `ES5闭包`来解决循环中使用函数(函数中使用变量i),函数打印结果不是自己预期结果的问题,如下: ~~~ var arr = [] for (var i = 0; i < 10; i++) { arr.push((function(value) { return function() { console.log(value) } }(i))) } arr.forEach(function(item) { item() // 从 0 到 9 依次输出 }) ~~~ 同样,不用闭包,`ES6`的`let`也可以实现打印`0-9`,如下: ~~~ var arr = [] for (let i = 0; i < 10; i++) { // 这里的let如果是var的话就会输出10次 10 arr.push(function(){ console.log(i) }) } arr.forEach(function(iten) { iten() // 从 0 到 9 依次输出 }) ~~~ ~~~ 实际上'for in'和'for of'循环时候往'数组'中'push'方法,并且方法中使用了'for in'循环时的'key'值, 也会出现调用方法时候打印的是循环的'对象'的'最后一个属性'。同理用'let'或者'闭包'也能解决 ~~~ <br/> >[success] ## 全局块级绑定 ~~~ 'let'和'const'和'var'在'全局作用域上'的表现不同,'var'在'全局作用域上定义初始化变量'时候,他会在 在'window'上创建一个属性,但是'let'和'const'在全局作用域下定义初始化是不能在'window'上创建'属性' ~~~ ~~~ // var var a = '测试' console.log(window.a) // 测试 // let let a = '测试' console.log(window.a) // undefined // const const a = '测试' console.log(window.a) // undefined ~~~ <br/> >[success] # 总结 <br/> | 名称 | 定义后是否可以不赋值 | 变量提升 | 同一作用域下是否可以同名 |不同作用域下是否可以同名 | 是否可修改 | 命名规则 |全局变量时的影响| | --- | --- | --- | --- | --- | --- | --- | --- | | var | 是 | 是 | 是 | 是 | 是 | 小驼峰 | 自动挂载到window | | let | 是 | 否 | 否 | 是 | 是 | 小驼峰 | 无 | | const | 否 | 否 | 否 | 是 | 值为Object类型时,可以改值,不可改址 | (TEST_NAME)所有字母大写,单词用下划线分割 | 无 <br/> 补充: ~~~ 1. 使用'let'和'const'声明定义了变量或常量,如果使用了'typeof',就可以打印出未定义的'let'或者'const' 2. 使用'for循环'时'var'和'let'的区别: 2.1 'var i=0';,这里的'i'是有变量提升的,在'for循环同级层'打印的是'i'循环总次数,并不是每次不同的'i'。 还有一种场景:'for循环'向'数组'中添加'n'个函数,并且'函数'中使用到'for循环'的'索引值(index)', 这种情况需要用'闭包'来保存这个'索引值(index)'才可以解决。 2.2 'let i=0;','let'不存在'变量提升',所以在外面打印不到这个'i','i'是在'for'的'代码块'中创建的 3. 如果想把'let、const'声明的'变量或者常量'挂载到'window'上,可以这样写: let num = 0 ; window.num = num; const NUM = 0; window.NUM = NUM; 这样写即可挂载到'window'上 ~~~