一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

JavaScript 究竟是如何工作的?(第二部分)

 Earlycl6i7o9ek 2019-06-11

(給前端大全加星標(biāo),提升前端技能

英文:Priyesh Patel  譯文:chorer

在這篇文章的第一部分JavaScript 究竟是如何工作的?(第一部分),我簡(jiǎn)要概述了編程語(yǔ)言的一般工作機(jī)制,并深入探討了 V8 引擎的管道。第二部分將介紹一些更重要的概念,這些概念是每一個(gè) JavaScript 程序員都必須了解的,并且不僅僅和 V8 引擎有關(guān)。

對(duì)于任何一個(gè)程序員來(lái)說,最關(guān)注的兩個(gè)問題無(wú)非就是:時(shí)間復(fù)雜度和空間復(fù)雜度。第一部分介紹了 V8 為改進(jìn) JavaScript 執(zhí)行時(shí)間所做的速度提升和優(yōu)化,第二部分則將著重介紹內(nèi)存管理方面的知識(shí)。

內(nèi)存堆

Orinoco 的 logo:V8 的垃圾回收器

每當(dāng)你在 JavaScript 程序中定義了一個(gè)變量、常量或者對(duì)象時(shí),你都需要一個(gè)地方來(lái)存儲(chǔ)它。這個(gè)地方就是內(nèi)存堆。

當(dāng)遇到語(yǔ)句 var a = 10 的時(shí)候,內(nèi)存會(huì)分配一個(gè)位置用于存儲(chǔ) a 的值

可用內(nèi)存是有限的,而復(fù)雜的程序可能有很多變量和嵌套對(duì)象,因此合理地使用可用內(nèi)存非常重要。

和諸如 C 這種需要顯式分配和釋放內(nèi)存的語(yǔ)言不同,JavaScript 提供了自動(dòng)垃圾回收機(jī)制。一旦對(duì)象/變量離開了上下文并且不再使用,它的內(nèi)存就會(huì)被回收并返還到可用內(nèi)存池中。

在 V8 中,垃圾回收器的名字叫做 Orinoco,它的處理過程非常高效。這篇文章有相關(guān)解釋

標(biāo)記與清除算法

標(biāo)記和清除算法

我們通常會(huì)使用這種簡(jiǎn)單有效的算法來(lái)判定可以從內(nèi)存堆中安全清除的對(duì)象。算法的工作方式正如其名:將對(duì)象標(biāo)記為可獲得/不可獲得,并將不可獲得的對(duì)象清除。

垃圾回收器周期性地從根部或者全局對(duì)象開始,移向被它們引用的對(duì)象,接著再移向被這些對(duì)象引用的對(duì)象,以此類推。所有不可獲得的對(duì)象會(huì)在之后被清除。

內(nèi)存泄漏

雖然垃圾回收器很高效,但是開發(fā)者不應(yīng)該就此將內(nèi)存管理的問題束之高閣。管理內(nèi)存是一個(gè)很復(fù)雜的過程,哪一塊內(nèi)存不再需要并不是單憑一個(gè)算法就能決定的。

內(nèi)存泄漏指的是,程序之前需要用到部分內(nèi)存,而這部分內(nèi)存在用完之后并沒有返回到內(nèi)存池。

下面是一些會(huì)導(dǎo)致你的程序出現(xiàn)內(nèi)存泄漏的常見錯(cuò)誤:

全局變量:如果你不斷地創(chuàng)建全局變量,不管有沒有用到它們,它們都將滯留在程序的整個(gè)執(zhí)行過程中。如果這些變量是深層嵌套對(duì)象,將會(huì)浪費(fèi)大量?jī)?nèi)存。

var a = { ... }var b = { ... }function hello() {
c
= a; // 這是一個(gè)你沒有意識(shí)到的全局變量}

如果你試圖訪問一個(gè)此前沒有聲明過的變量,那么將在全局作用域中創(chuàng)建一個(gè)變量。在上面的例子中,c 是沒有使用 var 關(guān)鍵字顯式創(chuàng)建的變量/對(duì)象。

事件監(jiān)聽器:為了增強(qiáng)網(wǎng)站的交互性或者是制作一些浮華的動(dòng)畫,你可能會(huì)創(chuàng)建大量的事件監(jiān)聽器。而用戶在你的單頁(yè)面應(yīng)用中移向其他頁(yè)面時(shí),你又忘記移除這些監(jiān)聽器,那么也可能會(huì)導(dǎo)致內(nèi)存泄漏。當(dāng)用戶在這些頁(yè)面來(lái)回移動(dòng)的時(shí)候,這些監(jiān)聽器會(huì)不斷增加。

var element  = document.getElementById('button');
element
.addEventListener('click', onClick)

Intervals 和 Timeouts:當(dāng)在這些閉包中引用對(duì)象時(shí),除非閉包本身被清除,否則不會(huì)清除相關(guān)對(duì)象。

setInterval(() => {
// 引用對(duì)象}// 這時(shí)候忘記清除計(jì)時(shí)器// 那么將導(dǎo)致內(nèi)存泄漏!

移除 DOM 元素:這個(gè)問題很常見,類似于全局變量導(dǎo)致的內(nèi)存泄漏。DOM 元素存在于對(duì)象圖內(nèi)存和 DOM 樹中。用例子來(lái)解釋可能會(huì)更好:

var terminator = document.getElementById('terminate');var badElem = document.getElementById('toDelete');
terminator
.addEventListener('click', function() {memory
badElem
.remove();});

在你通過 id = ‘terminate’ 點(diǎn)擊了按鈕之后,toDelete 會(huì)從 DOM 中移除。不過,由于它仍然被監(jiān)聽器引用,為這個(gè)對(duì)象分配的內(nèi)存并不會(huì)被釋放。

var terminator = document.getElementById('terminate');
terminator
.addEventListener('click', function() {
var badElem = document.getElementById('toDelete');
badElem
.remove();});

badElem 是局部變量,在移除操作完成之后,內(nèi)存將會(huì)被垃圾回收器回收。

調(diào)用棧

棧是一種遵循 LIFO(先進(jìn)后出)規(guī)則的數(shù)據(jù)結(jié)構(gòu),用于存儲(chǔ)和獲取數(shù)據(jù)。JavaScript 引擎通過棧來(lái)記住一個(gè)函數(shù)中最后執(zhí)行的語(yǔ)句所在的位置。

function multiplyByTwo(x) {
return x*2;}function calculate() {
const sum = 4 2;
return multiplyByTwo(sum);}
calculate
()var hello = 'some more code follows'

1.引擎了解到我們的程序中有兩個(gè)函數(shù)
2.運(yùn)行 calculate() 函數(shù)
3.將 calculate 壓棧并計(jì)算兩數(shù)之和
4.運(yùn)行 multiplyByTwo() 函數(shù)
5.將 multiplyByTwo 函數(shù)壓棧并執(zhí)行算術(shù)計(jì)算 x*2
6.在返回結(jié)果的同時(shí),將 multiplyByTwo() 從棧中彈出,之后回到 calculate() 函數(shù)
7.在 calculate() 函數(shù)返回結(jié)果的同時(shí),將 calculate() 從棧中彈出,繼續(xù)執(zhí)行后面的代碼

棧溢出

在不對(duì)棧執(zhí)行彈出的情況下,可連續(xù)壓棧的數(shù)目取決于棧的大小。如果超過了這個(gè)界限之后還不斷地壓棧,最終會(huì)導(dǎo)致棧溢出。chrome 瀏覽器將會(huì)拋出一個(gè)錯(cuò)誤以及被稱為棧幀的??煺铡?/span>

遞歸:遞歸指的是函數(shù)調(diào)用自身。遞歸可以大幅度地減少執(zhí)行算法所花費(fèi)的時(shí)間(時(shí)間復(fù)雜度),不過它的理解和實(shí)施較為復(fù)雜。

下面的例子中,基本事件永遠(yuǎn)不會(huì)執(zhí)行,lonley 函數(shù)在沒有返回值的情況下不斷地調(diào)用自身,最終會(huì)導(dǎo)致棧溢出。

function lonely() {
if (false) {
return 1; // 基本事件
}
lonely
(); // 遞歸調(diào)用}

為什么 JavaScript 是單線程的?

一個(gè)線程代表著在同一時(shí)間段內(nèi)可以單獨(dú)執(zhí)行的程序部分的數(shù)目。要想查看一門語(yǔ)言是單線程的還是多線程的,最簡(jiǎn)單的方式就是了解它有多少個(gè)調(diào)用棧。JS 只有一個(gè),所以它是單線程語(yǔ)言。

這樣不是會(huì)阻礙程序運(yùn)行嗎?如果我運(yùn)行多個(gè)耗時(shí)的阻塞操作,例如 HTTP 請(qǐng)求,那么程序必須得在每一個(gè)操作得到響應(yīng)之后才能執(zhí)行后面的代碼。

為了解決這個(gè)問題,我們需要找到一種可以在單線程下異步完成任務(wù)的辦法。事件循環(huán)就是用來(lái)發(fā)揮這個(gè)作用的。

事件循環(huán)

到現(xiàn)在為止,我們談到的內(nèi)容大多包含在 V8 里面,但是如果你去查看 V8 的代碼庫(kù),你會(huì)發(fā)現(xiàn)它并不包含例如 setTimeout 或者 DOM 的實(shí)現(xiàn)。事實(shí)上,除了運(yùn)行引擎之外,JS 還包括瀏覽器提供的 Web API,這些 API 用于拓展 JS。

結(jié)論

關(guān)于制作一門編程語(yǔ)言,其實(shí)還有很多內(nèi)容,并且語(yǔ)言的實(shí)現(xiàn)在這些年也是不斷變化的。我希望這兩篇博客可以幫助你成為一名更好的 JS 程序員,并且接受 JS 中那些晦澀難懂的內(nèi)容 。對(duì)于諸如“V8”,“事件循環(huán)”,“調(diào)用?!边@樣的術(shù)語(yǔ),你現(xiàn)在應(yīng)該熟悉了。

大部分的學(xué)生(比如我)是從一個(gè)新的框架起步,之后再去學(xué)習(xí)原生 JS?,F(xiàn)在他們應(yīng)該熟悉代碼背后發(fā)生的事情了,反過來(lái),這將幫助他們寫出更好的代碼。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    日韩在线视频精品视频| 麻豆果冻传媒一二三区| 色婷婷人妻av毛片一区二区三区| 亚洲精品成人综合色在线| 精品香蕉国产一区二区三区| 久久一区内射污污内射亚洲| 久久大香蕉一区二区三区| 国产成人国产精品国产三级| 亚洲精品熟女国产多毛 | 男女午夜在线免费观看视频| 国内九一激情白浆发布| 日韩一区二区三区观看| 国产成人综合亚洲欧美日韩| 午夜福利黄片免费观看| 日韩欧美国产高清在线| 丰满少妇高潮一区二区| 狠色婷婷久久一区二区三区| 日本一本不卡免费视频 | 欧美一区二区三区喷汁尤物| 大屁股肥臀熟女一区二区视频| 国产乱淫av一区二区三区| 日韩在线欧美一区二区| 欧美乱视频一区二区三区| 日韩午夜老司机免费视频| 中文字幕五月婷婷免费 | 一二区不卡不卡在线观看| 内射精子视频欧美一区二区| 好吊日在线观看免费视频| 欧美日韩亚洲精品内裤| 日韩精品一区二区毛片| 日韩精品毛片视频免费看| 少妇肥臀一区二区三区| 深夜视频成人在线观看| 这里只有九九热精品视频| 日韩精品一区二区三区射精 | 国产一级精品色特级色国产| 国产又粗又猛又爽又黄| 日本在线不卡高清欧美| 久久精品国产亚洲av久按摩| 国产精品超碰在线观看| 日本和亚洲的香蕉视频|