一 初步了解作用域
在了解作用域之前,先來(lái)看一個(gè)問題;
var xixi= 222;在js處理這句話的時(shí)候,會(huì)發(fā)生什么呢?
-
參與這句代碼處理過(guò)程的參與者
作用域參與.jpg -
代碼處理過(guò)程
過(guò)程.jpg -
作用域是什么呢
我們將作用域定義為一套規(guī)則,用來(lái)管理引擎是如何在當(dāng)前作用域以及嵌套的子作用域中根據(jù)標(biāo)識(shí)符名稱進(jìn)行變量查找.
-
作用域嵌套
當(dāng)一個(gè)函數(shù)嵌套在另一個(gè)函數(shù)中的時(shí)候,就發(fā)生了作用域的嵌套,因此,在當(dāng)前作用域中無(wú)法找到某個(gè)變量的時(shí)候,引擎就會(huì)在外層嵌套的作用域中繼續(xù)查找,直到找到該變量,或者到達(dá)最外層作用域.
-
將作用域鏈比作建筑的話:一樓就代表當(dāng)前的作用域,如果找不到變量,就會(huì)往上爬樓,依次尋找變量,直到最頂層仍找不到變量的話,就會(huì)報(bào)出錯(cuò)誤.
作用域鏈.jpg
二 函數(shù)作用域
-
定義
可以將內(nèi)部的變量和函數(shù)定義隱藏起來(lái),外部作用域無(wú)法訪問包裝函數(shù)內(nèi)部的任何內(nèi)容
如果外部有一個(gè)變量xi,但是在函數(shù)作用域中也想用這個(gè)名字該怎么辦呢?var xi = '外部'; function foo(){ var xi = '內(nèi)部'; console.log(xi) //'內(nèi)部' } foo() console.log(xi); //'外部'但是所定義的變量foo可能污染了它當(dāng)前所在的作用域,且執(zhí)行這個(gè)函數(shù),必須要再加一行代碼foo()來(lái)調(diào)用;優(yōu)化一下;
var xi = '外部'; (function foo(){ var xi = '內(nèi)部'; console.log(xi) //'內(nèi)部' })() console.log(xi); //'外部'如果我們想在內(nèi)部也訪問外部的相同變量,該怎么區(qū)分和訪問呢?
var xi = '外部';
(function foo(global){//這里可以將后面?zhèn)鱽?lái)的參數(shù)重新命名;
var xi = '內(nèi)部';
console.log(xi) //'內(nèi)部'
console.log(global.xi) //內(nèi)部
})(window) //這里只要把window對(duì)象作為一個(gè)參數(shù)傳給封裝函數(shù)就行了.
console.log(xi); //'外部'
**很多框架源碼都用了這個(gè)方式來(lái)解決諸如防止undefined被篡改等情況**
**函數(shù)是js中最常見的作用域單元**
###三 提升
直覺上,我們會(huì)認(rèn)為js在執(zhí)行代碼的時(shí)候是由上到下一步一步執(zhí)行的,但是實(shí)際上并不完全是這樣
我們先看看一段代碼
```java
a = 2;
var a;
console.log(a); //2
這句話的代碼等同于
var a;
a= 2;
console.log(2)
再來(lái)看一段代碼:
console.log(a);//undefined
var a = 2;
這段代碼等同于:
var a;
console.log(a);
a = 2;
在這段代碼里到底發(fā)生了什么呢?
-
編譯器
js的作用域基本上都是詞法作用域;什么是詞法作用域呢?就是定義在詞法階段的作用域,是由你在寫代碼的時(shí)候?qū)⒆兞亢妥饔糜驅(qū)懺谀膩?lái)決定的.大部分情況下詞法作用域是不變的.
我們之前提到過(guò),編譯器第一個(gè)工作就是把代碼解析成詞法單元,然后語(yǔ)法分析成語(yǔ)法樹;在這期間,編譯器基本能夠知道標(biāo)識(shí)符是在哪聲明以及是怎么聲明的.它會(huì)用合適的作用域?qū)⑺麄冴P(guān)聯(lián)起來(lái).

回到我們剛剛的代碼;
console.log(a);//undefined
var a = 2;
在編譯階段,會(huì)執(zhí)行var a;;剩余的a = 2會(huì)留在原地等待執(zhí)行;因此等同于
var a;
console.log(a);
a = 2;
在這個(gè)階段,變量聲明會(huì)從他們代碼所在的位置移動(dòng)到最頂層更,這個(gè)過(guò)程就叫做提升;
-
函數(shù)優(yōu)先
函數(shù)聲明和變量聲明都會(huì)被提升,但是,函數(shù)會(huì)首先被提升.然后才是變量;(值得注意的是,函數(shù)聲明提升的時(shí)候,函數(shù)體一同被提升;函數(shù)表達(dá)式的函數(shù)體則不提升;)
foo(); // 1
var foo;//盡管此變量聲明在function foo()之前,但是因?yàn)楹瘮?shù)優(yōu)先
//foo變量聲明就會(huì)變成重復(fù)聲明,忽略;
function foo(){
console.log(1);
}
foo = function(){
console.log(2);
}
這段代碼會(huì)被引擎理解為:
function foo(){
console.log(1)
}
foo();
foo = function(){
console.log(2)
}
雖然var重復(fù)聲明會(huì)被忽略,但是后面的函數(shù)聲明會(huì)覆蓋前面的
foo(); // 3
function foo(){
console.log(1);
}
var foo = function(){
console.log(2);
}
function foo(){ //覆蓋了之前的function foo();
console.log(3);
}


