這里說(shuō)的變量提升指的是使用var聲明的變量
變量提升也可以說(shuō)是“變量聲明前置”场航,就是把函數(shù)里面用到的變量聲明提升到前面切省,賦值步驟在書寫的位置進(jìn)行您旁。
是否使用var定義變量的區(qū)別
function foo () {
var a = 'var-a'
b = 'var-b'
}
foo()
console.log(b) // 'var-b'
console.log(a) // ReferenceError: a is not defined
- 使用var定義的變量是函數(shù)級(jí)變量,只在函數(shù)內(nèi)能訪問(wèn)到汉额。
- 不使用var定義的變量是全局變量曹仗,等同于
window.b = 'var-b'
- 變量提升先于代碼執(zhí)行
塊級(jí)作用域和函數(shù)級(jí)作用域
- 使用傳統(tǒng)的
var
定義的變量執(zhí)行的是函數(shù)級(jí)作用域。
描述一下就是:在函數(shù)中使用var
定義的變量都會(huì)將變量聲明放在函數(shù)的第一行蠕搜,變量賦值在書寫的位置賦值怎茫。
- 使用let定義變量將執(zhí)行塊級(jí)作用域,即
{}
內(nèi)范圍
function test () {
var varvars = 'varvars'
let letvars = 'letvars'
if(true){
var varvars = '11'
let letvars = '22'
}
console.log('test varvars:' + varvars)
console.log('test letvars:' + letvars)
}
test()
等同于:
function test () {
var varvars = undefined
let letvars = 'letvars'
varvars = 'varvars'
if(true){
varvars = '11'
let letvars = '22'
}
console.log('test varvars:' + varvars) // 1
console.log('test letvars:' + letvars) // letvars
}
test()
結(jié)合函數(shù)執(zhí)行過(guò)程說(shuō)明
當(dāng)程序進(jìn)入執(zhí)行的上下文(代碼真正執(zhí)行之前)妓灌,變量對(duì)象(VO)已經(jīng)包含的屬性是:
- 函數(shù)的所有形參
創(chuàng)建由變量名稱和對(duì)應(yīng)值組成的一個(gè)變量對(duì)象轨蛤,如果名稱沒(méi)有對(duì)應(yīng)的值時(shí),默認(rèn)值為undefined
- 所有函數(shù)聲明
創(chuàng)建由函數(shù)名和函數(shù)對(duì)象組成的一個(gè)變量對(duì)象虫埂,如果函數(shù)名已存在則完全替換
- 所有變量聲明(var聲明)
創(chuàng)建由名稱和對(duì)應(yīng)值組成的一個(gè)變量對(duì)象祥山,如果名稱沒(méi)有對(duì)應(yīng)的值時(shí),默認(rèn)值為undefined
掉伏,如果函數(shù)名已存在則完全替換
示例及優(yōu)先級(jí)說(shuō)明
function test (name) {
var name = undefined // 這里的定義將覆蓋形參name和函數(shù)name, 聲明提升缝呕,默認(rèn)為undefined
function name () { // 這里定義的name函數(shù)將覆蓋形參name
console.log('hello')
}
name = 'inner' // 這里的定義將覆蓋形參name和函數(shù)name
console.log(name) // 輸出 inner
}
test('outer')
或者:
function test (name) {
// 1. 形參: {name: 'outer'}
// 2. 函數(shù): {name: function () { // ... }} 覆蓋上面的同名定義
// 3. 變量: {name: 'inner'} 覆蓋上面的同名定義,多個(gè)var賦值取最后一次賦值
var name = 'inner' // 這里的定義將覆蓋形參name和函數(shù)name, 聲明提升斧散,這里的定義將覆蓋形參name和函數(shù)name
function name () { // 這里定義的name函數(shù)將覆蓋形參name
console.log('hello')
}
console.log(name) // 輸出 inner
}
test('outer')
注意:
- 不管var聲明放在何處供常,結(jié)果都是一樣的
- 多次var聲明同一個(gè)變量,代碼不會(huì)報(bào)錯(cuò)鸡捐,聲明前置栈暇,原地賦值
- 如果使用let聲明,則不可定義已存在的變量名(包括 函數(shù)名和形參)箍镜, 返回錯(cuò)誤
SyntaxError: Identifier 'name' has already been declared
總結(jié)
- var定義的變量為函數(shù)級(jí)作用域
- var定義的變量會(huì)有變量聲明提升的現(xiàn)象源祈,但是在書寫位置賦值
- let定義的變量為塊級(jí)作用域煎源,不會(huì)出現(xiàn)變量聲明提升的現(xiàn)象,在書寫的位置聲明及賦值香缺,變量名稱不能和 [形參名稱/函數(shù)名稱] 重復(fù)手销。
- 生效優(yōu)先級(jí):
變量名(var定義) > 函數(shù)名 > 形參名
- let不會(huì)出現(xiàn)同名的問(wèn)題,但是
函數(shù)名 > 形參名
的情況會(huì)存在