js補充之作用域
任何程序設計語言都有作用域的概念
簡單的說,作用域就是變量與函數的可訪問範圍,即作用域控制著變量與函數的可見性和生命周期。
在JavaScript中,變量的作用域有全局作用域和局部作用域兩種。
1. 全局作用域(Global Scope)
在代碼中任何地方都能訪問到的對象擁有全局作用域,一般來說一下幾種情形擁有全局作用域:
(1)最外層函數和在最外層函數外面定義的變量擁有全局作用域
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在函數外部無法訪問到。
(3)所有window對象的屬性擁有全局作用域
一般情況下,window對象的內置屬性都都擁有全局作用域,例如window.alert()、window.location、window.top等等。
2. 局部作用域(Local Scope)
和全局作用域相反,局部作用域一般只在固定的代碼片段內可訪問到,最常見的例如函數內部,所有在一些地方也會看到有人把這種作用域成為函數作用域.
如示例1中的age與inner都只有局部作用域。(js中if、for沒有自己的作用域)
作用域鏈(Scope Chain)
在JavaScript中,函數也是對象,實際上,JavaScript裏一切都是對象。函數對象和其它對象一樣,擁有可以通過代碼訪問的屬性和一系列僅供JavaScript引擎訪問的內部屬性。其中一個內部屬性是[[Scope]],由ECMA-262標準第三版定義,該內部屬性包含了函數被創建的作用域中對象的集合,這個集合被稱為函數的作用域鏈,它決定了哪些數據能被函數訪問。
示例演示
please have a try:
//-----**********************例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")}// 函數的定於或聲明是在詞法分析時完成的,執行時已不再有任何操作 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); //-----********************************************************
結果分析
我相信大家一定會有想不到的結果,接下來我們就以最復雜的例3來分析整個過程。
當一個函數創建後,它的作用域鏈會被創建此函數的作用域中可訪問的數據對象填充。在函數bar創建時,它的作用域鏈中會填入一個全局對象,該全局對象包含了所有全局變量,如下圖所示:
解析到函數調用時,即bar(5),會生成一個active object的對象,該對象包含了函數的所有局部變量、命名參數、參數集合以及this,然後此對象會被推入作用域鏈的前端,當運行期上下文被銷毀,活動對象也隨之銷毀。新的作用域鏈如下圖所示:
過程解析:
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); 一 詞法分析過程(涉及參數,局部變量聲明,函數聲明表達式): 1-1 、分析參數,有一個參數,形成一個 AO.age=undefine; 1-2 、接收參數 AO.age=5; 1-3 、分析變量聲明,有一個 var age, 發現 AO 上面有一個 AO.age ,則不做任何處理 1-4 、分析變量聲明,有一個 var sex,形成一個 AO.sex=undefine; 1-5 、分析函數聲明,有一個 function age(){} 聲明, 則把原有的 age 覆蓋成 AO.age=function(){}; 二 執行過程: 2-1 、執行第一個 console.log(age) 時,當前的 AO.age 是一個函數,所以輸出的一個函數 2-2 、這句 var age=99; 是對不 AO.age 的屬性賦值, AO.age=99 ,所以在第二個輸出的age是 99; 2-3 、同理第三個輸出的是 99, 因為中間沒有改變 age 值的語句了。 註意:執行階段: function age(){ alert(123) } ; 不進行任何操作,將執行語句復制給age這部操作是在詞法分析時,即運行前完成的。
js補充之作用域