您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“js作用域及作用域鏈工作引擎怎么應用”,內容詳細,步驟清晰,細節處理妥當,希望這篇“js作用域及作用域鏈工作引擎怎么應用”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
作用域的定義:作用域是在運行時代碼中的某些特定部分中變量,函數和對象的可訪問性。
全局作用域
var name="global"; function foo(){ console.log(name); } foo();//global
這里函數foo()內部并沒有聲明name變量,但是依然打印了name的值,說明函數內部可以訪問到全局作用域,讀取name變量。再來一個例子:
hobby='music'; function foo(){ hobby='book'; console.log(hobby); } foo();//book
這里全局作用域和函數foo()內部都沒有聲明hobby這個變量,為什么不會報錯呢?這是因為hobby='music';
寫在了全局作用域,就算沒有var,let,const的聲明,也會被掛在window對象上,所以函數foo()不僅可以讀取,還可以修改值。也就是說hobby='music';
等價于window.hobby='music';
。
函數體作用域
函數體的作用域是通過隱藏內部實現的。換句話說,就是我們常說的,內層作用域可以訪問外層作用域,但是外層作用域不能訪問內層。原因,說到作用域鏈的時候就迎刃而解了。
function foo(){ var age=19; console.log(age); } console.log(age);//ReferenceError:age is not defined
很明顯,全局作用域下并沒有age變量,但是函數foo()內部有,但是外部訪問不到,自然而然就會報錯了,而函數foo()沒有調用,也就不會執行。
塊級作用域
塊級作用域更是見怪不怪,像我們接觸的let作用域,代碼塊{},for循環用let時的作用域,if,while,switch等等。然而,更深刻理解塊級作用域的前提是,我們需要先認識認識這幾個名詞:
--標識符:能在作用域生效的變量。函數的參數,變量,函數名。需要格外注意的是:函數體內部的標識符外部訪問不到
。
--函數聲明:function 函數名(){}
--函數表達式: var 函數名=function(){}
--自執行函數: (function 函數名(){})();自執行函數前面的語句必須有分號
,通常用于隱藏作用域。
接下來我們就用一個例子,一口氣展示完吧
function foo(sex){ console.log(sex); } var f=function(){ console.log('hello'); } var height=180; ( function fn(){ console.log(height); } )(); foo('female'); //依次打印: //180 //female //hello
分析一下:標識符:foo,sex,height,fn;函數聲明:function foo(sex){};函數表達式:var f=function(){};自執行函數:(function fn(){})();需要注意,自執行函數fn()前面的var height=180;
語句,分號不能拋棄
。否則,你可以試一下。
說好只是作用域和作用域鏈的,但是考慮到理解作用域鏈的必要性,這里還是先聊聊預編譯吧。先討論預編譯在不同環境發生的情況下,是如何進行預編譯的。
發生在代碼執行之前
(1)聲明提升
console.log(b); var b=123;//undefined
這里打印undefined,這不是報錯,與Refference:b is not defined不同。這是代碼執行之前,預編譯的結果,等同于以下代碼:
var b;//聲明提升 console.log(b);//undefined b=123;
(2)函數聲明整體提升
test();//hello123 調用函數前并沒有聲明,但是任然打印,是因為函數聲明整體提升了 function test(){ var a=123; console.log('hello'+a); }
2.發生在函數執行之前
理解這個只需要掌握四部曲
:
(1)創建一個AO(Activation Object)
(2)找形參和變量聲明,然后將形參和變量聲明作為AO的屬性名,屬性值為undefined
(3)將實參和形參統一
(4)在函數體內找函數聲明,將函數名作為AO對象的屬性名,屬性值予函數體 那么接下來就放大招了:
var global='window'; function foo(name,sex){ console.log(name); function name(){}; console.log(name); var nums=123; function nums(){}; console.log(nums); var fn=function(){}; console.log(fn); } foo('html');
這里的結果是什么呢?分析如下:
//從上到下 //1、創建一個AO(Activation Object) AO:{ //2、找形參和變量聲明,然后將形參和變量聲明作為AO的屬性名,屬性值為undefined name:undefined, sex:undefined, nums=undefined, fn:undefined, //3、將實參和形參統一 name:html, sex:undefined, nums=123, fn:function(){}, //4、在函數體內找函數聲明,將函數名作為AO對象的屬性名,屬性值予函數體 name:function(){}, sex:undefined, fn:function(){}, nums:123//這里不僅存在nums變量聲明,也存在nums函數聲明,但是取前者的值 以上步驟得到的值,會按照后面步驟得到的值覆蓋前面步驟得到的值 } //依次打印 //[Function: name] //[Function: name] //123 //[Function: fn]
3.發生在全局(內層作用域可以訪問外層作用域)
同發生在函數執行前一樣,發生在全局的預編譯也有自己的三部曲:
(1)創建GO(Global Object)對象
(2)找全局變量聲明,將變量聲明作為GO的屬性名,屬性值為undefined
(3)在全局找函數聲明,將函數名作為GO對象的屬性名,屬性值賦予函數體
舉個栗子:
var global='window'; function foo(a){ console.log(a); console.log(global); var b; } var fn=function(){}; console.log(fn); foo(123); console.log(b);
這個例子比較簡單,一樣的步驟和思路,就不在贅述分析了,相信你已經會了。打印結果依次是:
[Function: fn] 123 window ReferenceError: b is not defined
好啦,進入正軌,我們接著說作用域鏈。
作用域鏈就可以幫我們找到,為什么內層可以訪問到外層,而外層訪問不到內層?但是同樣的,在認識作用域鏈之前,我們需要見識見識一些更加晦澀抽象的名詞。
執行期上下文:當函數執行的時候,會創建一個稱為執行期上下文的對象(AO對象),一個執行期上下文定義了一個函數執行時的環境。 函數每次執行時,對應的執行上下文都是獨一無二的,所以多次調用一個函數會導致創建多個執行期上下文,當函數執行完畢,它所產生的執行期上下文會被銷毀。
查找變量:從作用域鏈的頂端依次往下查找。
[[scope]]
:作用域屬性,也稱為隱式屬性,僅支持引擎自己訪問。函數作用域,是不可訪問的,其中存儲了運行期上下文的結合。
我們先看一眼函數的自帶屬性:
function test(){//函數被創建的那一刻,就攜帶name,prototype屬性 console.log(123); } console.log(test.name);//test console.log(test.prototype);//{} 原型 // console.log(test[[scope]]);訪問不到,作用域屬性,也稱為隱式屬性 // test() --->AO:{}執行完畢會回收 // test() --->AO:{}執行完畢會回收
接下來看看作用域鏈怎么實現的:
var global='window'; function foo(){ function fn(){ var fn=222; } var foo=111; console.log(foo); } foo();
分析:
GO:{ foo:function(){} } fooAO:{ foo:111, fn:function(){} } fnAO:{ fn:222 } // foo定義時 foo.[[scope]]---->0:GO{} // foo執行時 foo.[[scope]]---->0:AO{} 1:GO{} 后訪問的在前面 //fn定義時 fn.[[scope]]---->0:fnAO{} 1:fooAO{} 2:GO{} fnAO:fn的AO對象;fooAO:foo的AO對象
讀到這里,這篇“js作用域及作用域鏈工作引擎怎么應用”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。