哈哈。開(kāi)玩笑彻采。言歸正傳腐缤。我們先看下面代碼
console.log(a);
var a = 2;
輸出結(jié)果是但是實(shí)際上感覺(jué)是代碼是這樣的
var a;
console.log(a);
a = 2;
為什么會(huì)這樣呢?這里就需要知道編輯器在執(zhí)行程序所需要的做的事了特笋,編輯器在執(zhí)行程序的時(shí)候會(huì)先執(zhí)行編譯剃浇,那么編譯階段有一部分的工作是找到所在作用域中的所有的聲明,所以我們可以理解為,在js中任何代碼在執(zhí)行前虎囚,其(函數(shù)角塑,變量)聲明都會(huì)被先處理。因此淘讥,我們看到var a = 2;其實(shí)在編譯器中是var a和a = 2;兩部分圃伶。var a,是在編譯階段執(zhí)行的,a = 2在執(zhí)行階段執(zhí)行适揉。這個(gè)過(guò)程就好像變量以及函數(shù)聲明從他們?cè)械拇a處被移動(dòng)了,所以叫做提升煤惩。
上面說(shuō)的是變量嫉嘀,那么函數(shù)呢?是否也會(huì)提升呢魄揉?我們來(lái)看下下面的例子
sayName();
function sayName(){
console.log('來(lái)瓶二鍋頭');
}
將上面代碼放到控制臺(tái)剪侮,看下結(jié)果顯而易見(jiàn),我們的函數(shù)聲明也提升了洛退,實(shí)際上在執(zhí)行的時(shí)候代碼是這樣的
function sayName(){
console.log('來(lái)瓶二鍋頭');
}
sayName();
那么函數(shù)內(nèi)部的變量是否也會(huì)提升呢瓣俯?我們?cè)賮?lái)看下這個(gè)例子
function sayName(){
console.log(name);
var name = '來(lái)瓶二鍋頭';
}
sayName();
在控制臺(tái)我們看下結(jié)果可以看到我們輸出的是undefined,而不是referenceError.所以函數(shù)中的變量也得到了提升,實(shí)際的代碼是這樣的
function sayName(){
var name;
console.log(name);
name = '來(lái)瓶二鍋頭';
}
sayName();
所以前面我理解的結(jié)論 在js中任何代碼在執(zhí)行前兵怯,其(函數(shù)彩匕,變量)聲明都會(huì)被先處理是正確的。
此時(shí)我們?cè)卺槍?duì)函數(shù)來(lái)想下媒区,除了上面的函數(shù)聲明驼仪,我們?nèi)绻ㄟ^(guò)函數(shù)表達(dá)式是否會(huì)得到提升呢?看下下面的代碼
sayName();
var sayName = function (){
console.log('來(lái)瓶二鍋頭');
}
輸出到控制臺(tái)袜漩,看下結(jié)果此時(shí)報(bào)錯(cuò)不在referenceError而是TypeError绪爸。那么我們?cè)谠囅孪旅娴拇a看看
console.log(sayName);
var sayName = function (){
console.log('來(lái)瓶二鍋頭');
}
在控制臺(tái)執(zhí)行下看看結(jié)果至此大家應(yīng)該明白啥意思了吧。前面說(shuō)過(guò)了宙攻,我們會(huì)把聲明提前奠货,但是執(zhí)行的依舊在原位置,所以程序執(zhí)行前面的代碼的時(shí)候座掘,實(shí)際上是這樣的
var sayName;
sayName();
sayName = function (){
console.log('來(lái)瓶二鍋頭');
}
這個(gè)時(shí)候递惋,sayName只是被定義了,不是一個(gè)函數(shù)溢陪,所以執(zhí)行sayName()會(huì)報(bào)TypeError丹墨,而執(zhí)行console.log(sayName);也不會(huì)報(bào)錯(cuò)
那么我們?nèi)绻ㄟ^(guò)函數(shù)表達(dá)式加具名函數(shù)一起使用,會(huì)是什么樣子呢嬉愧?一起看下下面代碼
sayName();
a();
var sayName = function a(){
console.log('來(lái)瓶二鍋頭')
}
在控制臺(tái)執(zhí)行下贩挣,看下結(jié)果可以看到sayName是報(bào)TypeError,但是a()是報(bào)ReferenceError錯(cuò)誤。sayName上面說(shuō)過(guò)了,那a()是為什么呢王财?我們?cè)诳聪孪旅娴拇a
var sayName = function a(){
console.log(a);
console.log('來(lái)瓶二鍋頭')
}
sayName();
控制臺(tái)看下結(jié)果那么我們可以看出實(shí)際上程序在執(zhí)行的時(shí)候卵迂,代碼是下面這樣的
var sayName;
sayName = function(){
var a = 函數(shù)本身;
console.log(a);
console.log('來(lái)瓶二鍋頭')
}
sayName();
所以我們從另一個(gè)角度也能得出一個(gè)結(jié)論,那就是如果函數(shù)表達(dá)式是跟著具名函數(shù)绒净,那么具名函數(shù)實(shí)際是這個(gè)方法本身见咒。在其函數(shù)作用域內(nèi)生效。是不是很神奇挂疆。那么原理具體是什么呢改览?我們?cè)诤竺娴暮瘮?shù)篇來(lái)給出解答(挖坑,看下后面是否真的可以給填上缤言,哈哈)宝当。
我們來(lái)看下下面的例子
test()
var test;
function test() {
console.log('a');
}
test = function(){
console.log('b');
}
控制臺(tái)執(zhí)行結(jié)果為那么我們可以看出跌穗,在執(zhí)行過(guò)程中订晌,實(shí)際上為
function test() {
console.log('a');
}
var test;
test();
test = function(){
console.log('b');
}
所以我們可以得出結(jié)論,在變量以及函數(shù)相同的情況下函數(shù)優(yōu)先蚌吸。我們?cè)賮?lái)思考一個(gè)問(wèn)題锈拨,那就是變量實(shí)際上存在覆蓋的問(wèn)題,那么下面的代碼執(zhí)行的結(jié)果是啥呢?
test();
function test(){
console.log('a');
}
var test = function(){
console.log('b');
}
function test(){
console.log('c');
}
按照我們上面說(shuō)的編譯時(shí)候提升的問(wèn)題羹唠,代碼可以理解為
function test(){
console.log('a');
}
function test(){
console.log('c');
}
var test;
test();
test = function(){
console.log('b');
}
由于存在 重載推励,所以執(zhí)行結(jié)果為c。
我們?cè)賮?lái)設(shè)想一個(gè)場(chǎng)景肉迫,如果我們?cè)谄胀ǖ膲K級(jí)作用域中定義函數(shù)验辞,會(huì)怎么樣呢?看下下面的代碼
test1()
if(true){
function test1(){
console.log('1')
}
}
結(jié)果為為什么呢喊衫?我們都知道普通的塊級(jí)作用域會(huì)將變量提升到上級(jí)作用域中跌造,那么實(shí)際上代碼邏輯為
var test1;
test1();
if(true){
test1 = function(){
console.log('1')
}
}
所以你懂了嗎?至此關(guān)于提升的問(wèn)題結(jié)束了族购。接下來(lái)我們將看看變量的類型一系列問(wèn)題壳贪。