一、聲明提前
1、變量聲明提前——變量提升
可參照這里的描述:
#hello,JS:01數(shù)據(jù)類型、運算符、運算符優(yōu)先級
//先輸出a,再聲明
var a=3
console.log(a) //undefined
var a=3
再如:
console.log(a)//undefined
console.log(b) //報錯
var a=3
為什么console.log(a)輸出undefined,而console.log(b) 則會報錯呢?
出現(xiàn)這樣的情況是為什么?實際上JS引擎在一行行執(zhí)行代碼的時候,有一些默認(rèn)的運行我們并不知道,即:
var a //undefined,變量a已經(jīng)前置聲明,則結(jié)果為undefined console.log(a) //undefined
console.log(b) //報錯,沒有變量b,引用失敗
a=3
最后是:變量的聲明前置的完整運行:
var a //undefined,已經(jīng)前置聲明為undefined
console.log(a) //undefined
a=3
console.log(a)
--> undefined 3
2、函數(shù)的聲明前置
先看這段代碼:
sum(5,3) //放在任何的地方,但是只是一個函數(shù)值,并沒有打印這個函數(shù)出來
function sum(a,b){
return a+b
}
設(shè)置兩種看兩種執(zhí)行結(jié)果:
看代碼,不是沒有任何的變量聲明?為什么就可以使用函數(shù)輸出結(jié)果?這是因為函數(shù)所執(zhí)行的JS引擎默認(rèn)操作與變量聲明前置機制是類似。
相當(dāng)于:包含變量數(shù)據(jù)的function函數(shù)前置,即以上代碼等同于
function sum(a,b){
return a+b
}
sum(5,3)
//直接返回8
3、函數(shù)表達(dá)式的聲明前置
還是先看代碼:
fn()
var fn = function(){
console.log('fn...')
}
//報錯,直接說fn不是一個函數(shù)
這里,function函數(shù)是一個變量,相當(dāng)于把一個數(shù)字賦值給fn,而這個function函數(shù)表達(dá)式,事實上也是有一個聲明前置的,即:
var fn //undefined
fn() //此為函數(shù),會執(zhí)行,但是如果是undefined(),這種是不成立,即報錯
fn = function(){
console.log('fn...')
}
那么原始代碼是怎么執(zhí)行的?
原始代碼:
fn()
sum(3,4)
var fn = function(){
console.log('fn...')
}
function sum(a,b){
return a+b
}
對于瀏覽器來說它做了什么:一個聲明前置:包括變量聲明前置和函數(shù)聲明前置
var fn //變量聲明前置
function sum(a,b){
return a+b } //函數(shù)聲明前置
fn()
sum(3,4)
fn = function(){
console.log('fn...')
}
二、立刻執(zhí)行的函數(shù)表達(dá)式
注:關(guān)于js的語法規(guī)則如何體現(xiàn)?
1、先看下面這個代碼
(function(){ console.log('wangxiaoqin') })()
-->"wangxiaoqin"
先暫且不管它的結(jié)果如何產(chǎn)生。先了解一下JS的語法規(guī)則
按照這樣寫,為何只有function(){}單獨作為一個變量時,通過模仿語句a( );,function(){}();這個語句則會操作會報錯,這是為什么?
對于JS引擎來說不認(rèn)為是一個表達(dá)式,很像一個函數(shù)聲明,再加一個括號,即會報錯。那么如何讓這個語句正常賦值?直接將整個函數(shù)聲明加一個括號,即:
(function(){})()
由于作為運算符,括號和括號里的內(nèi)容組合為一個表達(dá)式。加上括號之后,會讓JS引擎認(rèn)為它是一個表達(dá)式(或引用類型),那么就符合了JS的語法規(guī)則。
總結(jié):
當(dāng)在一個函數(shù)聲明后加了圓括號(也是一種運算符)后運行的話,會報錯。因為這被認(rèn)為是語法錯誤。在JS中,以function開頭會被認(rèn)為是語句,而語句不應(yīng)該以圓括號結(jié)尾。所以此時可以選用的解決辦法是把整個語句用圓括號包起來。
2、那么剛才列舉的代碼:
(function(){
console.log('wangxiaoqin') })()
-->"wangxiaoqin" //即函數(shù)表達(dá)式,立刻去執(zhí)行它
//等同于
var fn = function(){
}
fn()
這類型的函數(shù)表達(dá)式有什么用?這里涉及了函數(shù)中所對應(yīng)的作用域的概念,假設(shè)我們在這類函數(shù)里添加一個變量
(function(){
var a =3 console.log('wangxiaoqin')
})()
console.log(a)
//運行,后臺報錯,a是沒有被定義的。
//因為a變量是不被看到的,因為a在function函數(shù)的這個作用域里,與外界無關(guān)
3、立刻執(zhí)行函數(shù)的好處:
var fn=function(){}
相當(dāng)于
fn()===(函數(shù)表達(dá)式)()
() 作為一種運算符,用這種局部作用域的方式將函數(shù)引用包裹起來,形成一個立即執(zhí)行的表達(dá)式,好處在于:
A、函數(shù)不必再另外命名,避免了污染全局,不會在復(fù)雜頁面協(xié)作中造成錯亂;
B、實現(xiàn)一個作用域隔離,封裝外部無法讀取的私有變量;
C、避免命名沖突,符合js語法規(guī)則,并立刻執(zhí)行。
三、命名沖突
當(dāng)在同一個作用域內(nèi)定義了名字相同的變量和方法的話,會根據(jù)前置順序產(chǎn)生覆蓋
var fn = 3;
function fn(){
}
console.log(fn);
// 3
相當(dāng)于
var fn function fn(){} //覆蓋上面的 fn = 3 //重新賦值 console.log(fn) //為函數(shù)
當(dāng)函數(shù)執(zhí)行有命名沖突的時候,可以認(rèn)為在還是內(nèi)部一開始有隱藏的聲明變量這個操作
function fn(fn){
console.log(fn);
var fn = 3;
console.log(fn);
}
fn(10) //10 3
//等同于有一個默認(rèn)的var fn = arguments[0]的操作
function fn(){
var fn = arguments[0] //1、將它先聲明前置,再賦值,再輸出
console.log(fn);
var fn = 3; //2、再賦值
console.log(fn);
} fn(10)
//10 3