亚欧色一区w666天堂,色情一区二区三区免费看,少妇特黄A片一区二区三区,亚洲人成网站999久久久综合,国产av熟女一区二区三区

  • 發布文章
  • 消息中心
點贊
收藏
評論
分享
原創

JavaScript運行原理

2024-05-30 01:31:26
10
0

說(shuo)到JavaScript的運行原(yuan)理,自(zi)然(ran)繞不開JS引擎,運行上下文,單線程,事(shi)件循環,事(shi)件驅動,回調函數等概(gai)念

為了更好(hao)的(de)(de)理解(jie)JavaScript如何工作的(de)(de),首先要理解(jie)以下(xia)幾個概(gai)念。

  • JS Engine(JS引擎)
  • Runtime(運行上下文)
  • Call Stack (調用棧)
  • Event Loop(事件循環)
  • Callback (回調)

1.JS Engine

簡單來(lai)說,JS引擎主要(yao)是(shi)對JS代(dai)碼進(jin)行(xing)詞法(fa)、語(yu)法(fa)等(deng)分析,通過編(bian)(bian)譯(yi)器將代(dai)碼編(bian)(bian)譯(yi)成(cheng)可執行(xing)的機器碼讓計算機去執行(xing)。

目前最流行的JS引擎非V8莫屬了(le),Chrome瀏覽(lan)器和Node.js采用的引擎就是(shi)V8引擎。

就(jiu)如JVM虛擬機(ji)一樣,JS引擎中也有堆(Memory Heap)和棧(Call Stack)的概念。

  • 棧。用(yong)來存(cun)儲方(fang)法(fa)調用(yong)的地方(fang),以及(ji)基礎數據類(lei)型(如var a = 1)也是存(cun)儲在棧里(li)面的,會隨著方(fang)法(fa)調用(yong)結束(shu)而(er)自(zi)動銷毀掉(入棧-->方(fang)法(fa)調用(yong)后-->出棧)。

  • 堆(dui)。JS引擎(qing)中給對(dui)象分配的(de)內(nei)存空間是放在堆(dui)中的(de)。如var foo = {name: 'foo'} 那么(me)這個foo所(suo)指向的(de)對(dui)象是存儲在堆(dui)中的(de)。

此外,JS中存(cun)在(zai)閉包的概(gai)念,對于基本類型變量如果存(cun)在(zai)與閉包當(dang)中,那(nei)么也將存(cun)儲(chu)在(zai)堆中。

2.RunTime

JS在(zai)(zai)瀏(liu)覽器中可(ke)以(yi)調用瀏(liu)覽器提(ti)供的(de)API,如window對象,DOM相(xiang)關API等(deng)。這些接口并不是由(you)V8引擎(qing)提(ti)供的(de),是存在(zai)(zai)與瀏(liu)覽器當中的(de)。因此簡單來說,對于(yu)這些相(xiang)關的(de)外部接口,可(ke)以(yi)在(zai)(zai)運行時(shi)供JS調用,以(yi)及JS的(de)事(shi)件循環(Event Loop)和事(shi)件隊(dui)列(Callback Queue),把這些稱為RunTime。有些地方也把JS所(suo)用到(dao)的(de)core lib核心庫也看(kan)作RunTime的(de)一部分(fen)。

同(tong)樣,在(zai)Node.js中(zhong),可以把Node的各種庫提(ti)供的API稱(cheng)為(wei)RunTime。所以可以這么理解,Chrome和Node.js都采用(yong)相同(tong)的V8引擎,但擁(yong)有不(bu)同(tong)的運行環境(RunTime Environments)。

3.Call Stack

JS被設計為(wei)單線(xian)程運(yun)行(xing)的(de),這是因(yin)為(wei)JS主要用(yong)來(lai)實(shi)現(xian)很多(duo)交(jiao)互相關的(de)操作,如(ru)DOM相關操作,如(ru)果(guo)是多(duo)線(xian)程會(hui)(hui)造成(cheng)復雜(za)的(de)同(tong)步問題。因(yin)此JS自誕(dan)生(sheng)以來(lai)就是單線(xian)程的(de),而且主線(xian)程都(dou)是用(yong)來(lai)進行(xing)界(jie)面(mian)相關的(de)渲染(ran)操作**(為(wei)什么(me)說是主線(xian)程,因(yin)為(wei)HTML5 提供了Web Worker,獨立(li)的(de)一個后臺(tai)JS,用(yong)來(lai)處理一些耗時數據操作。因(yin)為(wei)不會(hui)(hui)修改相關DOM及頁(ye)面(mian)元素,因(yin)此不影響頁(ye)面(mian)性(xing)能)**,如(ru)果(guo)有(you)阻塞產生(sheng)會(hui)(hui)導致瀏覽器卡死。

如(ru)果一(yi)個遞(di)歸調用沒(mei)有終止(zhi)條件,是(shi)一(yi)個死循(xun)環(huan)的話,會導致調用棧(zhan)內存不夠(gou)而(er)溢出,如(ru):

function foo() {
	foo();
}
foo();

例(li)子中foo函(han)數(shu)循環調(diao)用其本身(shen),且沒(mei)有終止條件,瀏(liu)覽器控(kong)制臺輸(shu)出(chu)調(diao)用棧達到(dao)最大(da)調(diao)用次數(shu)。

JS線(xian)程如果遇(yu)到比較耗時操作,如讀取文(wen)件(jian),AJAX請求操作怎(zen)么辦(ban)?這里JS用到了Callback回(hui)調函(han)數來處理。

4.Event Loop & Callback

JS通過(guo)回調的方式,異步處(chu)理耗時的任務。一個簡單的例(li)子:

var result = ajax('...');
console.log(result);

此時并不(bu)會得到result的值,result是(shi)undefined。這是(shi)因為ajax的調(diao)用(yong)是(shi)異步的,當前線程并不(bu)會等(deng)到ajax請求(qiu)到結果后才執行console.log語(yu)句。而是(shi)調(diao)用(yong)ajax后請求(qiu)的操作交(jiao)給回調(diao)函數(shu),自己是(shi)立刻返回。正確的寫法(fa)應(ying)該(gai)是(shi):

ajax('...', function(result) {
	console.log(result);
})

此時才能正確輸出請求返回的結(jie)果。

JS引(yin)擎其實并不提供異(yi)步(bu)的支持,異(yi)步(bu)支持主要(yao)依賴于運(yun)行環境(瀏(liu)覽器或Node.js)。

So, for example, when your JavaScript program makes an Ajax request to fetch some data from the server, you set up the “response” code in a function (the “callback”), and the JS Engine tells the hosting environment:
“Hey, I’m going to suspend execution for now, but whenever you finish with that network request, and you have some data, please call this function back.”

The browser is then set up to listen for the response from the network, and when it has something to return to you, it will schedule the callback function to be executed by inserting it into the event loop.

上面這兩段話摘(zhai)自于How JavaScript works,以通俗(su)的方(fang)式解釋(shi)了JS如何調用回調函數實現(xian)異步處(chu)理。

所以什么是Event Loop?

Event Loop只做一件事情,負責監聽Call Stack和Callback Queue。當Call Stack里面的調用棧運行完變成空了,Event Loop就把Callback Queue里面的第一條事件(其實就是回調函數)放到調用棧中并執行它,這個過程就是Event Loop的一個tick,后續(xu)不斷(duan)循環執行這個操作(zuo)。

一個setTimeout的例子(zi):

console.log('Hi');
setTimeout(function cb1() { 
	console.log('cb1');
}, 5000);
console.log('Bye');

setTimeout有(you)個(ge)要注意的(de)地方,如(ru)上(shang)述例子延(yan)遲5s執行,不(bu)是(shi)嚴格(ge)意義(yi)上(shang)的(de)5s,正確來說(shuo)是(shi)至少5s以后(hou)會(hui)執行。因(yin)為Web API會(hui)設定一個(ge)5s的(de)定時(shi)器(qi),時(shi)間到期后(hou)將回調函數(shu)加到隊(dui)列(lie)中,此時(shi)該(gai)回調函數(shu)還(huan)不(bu)一定會(hui)馬上(shang)運行,因(yin)為隊(dui)列(lie)中可能還(huan)有(you)之前加入的(de)其(qi)他回調函數(shu),而且還(huan)必須(xu)等到Call Stack空了(le)之后(hou)才會(hui)從隊(dui)列(lie)中取一個(ge)回調執行。

所以常見的setTimeout(callback, 0) 的做法就是(shi)為(wei)了在(zai)常規的調(diao)(diao)用介紹后馬上運行(xing)回調(diao)(diao)函數(shu)。

console.log('Hi');
setTimeout(function() {
	console.log('callback');
}, 0);
console.log('Bye');
// 輸出
// Hi
// Bye
// callback

說一個容易犯錯的例子:

for (var i = 0; i < 5; i++) {
	setTimeout(function() {
   		console.log(i);
	}, 1000 * i);
}

// 輸出:5 5 5 5 5

上面這(zhe)個栗子并不是輸出(chu)0,1,2,3,4,第一(yi)反應覺得應該是這(zhe)樣。但梳(shu)理了JS的時間循環后,應該很(hen)容易(yi)明白。

調用棧先執行 for(var i = 0; i < 5; i++) {...}方法,里面的定時器會到時間后會直接把回調函數放到事件隊列中,等for循環執行完在依次取出放進調用棧。當for循環執行完時,i的值(zhi)已經變成5,所以最后輸出全都是5。

總結

最后總結(jie)一下,JS的(de)運行(xing)原理主要有(you)以(yi)下幾個方面:

  • JS引擎主要負責把JS代(dai)碼(ma)轉為(wei)機(ji)器能(neng)執行的(de)機(ji)器碼(ma),而JS代(dai)碼(ma)中調用(yong)的(de)一些WEB API則由其(qi)運行環境提供,這里指的(de)是瀏覽(lan)器。

  • JS是單線程(cheng)(cheng)運行,每次都從調(diao)用棧出(chu)(chu)取出(chu)(chu)代碼(ma)進行調(diao)用。如(ru)果(guo)當(dang)前代碼(ma)非常(chang)耗時,則會阻塞當(dang)前線程(cheng)(cheng)導(dao)致瀏覽(lan)器卡頓。

  • 回調(diao)函數是通(tong)過加入到(dao)事(shi)件隊列中(zhong),等(deng)待(dai)Event Loop拿出并放到(dao)調(diao)用(yong)棧(zhan)中(zhong)進行調(diao)用(yong)。只(zhi)有Event Loop監(jian)聽到(dao)調(diao)用(yong)棧(zhan)為空(kong)時(shi),才會從(cong)(cong)事(shi)件隊列中(zhong)從(cong)(cong)隊頭(tou)拿出回調(diao)函數放進調(diao)用(yong)棧(zhan)里。

0條評論
作者已關閉評論
無住生心
3文章(zhang)數
0粉絲(si)數
無住生心
3 文章(zhang) | 0 粉(fen)絲(si)
無住生心
3文(wen)章(zhang)數
0粉絲數
無住生心
3 文(wen)章 | 0 粉絲(si)
原創

JavaScript運行原理

2024-05-30 01:31:26
10
0

說(shuo)到JavaScript的運行(xing)原理,自然繞不(bu)開(kai)JS引擎(qing),運行(xing)上下文,單線程,事件(jian)循環,事件(jian)驅動,回調函(han)數等(deng)概念(nian)

為(wei)了更好的(de)理(li)解JavaScript如何工作的(de),首先要(yao)理(li)解以下幾個(ge)概念。

  • JS Engine(JS引擎)
  • Runtime(運行上下文)
  • Call Stack (調用棧)
  • Event Loop(事件循環)
  • Callback (回調)

1.JS Engine

簡單來說,JS引擎主要是對JS代碼(ma)進行詞法(fa)(fa)、語(yu)法(fa)(fa)等分析,通過編譯器將代碼(ma)編譯成可執(zhi)行的機器碼(ma)讓計算機去(qu)執(zhi)行。

目前最流行的JS引(yin)擎非(fei)V8莫屬了(le),Chrome瀏覽器和(he)Node.js采(cai)用的引(yin)擎就是(shi)V8引(yin)擎。

就如JVM虛擬機一樣,JS引擎中也有堆(Memory Heap)和棧(zhan)(Call Stack)的概(gai)念。

  • 棧(zhan)。用來存儲方法(fa)(fa)調(diao)用的地方,以及基(ji)礎數(shu)據類型(如var a = 1)也是存儲在棧(zhan)里面的,會隨著方法(fa)(fa)調(diao)用結束而自動銷毀掉(diao)(入棧(zhan)-->方法(fa)(fa)調(diao)用后-->出棧(zhan))。

  • 堆(dui)。JS引擎中(zhong)給對(dui)象分配的(de)內存空(kong)間是(shi)放在堆(dui)中(zhong)的(de)。如var foo = {name: 'foo'} 那么這個foo所指向(xiang)的(de)對(dui)象是(shi)存儲在堆(dui)中(zhong)的(de)。

此(ci)外(wai),JS中(zhong)存(cun)在(zai)閉包(bao)的(de)概念,對(dui)于基本類型(xing)變量如果存(cun)在(zai)與(yu)閉包(bao)當中(zhong),那么也將存(cun)儲(chu)在(zai)堆中(zhong)。

2.RunTime

JS在瀏(liu)覽(lan)器(qi)(qi)中(zhong)可以調用(yong)瀏(liu)覽(lan)器(qi)(qi)提供(gong)的API,如window對(dui)象,DOM相(xiang)關API等。這些接(jie)口并不(bu)是(shi)由V8引擎提供(gong)的,是(shi)存在與瀏(liu)覽(lan)器(qi)(qi)當(dang)中(zhong)的。因此(ci)簡單來說,對(dui)于這些相(xiang)關的外部(bu)接(jie)口,可以在運行時供(gong)JS調用(yong),以及JS的事(shi)件(jian)循(xun)環(Event Loop)和事(shi)件(jian)隊列(Callback Queue),把這些稱為RunTime。有些地方也把JS所(suo)用(yong)到的core lib核心庫(ku)也看(kan)作RunTime的一部(bu)分。

同(tong)(tong)樣,在(zai)Node.js中,可以(yi)把Node的(de)各種庫提(ti)供的(de)API稱為RunTime。所以(yi)可以(yi)這么(me)理(li)解(jie),Chrome和Node.js都采用(yong)相(xiang)同(tong)(tong)的(de)V8引擎(qing),但擁有不同(tong)(tong)的(de)運行環境(RunTime Environments)。

3.Call Stack

JS被設計為單(dan)線(xian)程運行(xing)(xing)的,這是因(yin)為JS主要(yao)用(yong)(yong)來(lai)(lai)實現很多(duo)(duo)交互相(xiang)關(guan)的操(cao)作,如(ru)DOM相(xiang)關(guan)操(cao)作,如(ru)果是多(duo)(duo)線(xian)程會(hui)造成復雜的同步問題。因(yin)此JS自誕生以來(lai)(lai)就(jiu)是單(dan)線(xian)程的,而且(qie)主線(xian)程都是用(yong)(yong)來(lai)(lai)進行(xing)(xing)界面(mian)相(xiang)關(guan)的渲染操(cao)作**(為什么(me)說是主線(xian)程,因(yin)為HTML5 提供了Web Worker,獨立的一個后臺JS,用(yong)(yong)來(lai)(lai)處(chu)理一些(xie)耗時數據操(cao)作。因(yin)為不(bu)會(hui)修(xiu)改(gai)相(xiang)關(guan)DOM及頁面(mian)元素(su),因(yin)此不(bu)影響頁面(mian)性能)**,如(ru)果有阻塞產生會(hui)導致瀏覽器(qi)卡(ka)死。

如(ru)果一(yi)(yi)個(ge)遞歸調用沒有終止(zhi)條件,是一(yi)(yi)個(ge)死循(xun)環的話,會導致調用棧內存不夠而溢出,如(ru):

function foo() {
	foo();
}
foo();

例(li)子中foo函數循(xun)環調用(yong)(yong)其(qi)本身,且沒有終止條(tiao)件,瀏覽器控制(zhi)臺輸(shu)出調用(yong)(yong)棧達到最(zui)大(da)調用(yong)(yong)次數。

JS線(xian)程如果遇到比較耗時操作,如讀取文件,AJAX請求操作怎么辦?這里JS用到了Callback回(hui)調函數來(lai)處理。

4.Event Loop & Callback

JS通過(guo)回調的方式,異(yi)步處理(li)耗時的任務。一個簡(jian)單的例(li)子:

var result = ajax('...');
console.log(result);

此時(shi)并不會(hui)(hui)得到result的(de)(de)(de)值,result是(shi)(shi)undefined。這是(shi)(shi)因(yin)為(wei)ajax的(de)(de)(de)調用(yong)是(shi)(shi)異步的(de)(de)(de),當(dang)前(qian)線程并不會(hui)(hui)等到ajax請(qing)(qing)求(qiu)(qiu)到結果后才(cai)執行console.log語句(ju)。而是(shi)(shi)調用(yong)ajax后請(qing)(qing)求(qiu)(qiu)的(de)(de)(de)操作(zuo)交給回(hui)調函數,自己是(shi)(shi)立刻(ke)返(fan)回(hui)。正確(que)的(de)(de)(de)寫(xie)法(fa)應該是(shi)(shi):

ajax('...', function(result) {
	console.log(result);
})

此時才能正確(que)輸出請求返回的結果。

JS引擎其實(shi)并不提供異步的支持(chi),異步支持(chi)主要依賴(lai)于運行環境(jing)(瀏覽(lan)器或Node.js)。

So, for example, when your JavaScript program makes an Ajax request to fetch some data from the server, you set up the “response” code in a function (the “callback”), and the JS Engine tells the hosting environment:
“Hey, I’m going to suspend execution for now, but whenever you finish with that network request, and you have some data, please call this function back.”

The browser is then set up to listen for the response from the network, and when it has something to return to you, it will schedule the callback function to be executed by inserting it into the event loop.

上面這(zhe)兩段話(hua)摘自于How JavaScript works,以通(tong)俗的方式解釋了(le)JS如(ru)何調用回調函數實現異(yi)步處(chu)理。

所以什么是Event Loop?

Event Loop只做一件事情,負責監聽Call Stack和Callback Queue。當Call Stack里面的調用棧運行完變成空了,Event Loop就把Callback Queue里面的第一條事件(其實就是回調函數)放到調用棧中并執行它,這個過程就是Event Loop的一個tick,后續不斷循(xun)環執行這(zhe)個(ge)操作。

一個setTimeout的例子:

console.log('Hi');
setTimeout(function cb1() { 
	console.log('cb1');
}, 5000);
console.log('Bye');

setTimeout有個要(yao)注意的(de)(de)地方(fang),如上述例(li)子延遲5s執(zhi)行,不是嚴(yan)格意義上的(de)(de)5s,正確(que)來說是至少5s以后會(hui)(hui)(hui)(hui)執(zhi)行。因(yin)為Web API會(hui)(hui)(hui)(hui)設定一個5s的(de)(de)定時器,時間到期后將回(hui)調(diao)函數加到隊(dui)列(lie)中,此時該回(hui)調(diao)函數還(huan)不一定會(hui)(hui)(hui)(hui)馬上運行,因(yin)為隊(dui)列(lie)中可能還(huan)有之前加入的(de)(de)其他回(hui)調(diao)函數,而且還(huan)必須(xu)等到Call Stack空了之后才(cai)會(hui)(hui)(hui)(hui)從隊(dui)列(lie)中取一個回(hui)調(diao)執(zhi)行。

所以常見的setTimeout(callback, 0) 的做法就是為了在常規的調(diao)用介紹后馬(ma)上(shang)運行回調(diao)函數。

console.log('Hi');
setTimeout(function() {
	console.log('callback');
}, 0);
console.log('Bye');
// 輸出
// Hi
// Bye
// callback

說(shuo)一個容易(yi)犯錯的(de)例子:

for (var i = 0; i < 5; i++) {
	setTimeout(function() {
   		console.log(i);
	}, 1000 * i);
}

// 輸出:5 5 5 5 5

上面這個栗子(zi)并不是輸出0,1,2,3,4,第(di)一反應覺得應該是這樣。但梳理(li)了(le)JS的時間循環后,應該很容易(yi)明白。

調用棧先執行 for(var i = 0; i < 5; i++) {...}方法,里面的定時器會到時間后會直接把回調函數放到事件隊列中,等for循環執行完在依次取出放進調用棧。當for循環執行完時,i的值已(yi)經變(bian)成(cheng)5,所以(yi)最后輸出全都是5。

總結

最后總結一下(xia),JS的運行原理主(zhu)要有以下(xia)幾個方面:

  • JS引擎(qing)主要負責把(ba)JS代碼(ma)(ma)轉為機(ji)器(qi)能執行(xing)的機(ji)器(qi)碼(ma)(ma),而JS代碼(ma)(ma)中調用的一些WEB API則(ze)由(you)其運行(xing)環境提供,這里指的是(shi)瀏覽器(qi)。

  • JS是(shi)單線程(cheng)運行,每次都從調用(yong)棧(zhan)出取出代碼進行調用(yong)。如果(guo)當前代碼非(fei)常耗時,則會(hui)阻塞當前線程(cheng)導致(zhi)瀏覽器卡頓(dun)。

  • 回調(diao)函數(shu)是通過(guo)加入到事(shi)件隊列中,等待Event Loop拿(na)出并(bing)放到調(diao)用棧中進行調(diao)用。只有Event Loop監聽到調(diao)用棧為空時,才會從事(shi)件隊列中從隊頭(tou)拿(na)出回調(diao)函數(shu)放進調(diao)用棧里。

文章來自個人專欄
文章 | 訂閱
0條評論
作者已關閉評論
作者已關閉評論
0
0