[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'上
~~~
- Javascript基础篇
- Array数组
- 数组插入值
- filter()
- forEach()
- push()
- pop()
- unshift()
- shift()
- valueOf()
- 面向对象思想
- Javascript 面向对象编程(一):封装
- Javascript面向对象编程(二):构造函数的继承
- Javascript面向对象编程(三):非构造函数的继承
- 解构
- 数组的解构赋值
- 对象的解构赋值
- 函数参数解构
- 字符串的解构赋值
- 数值和布尔值的解构赋值
- 圆括号问题
- 字符串.
- split()
- charAt()
- charCodeAt()
- concat()
- indexOf()
- lastIndexOf()
- match()
- replace()
- includes()
- 初识递归
- 渲染ul-li树形结构
- 异步函数解决方案
- 1. callback回调函数
- 2. ES6 - Promise
- JavaScript高级程序设计(书)
- 在html中使用JavaScript
- script标签的位置
- 延迟脚本
- 异步脚本
- <noscript>元素
- 基本概念
- 严格模式
- 变量详解
- 数据类型
- typeof操作符
- undefined类型
- Null类型
- Boolean类型
- Number类型
- 深入了解ES6(书)
- var 、let 、 const
- 字符串与正则表达式
- 字符串
- 正则表达式
- 函数
- 函数形参默认值
- 使用不具名参数
- 函数构造器的增强能力
- 扩展运算符
- name属性
- 明确函数的多重用途
- 块级函数
- 箭头函数
- 尾调用优化
- 扩展的对象功能
- 对象类别
- 对象字面量语法的扩展
- ES6对象新增方法
- 重复的对象属性
- 自有属性的枚举顺序
- 更强大的原型
- 解构:更方便的数据访问
- 为什么要用解构?
- 对象解构
- 数组解构
- 混合解构
- 参数解构
- Symbol与Symbol属性
- 创建Symbol
- Symbol的使用方法
- Symbol全局私有属性
- Symbol与类型强制转换
- Symbol属性检索
- Symbol的一些构造方法
- Set集合与Map集合
- Set集合
- Weak Set集合(弱引用Set集合)
- Map集合
- JS标准内置对象
- Object 构造函数及属性
- Object 构造方法
- Symbol 内建对象类的函数及属性
- Set 构造函数及属性
- Weak Set 构造函数及属性
- JS杂项
- 类数组对象
- Class类的理解和使用