[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類的理解和使用