Js總的采取詞法作用域,也就是靜態(tài)作用域儡司。詞法作用域是作用域在詞法分析階段就確定了娱挨,不會改變,即變量的作用域是在定義的時候而不是執(zhí)行的時候確定的作用域捕犬。作用域最大的用處就是隔離變量跷坝,不同作用域下同名變量是不會有沖突的
詞法作用域分析1
var value = 1;
function foo() {
console.log(value);//1
}
function bar() {
var value = 2;
foo();
}
bar();
在執(zhí)行bar()的時候,當執(zhí)行到foo()函數(shù)的碉碉,當需要輸出value的時候柴钻,會先在他的內(nèi)部尋找是否有這個值,如果沒有就返回錯誤垢粮,尋找他的上一級作用域贴届,在此例中是全局的1,如果按照動態(tài)作用域分析的話,會輸出2.
全局作用域:在代碼的任何地方都能訪問到的對象擁有全局作用域毫蚓,全局作用域的變量是全局對象的屬性占键,不論在什么函數(shù)中都可以直接訪問
1 .最直接形式,最外層函數(shù)體聲明的變量
2 .window的所有屬性都具有全局作用域
3 .沒有使用var聲明的變量都具有全局作用域绍些,成為全局變量捞慌,所以聲明局部變量必須使用var
局部作用域:局部變量的優(yōu)先級高于全局變量(或者應(yīng)該叫做函數(shù)作用域)
1 .函數(shù)體內(nèi)使用var聲明的變量具有局部作用域,成為局部變量
2 .函數(shù)的參數(shù)也具有局部作用
var a=3; // 全局變量
function fn(b){ // 局部變量
c=2; // 全局變量
var d=5; // 局部變量
function subFn(){
var e=d; // **父函數(shù)的局部變量對子函數(shù)可見**
for(var i=0;i<3;i++){
console.write(i);
}
alert(i);// 3, 在for循環(huán)內(nèi)聲明柬批,循環(huán)外function內(nèi)仍然可見啸澡,沒有塊作用域 但是使用let聲明就不一樣了。
}
}
fn()
console.log(c); // 在function內(nèi)聲明但不帶var修飾氮帐,仍然是全局變量
塊級作用域
1 . let,const定義的在{}內(nèi)更加深入的一些變量
作用域鏈[[Scope]]內(nèi)部屬性嗅虏,該屬性包含了函數(shù)被創(chuàng)建的作用域中的對象的集合,這個集合被稱為函數(shù)的作用域鏈上沐,他決定了哪些數(shù)據(jù)能被函數(shù)訪問(服務(wù)于函數(shù),理解為一個數(shù)組皮服,而不是真的一條鏈)
1 .當一個函數(shù)創(chuàng)建之后,他實際上保存一個作用域鏈参咙,并且作用域鏈會被創(chuàng)建次函數(shù)的作用域中可訪問的數(shù)據(jù)對象填充龄广。
function fun03()
{
var a = 10;
return function(){
a*= 2 ;
return a ;
};
}
var f = fun03();
f();
var x = f();
console.log(x); //40
var g = fun03();
var y = g();
console.log(y); //20
1 .分析
var a;
function f(){
a=100;
}
f()
console.log(a)//100執(zhí)行函數(shù)過后,里面的覆蓋了外面的全局變量
var a;
function fun03()
{
var a = 10;
a=100
}
fun03()//100只是覆蓋了里面的局部的a的值蕴侧,外面的還是undefiend
console.log(a)//undefined
var a;
function fun03()
{
var a = 10;
return function(){
a*= 2 ;
return a;
};
}
var g = fun03();
var y = g();
console.log(y); //20 輸出的是整個函數(shù)的值择同,因為里面返回的是一個函數(shù),而且現(xiàn)在a的值仍然是undefiend.和上面的原理一樣净宵。
var f = fun03();
f();
var x = f();
console.log(x);//40上面的值被執(zhí)行了兩遍
function test(){
var a=b=c=d=e=0;
//除了直接接觸var的a敲才,剩下的都變成了全局變量
}
4 .參數(shù)和局部變量重名的時候,優(yōu)先級是相等的择葡。
var a=10;
function f(){
console.log(a)
var a=20;
}
f()//不傳參的時候為undefined
var a=10;
function f(a){
console.log(a)
var a=20;
}
f(a)//傳參以后10
后來
1 .在解釋的過程中紧武,js是嚴格按照作用域的機制來執(zhí)行,所以js解釋器只需要通過靜態(tài)分析就可以確定每個變量敏储,函數(shù)的作用域阻星。
2 .每個解釋器在執(zhí)行函數(shù)的時候,都會創(chuàng)建一個執(zhí)行環(huán)境已添,這個虛擬環(huán)境中先創(chuàng)建一個調(diào)用對象迫横,在這個調(diào)用對象中存儲著當前作用域中所有的局部變量,參數(shù)酝碳,嵌套函數(shù),外部作用域以及父級引用恨狈。
3 .js解釋器通過作用域鏈把多個嵌套函數(shù)作用域連接在一起疏哗,并借助這個鏈幫助了解釋器檢索變量的值,這個作用域鏈相當于一個索引表禾怠,并通過編號來存儲嵌套關(guān)系返奉。
作用域的優(yōu)先級
1 .局部變量優(yōu)先于全局變量贝搁。
2 .如果函數(shù)體內(nèi)存在同名的參數(shù)函數(shù)和局部參數(shù),函數(shù)參數(shù)優(yōu)先
3 .函數(shù)體內(nèi)有局部變量和全局變量芽偏,那么局部變量優(yōu)先雷逆。
4 .內(nèi)層函數(shù)可以訪問外層函數(shù)的局部變量,而外層函數(shù)卻不能訪問內(nèi)層函數(shù)的變量污尉,這就是變量作用域鏈膀哲。
5 .閉包函數(shù)根據(jù)定義作用域來確定自己包含變量的作用域和作用域鏈
6 .變量是對象的屬性,全局變量是window對象的屬性被碗,局部變量是調(diào)用對象的變量某宪。
管理作用域
1 .js引擎會在頁面加載后創(chuàng)建一個全局的執(zhí)行上下文,然后每執(zhí)行一個函數(shù)都會創(chuàng)建一個對應(yīng)的執(zhí)行上下文锐朴,最終機那里一個執(zhí)行上下文棧兴喂,當前起作用的執(zhí)行上下文在堆棧的最底部
2 .每個執(zhí)行上下文都有一個與之相關(guān)聯(lián)的作用域鏈,用于解析標識符焚志。作用域鏈包含一個或者多個變量對象衣迷,這些對象定義執(zhí)行了上下文作用域內(nèi)的標識符
3 .全局執(zhí)行上下文的作用域鏈中只有一個變量對象,他定義了js中所有可用的全局變量和函數(shù)酱酬。當函數(shù)被創(chuàng)建的時候壶谒,js引擎會把創(chuàng)建時執(zhí)行上下文的作用域鏈賦給函數(shù)的內(nèi)部屬性[[scope]],這個內(nèi)部屬性無法內(nèi)直接訪問。當函數(shù)執(zhí)行的時候岳悟,js引擎會創(chuàng)建一個活動對象佃迄,并在初始化的時候給this,argumets,命名參數(shù)贵少,和該函數(shù)的所有局部變量賦值呵俏。活動對象出現(xiàn)在執(zhí)行上下文作用域鏈的頂端滔灶,緊接著后面的是函數(shù)[[scope]]屬性中的對象普碎。
4 .在執(zhí)行代碼的時候,js引擎會通過搜索執(zhí)行上下文的作用域鏈來解析諸如變量和函數(shù)名這樣的標識符录平,解析標識符的過程從作用域鏈頂端開始麻车,自上而下的開始進行。
5 .標識符在作用域鏈中的位置越深斗这,查找和訪問他所需的時間就越長动猬,如果作用域管理不當,就會給腳本執(zhí)行帶來負面影響
6 .不過以上原則對于V8不是很適用表箭,因為他的優(yōu)化早已對這個無感了
7 .使用局部變量:局部變量是js中讀取最快的標識符赁咙。因為他們存在于執(zhí)行函數(shù)活動的對象中,解析符只需要查找作用域鏈中的單個對象。
8 .增長作用域鏈:with和try-catch會增長作用鏈彼水,所以不要在catch中執(zhí)行過多的代碼崔拥,來將其帶來的性能影響降到最小
高效的數(shù)據(jù)存取
1 .數(shù)據(jù)在腳本中存儲的位置直接影響腳本執(zhí)行的總耗時。
2 .字面量讀取值和局部變量讀取值的開銷差異十分的小凤覆,可以忽略不計
3 .真正的差異是在于從數(shù)組或者對象中讀取數(shù)據(jù)链瓦,一個需要索引,一個需要屬性值來查詢數(shù)據(jù)存儲的位置盯桦。
4 .始終將那些需要頻繁存取的值存儲到局部變量中慈俯。在數(shù)據(jù)村塾時,將函數(shù)中使用超過一次的兌現(xiàn)該屬性或數(shù)組元素存儲為局部變量
5 .在處理HTMLCollection對象這樣的dom方法的返回值俺附,使用局部變量特別重要肥卡,實際上每次存取這個對象,都是在對DOM文檔進行動態(tài)查詢事镣。