首先我們知道JavaScript引擎包括一個呼叫堆疊和堆,呼叫堆疊是代碼實際執行的地方,使用執行背景關系(執行環境)來完成;堆是非結構化的記憶體池,存盤了應用程式所需要的所有物件,
執行背景關系是什么?
執行背景關系包括全域執行背景關系和執行背景關系,
全域執行背景關系:代碼編譯完成后進入呼叫堆疊執行首先創建全域執行背景關系(整個專案只有一個全域執行背景關系),是用來執行頂層代碼(函式除外,函式只在被呼叫的時候執行),
執行背景關系:執行一段JavaScript的環境,存盤了一些代碼執行所需要的必要資訊,比如傳遞給函式的區域變數或者引數,打個比方:我們點外賣,送來的袋子(執行背景關系)不只有外賣(JavaScript代碼),還有餐具(代碼執行所需要的必要資訊),
每一個函式呼叫就會創建執行背景關系來執行,
執行背景關系分為三個部分,依次為變數環境,作用域和this關鍵字:
變數環境VE
-
let,const and var變數
-
函式宣告
-
函式形參
作用域
作用域(scoping):由JavaScript引擎組織和訪問,控制我們程式的變數,
主要分為以下三個
-
全域作用域:在代碼中任何地方都能訪問到的物件擁有全域作用域,
-
函式作用域:只能在函式中訪問到的物件具有函式作用域,也稱區域作用域,
-
塊作用域:ES6新特性,類似于函式作用域,指的是大括號括起來的塊,比如if和for回圈,塊中的變數只能在塊中訪問,具有塊作用域,但只能用let和const,使用var仍能被全域訪問,
if (birthYear >= 1981 && birthYear <= 1996) { var millenial = true; const str = `oh,you're a millenial,${firstName}`; console.log(str); function add(a, b) { return a + b; } } console.log(str);//str不能被列印 console.log(add(1, 1));//add函式不能被列印 console.log(millenial);//var變數能列印,能被全域訪問
作用域鏈:若在當前作用域無法查找到需要變數,則通過作用域鏈來進行變數查找,子作用域查找使用父作用域的變數,
注:如果子作用域和父作用域存在相同的變數名,則直接查找子作用域的,無需進行變數查找,
this關鍵字
定義:為每個執行背景關系(函式)創建的特殊變數,取的值為該函式的呼叫者本身,具體取值包括以下四種方法 this為呼叫該方法的物件
-
方法 this指向呼叫方法的物件
const luki = { name : 'lukirence', year : 2002, calcAge:function(){ return 2037-this.year; } };如果在方法內的函式嵌套一個新的函式,該嵌套函式相當于常規函式,this關鍵字為undefined
const luki = { fullName: 'lukirence', year: 2002, calcAge: function () { console.log(this); console.log(2037 - this.year); const isMillenial = function(){ console.log(this);//undefined console.log(this.year >=1981); } isMillenial(); } };如何解決嵌套函式能夠使用this?
- 添加self變數=this(ES6之前的舊方法)
const luki = { fullName: 'lukirence', year: 2002, calcAge: function () { console.log(this); console.log(2037 - this.year); const self = this; const isMillenial = function(){ console.log(self); console.log(self.year >=1981); } isMillenial(); } };- 將嵌套函式改成箭頭函式
const luki = { fullName: 'lukirence', year: 2002, calcAge: function () { console.log(this); console.log(2037 - this.year); const isMillenial = ()=>{ console.log(this); console.log(this.year >=1981); } isMillenial(); } }; -
常規函式宣告 this為undefined
const calcAge = function (birthYear) { console.log(2037 - birthYear); console.log(this);//顯示undefined }; calcAge(1991); -
箭頭函式 箭頭函式沒有this關鍵字,箭頭函式的關鍵詞會通過查找父函式的關鍵詞,若沒有則為全域的關鍵詞,即指向全域視窗,
console.log(this);//指向全域視窗window const calcAgeArrow = birthYear => { console.log(2037 - birthYear); console.log(this);//指向全域視窗window }; calcAgeArrow(1991); const luki = { fullName: 'lukirence', year: 2002, calcAge: function () { console.log(this); console.log(2037 - this.year); }, greet: () => { console.log(`hi,${this.fullName}`); }, }; luki.greet();//顯示hi undefined,因為this指向window物件
?
- 事件監聽 this為事件處理器所添加的DOM元素
注:箭頭函式的執行背景關系不包括引數物件和this關鍵字
執行背景關系創建
在詳細了解了執行背景關系的內容后,我們來看一段代碼實際執行時執行背景關系如何在呼叫堆疊中活動的,
將以以下代碼為例,按每一步驟描述執行背景關系創建流程:
const name = 'luki';//--------------------------------1
const first =() =>{//---------------------------------2
let a =1;//-------------------------------------2.1
const b =second(1,2);//-------------------------2.2
a=a+b;//----------------------------------------2.3
return a;//-------------------------------------2.4
};
function second(x,y){//-------------------------------3
var c =2;//-------------------------------------3.1
return c;//-------------------------------------3.2
}
const x =first();//-----------------------------------4
-
代碼被編譯后先創建全域執行背景關系推入呼叫堆疊(call stack);

-
執行頂層代碼(序號1,2,3):運行1宣告name變數; 運行2宣告first函式;運行3宣告second函式;保存以上變數環境到全域背景關系中;
-
執行到序號4開始呼叫first()函式,并創建first()的執行背景關系推入呼叫堆疊,準備執行first()內部代碼跳轉到2.1;

-
運行到2.1宣告變數a保存到first()的執行背景關系;
-
運行2.2呼叫新函式second(),創建second()的執行背景關系推入呼叫堆疊,停止first()的執行跳轉到3.1;

-
運行3.1宣告變數c保存到second()的執行背景關系;
-
運行3.2return陳述句表示完成該函式執行,second()的執行背景關系將從呼叫堆疊中彈出(此處雖然彈出,但是其中變數環境仍可能被使用,涉及到閉包的概念),呼叫堆疊重新指向first(),代碼重新跳轉回2.3;

-
運行2.3執行代碼內容;
-
運行2.4,first()的執行背景關系將從呼叫堆疊中彈出,呼叫堆疊重新指向gloal(),代碼重新跳轉回4,將回傳值最終賦值給x;

- 此后呼叫堆疊一直保持在這個狀態直到我們關閉瀏覽器來終止程式,最終彈出global()全域背景關系;

由此我們可以發現呼叫堆疊的執行背景關系根據函式呼叫來出入堆疊確保了JS引擎能正確執行代碼的順序,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/544769.html
標籤:JavaScript
