js擴(kuò)展
js的作用域
作用域是JavaScript最重要的概念之一痴施,想要學(xué)好JavaScript就需要理解JavaScript作用域和作用域鏈的工作原理。
任何程序設(shè)計(jì)語言都有作用域的概念椭坚,簡單的說,作用域就是變量與函數(shù)的可訪問范圍搏色,即作用域控制著變量與函數(shù)的可見性和生命周期善茎。在JavaScript中,變量的作用域有全局作用域和局部作用域兩種频轿。
1. 全局作用域(Global Scope)
在代碼中任何地方都能訪問到的對象擁有全局作用域垂涯,一般來說一下幾種情形擁有全局作用域:
(1)最外層函數(shù)和在最外層函數(shù)外面定義的變量擁有全局作用域
var name="yuan";
function foo(){
var age=23;
function inner(){
console.log(age);
}
inner();
}
console.log(name); // yuan
//console.log(age); // Uncaught ReferenceError: age is not defined
foo(); // 23
inner(); // Uncaught ReferenceError: inner is not defined
(2)所有末定義直接賦值的變量自動聲明為擁有全局作用域,例如:
var name="yuan";
function foo(){
age=23;
var sex="male"
}
foo();
console.log(age); // 23
console.log(sex); // sex is not defined
變量blog擁有全局作用域航邢,而sex在函數(shù)外部無法訪問到耕赘。
(3)所有window對象的屬性擁有全局作用域
一般情況下,window對象的內(nèi)置屬性都都擁有全局作用域翠忠,例如window.alert()鞠苟、window.location、window.top等等秽之。
2. 局部作用域(Local Scope)
和全局作用域相反当娱,局部作用域一般只在固定的代碼片段內(nèi)可訪問到,最常見的例如函數(shù)內(nèi)部考榨,所有在一些地方也會看到有人把這種作用域成為函數(shù)作用域.
如示例1中的age與inner都只有局部作用域跨细。(js中if、for沒有自己的作用域)
作用域鏈(Scope Chain)
在JavaScript中河质,函數(shù)也是對象冀惭,實(shí)際上,JavaScript里一切都是對象掀鹅。函數(shù)對象和其它對象一樣散休,擁有可以通過代碼訪問的屬性和一系列僅供JavaScript引擎訪問的內(nèi)部屬性。其中一個內(nèi)部屬性是[[Scope]]乐尊,由ECMA-262標(biāo)準(zhǔn)第三版定義戚丸,該內(nèi)部屬性包含了函數(shù)被創(chuàng)建的作用域中對象的集合,這個集合被稱為函數(shù)的作用域鏈扔嵌,它決定了哪些數(shù)據(jù)能被函數(shù)訪問限府。
示例演示
//-----**********************例1*********************************
var s=12;
function f(){
console.log(s);
var s=12; // if s=12
console.log(s)
}
f();
//-----**********************例2*********************************
var s=10;
function foo(){
console.log(s);
var s=5;
console.log(s);
function s(){console.log("ok")}// 函數(shù)的定于或聲明是在詞法分析時完成的,執(zhí)行時已不再有任何操作
console.log(s);
}
foo();
//-----***********************例3********************************
function bar(age) {
console.log(age);
var age = 99;
var sex= 'male';
console.log(age);
function age() {
alert(123)
};
console.log(age);
return 100;
}
result=bar(5);
//-----********************************************************
結(jié)果分析
我相信大家一定會有想不到的結(jié)果,接下來我們就以最復(fù)雜的例3來分析整個過程痢缎。
當(dāng)一個函數(shù)創(chuàng)建后胁勺,它的作用域鏈會被創(chuàng)建此函數(shù)的作用域中可訪問的數(shù)據(jù)對象填充。在函數(shù)bar創(chuàng)建時独旷,它的作用域鏈中會填入一個全局對象署穗,該全局對象包含了所有全局變量寥裂,如下圖所示:
解析到函數(shù)調(diào)用時,即bar(5)蛇捌,會生成一個active object的對象抚恒,該對象包含了函數(shù)的所有局部變量、命名參數(shù)络拌、參數(shù)集合以及this,然后此對象會被推入作用域鏈的前端回溺,當(dāng)運(yùn)行期上下文被銷毀春贸,活動對象也隨之銷毀。新的作用域鏈如下圖所示:
過程解析:
function bar(age) {
console.log(age);
var age = 99;
var sex="male";
console.log(age);
function age(){
alert(123);
} ;
console.log(age);
return 100;
}
result=bar(5);
一 詞法分析過程(涉及參數(shù)遗遵,局部變量聲明萍恕,函數(shù)聲明表達(dá)式):
1-1 、分析參數(shù)车要,有一個參數(shù)允粤,形成一個 AO.age=undefine;
1-2 、接收參數(shù) AO.age=5;
1-3 翼岁、分析變量聲明类垫,有一個 var age, 發(fā)現(xiàn) AO 上面有一個 AO.age ,則不做任何處理
1-4 琅坡、分析變量聲明悉患,有一個 var sex,形成一個 AO.sex=undefine;
1-5 榆俺、分析函數(shù)聲明售躁,有一個 function age(){} 聲明, 則把原有的 age 覆蓋成 AO.age=function(){};
二 執(zhí)行過程:
2-1 茴晋、執(zhí)行第一個 console.log(age) 時陪捷,當(dāng)前的 AO.age 是一個函數(shù),所以輸出的一個函數(shù)
2-2 诺擅、這句 var age=99; 是對不 AO.age 的屬性賦值市袖, AO.age=99 ,所以在第二個輸出的age是 99;
2-3 掀虎、同理第三個輸出的是 99, 因?yàn)橹虚g沒有改變 age 值的語句了凌盯。
注意:執(zhí)行階段:
function age(){
alert(123)
};
不進(jìn)行任何操作,將執(zhí)行語句復(fù)制給age這部操作是在詞法分析時烹玉,即運(yùn)行前完成的驰怎。