成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

JS高級(jí)入門(mén)教程

zsy888 / 1055人閱讀

摘要:解析首先簡(jiǎn)稱(chēng)是由歐洲計(jì)算機(jī)制造商協(xié)會(huì)制定的標(biāo)準(zhǔn)化腳本程序設(shè)計(jì)語(yǔ)言。級(jí)在年月份成為的提議,由核心與兩個(gè)模塊組成。通過(guò)引入統(tǒng)一方式載入和保存文檔和文檔驗(yàn)證方法對(duì)進(jìn)行進(jìn)一步擴(kuò)展。其中表示的標(biāo)記位正好是低三位都是。但提案被拒絕了。

JS高級(jí)入門(mén)教程 目錄

本文章定位及介紹

JavaScript與ECMAScript的關(guān)系

DOM的本質(zhì)及DOM級(jí)介紹

JS代碼特性

基本類(lèi)型與引用類(lèi)型

JS的垃圾回收機(jī)制

作用域鏈介紹及其實(shí)現(xiàn)原理

閉包

this指針

自執(zhí)行函數(shù)的介紹及應(yīng)用

聲明提前

JS線程問(wèn)題

本培訓(xùn)的定位及相關(guān)介紹 內(nèi)容特點(diǎn):

配套代碼解析,示例代碼

配套測(cè)試試題

內(nèi)容實(shí)在,不會(huì)講爛大街的東西。

目標(biāo)對(duì)象定位:

主要面向?qū)ο螅簩?duì)于有一年左右工作經(jīng)驗(yàn)的前端工程師。提高JS的認(rèn)識(shí),突破JS學(xué)習(xí)瓶頸。

對(duì)于沒(méi)有經(jīng)驗(yàn)的小伙伴,可以通過(guò)本文章對(duì)JS有初步認(rèn)識(shí),了解JS的相關(guān)特性。

除了系統(tǒng)性的JS內(nèi)容,還會(huì)穿插介紹一些筆者在學(xué)習(xí)過(guò)程中遇到的認(rèn)識(shí)上的困惑與小問(wèn)題。希望能減少小伙伴在學(xué)習(xí)過(guò)程中遇到的障礙。

對(duì)了,里面有些內(nèi)容是本人覺(jué)得有趣的,好玩的概念??赡艹四軒痛蠹依砬宄拍钜酝?,并沒(méi)什么卵用。希望大家不要見(jiàn)怪。

JavaScript與ECMAScript的關(guān)系 前言:

可能大家在閱讀JS相關(guān)書(shū)籍或?yàn)g覽網(wǎng)上資料的時(shí)候會(huì)多多少少看過(guò)ECMAScript這個(gè)詞匯,它與JS有什么關(guān)系呢?還有為什么JS最新的語(yǔ)法不是叫JS6而是叫ES6呢?在這里給大家介紹一下JS與ES的關(guān)系。

解析:

首先ECMAScript(簡(jiǎn)稱(chēng)ES)是由歐洲計(jì)算機(jī)制造商協(xié)會(huì)ECMA(European Computer Manufacturers Association)制定的標(biāo)準(zhǔn)化腳本程序設(shè)計(jì)語(yǔ)言。

其次ES只是JS的其中一部分。一個(gè)完整的JS主要包括這幾部分(ES,DOM,BOM)。他們的結(jié)構(gòu)圖是這樣的。

其中ES負(fù)責(zé)腳本的基本語(yǔ)法及邏輯的實(shí)現(xiàn)。

DOM負(fù)責(zé)HTML標(biāo)簽的解析及渲染實(shí)現(xiàn)。

BOM負(fù)責(zé)提供瀏覽器的API接口。

結(jié)論:

ES是一個(gè)與應(yīng)用場(chǎng)景無(wú)關(guān)的純語(yǔ)言,只提供基本邏輯操作的實(shí)現(xiàn),原則上不實(shí)現(xiàn)視覺(jué)上的功能。

WEB只是ES實(shí)現(xiàn)的宿主環(huán)境之一。ES在WEB中結(jié)合DOM和BOM形成了JS。

這也是為什么最新的JS語(yǔ)法稱(chēng)為ES6而不是JS6。(實(shí)際上,像前面所說(shuō)的,ES只是一個(gè)純語(yǔ)言,所以ES6上更新的只是JS語(yǔ)法上的內(nèi)容。并不會(huì)提供其他新的WEB功能。新的WEB功能的內(nèi)容屬于HTML或CSS的內(nèi)容,如HTML5和CSS3)

小插曲,有的小伙伴可能看到過(guò)JScript這個(gè)詞。JScript是在未制定出ES標(biāo)準(zhǔn)的混亂時(shí)代中,微軟出的自家使用腳本語(yǔ)言,相當(dāng)于IE版本的JS。但現(xiàn)在JS已經(jīng)得到了統(tǒng)一。JScript已經(jīng)不復(fù)存在了。

DOM的本質(zhì)及DOM級(jí)介紹 前言:

在前端學(xué)習(xí)過(guò)程中,我們一聽(tīng)到DOM會(huì)自然地聯(lián)想到HTML的標(biāo)簽。但是DOM真的只是和HTML有關(guān)系嗎?到底什么才是DOM呢?另外時(shí)常遇到的DOM0123級(jí)又是指什么東西呢?

解析(DOM的本質(zhì))

我們先來(lái)看看DOM的字面意思是什么:DOM(Document Object Model),文檔對(duì)象模型。是將基于某文檔結(jié)構(gòu)(如XML結(jié)構(gòu))的字符串轉(zhuǎn)化為一棵在常駐內(nèi)容的樹(shù)狀數(shù)據(jù)結(jié)構(gòu)的模型。對(duì)于XML來(lái)說(shuō),就是對(duì)XML標(biāo)簽進(jìn)行解析后的數(shù)據(jù)結(jié)構(gòu)體(我們稱(chēng)之為DOM樹(shù))。

它的理念是:開(kāi)發(fā)者通過(guò)該數(shù)據(jù)結(jié)構(gòu)獲得文檔結(jié)構(gòu)(即有特定字符串組成的文檔)的控制權(quán)。通過(guò)DOM提供的接口,對(duì)該文檔結(jié)構(gòu)進(jìn)行增,刪,改等操作,即對(duì)數(shù)據(jù)結(jié)構(gòu)進(jìn)行操作。

DOM本身是獨(dú)立于平臺(tái)和語(yǔ)言的。是進(jìn)行腳本解析的解決思路(將文檔轉(zhuǎn)化為對(duì)象,并以樹(shù)狀結(jié)構(gòu)組織起來(lái))。不同的語(yǔ)言可以針對(duì)自身的特點(diǎn)制作自己的DOM實(shí)現(xiàn)。(而我們理解中的DOM只是針對(duì)HTML語(yǔ)言的是其中一種實(shí)現(xiàn)而已)

除了HTML DOM外,其他的語(yǔ)言也發(fā)布了只針對(duì)自己的DOM標(biāo)準(zhǔn)。如SVG(可伸縮矢量圖),MathML(數(shù)學(xué)標(biāo)記語(yǔ)言),SMIL(同步多媒體集成語(yǔ)言)

解析(DOM級(jí)別介紹)

解釋完DOM的本質(zhì)以后,接下來(lái)的DOM全都特指HTML DOM

一句話說(shuō)完,DOM級(jí)別只是DOM的版本。不同的DOM級(jí)實(shí)現(xiàn)了不同的功能。

DOM0是指在W3C進(jìn)行標(biāo)準(zhǔn)化之前,還處于未形成標(biāo)準(zhǔn)的初期階段的版本。即還未形成標(biāo)準(zhǔn)的東西。嚴(yán)格意義上并不在DOM版本的范疇,只是為版本出來(lái)前的DOM起個(gè)名字,所以才說(shuō)0版本。

DOM1級(jí)在1998年10月份成為W3C的提議,由DOM核心與DOM HTML兩個(gè)模塊組成。DOM核心能映射以XML為基礎(chǔ)的文檔結(jié)構(gòu),允許獲取和操作文檔的任意部分(即對(duì)某標(biāo)簽的get和set)。DOM HTML通過(guò)添加HTML專(zhuān)用的對(duì)象與函數(shù)對(duì)DOM核心進(jìn)行了擴(kuò)展(即封裝了一些函數(shù)和對(duì)象,更方便地get和set)。

DOM2通過(guò)對(duì)象接口增加了對(duì)鼠標(biāo)和用戶(hù)界面事件、范圍、遍歷(重復(fù)執(zhí)行DOM文檔)和層疊樣式表(CSS)的支持。

DOM3通過(guò)引入統(tǒng)一方式載入和保存文檔和文檔驗(yàn)證方法對(duì)DOM進(jìn)行進(jìn)一步擴(kuò)展。

并沒(méi)什么卵用,只是學(xué)習(xí)幾個(gè)名詞的意思。

JS代碼特性介紹 前言

這部分內(nèi)容適合沒(méi)有實(shí)際經(jīng)驗(yàn)的同學(xué),可以通過(guò)這一部分了解JS的特性。有經(jīng)驗(yàn)的同學(xué)也可以了解一下,因?yàn)榻酉聛?lái)的內(nèi)容都是圍繞這幾大特性進(jìn)行講解的。

特性

弱類(lèi)型語(yǔ)言:在JS中,變量沒(méi)有固定的數(shù)據(jù)類(lèi)型。不同數(shù)據(jù)類(lèi)型的變量是可以相互轉(zhuǎn)換的,如:var a = 0; a = "a";(前面賦值為數(shù)值類(lèi)型,后面變成了字符串類(lèi)型) 而C++,PHP則是強(qiáng)類(lèi)型語(yǔ)言,不能直接進(jìn)行數(shù)據(jù)類(lèi)型的轉(zhuǎn)換。

解析性語(yǔ)言:不同于java或C#等編譯性語(yǔ)言,js是不需要進(jìn)行編譯的,而是由瀏覽器進(jìn)行動(dòng)態(tài)地解析與執(zhí)行。可以理解為瀏覽器是一個(gè)大型的函數(shù),而JS代碼是函數(shù)的參數(shù)。由瀏覽器去解析JS代碼。

跨平臺(tái)性:由于JS只依賴(lài)瀏覽器本身。底層實(shí)現(xiàn)無(wú)關(guān),使得其余操作環(huán)境無(wú)關(guān)。實(shí)現(xiàn)了跨平臺(tái)。

一切皆對(duì)象:JS是一項(xiàng)面向?qū)ο蟮恼Z(yǔ)言。所看到的一切都是一個(gè)對(duì)象。

單線程:JS是單線程的。這牽扯到JS的代碼執(zhí)行順序,下面章節(jié)會(huì)進(jìn)行介紹。

垃圾自動(dòng)回收:JS不需要主動(dòng)回收內(nèi)存。JS引擎內(nèi)部會(huì)周期性地檢查內(nèi)容,定時(shí)回收無(wú)用的內(nèi)存。

基本類(lèi)型與引用類(lèi)型 前言

本小節(jié)將介紹JS的基本類(lèi)型和引用類(lèi)型。當(dāng)然我們不會(huì)講哪些無(wú)聊的基本語(yǔ)法,而是深入內(nèi)部,介紹一些有趣的東西。

解析

ECMAScript中有5種基本數(shù)據(jù)類(lèi)型:Undefined,Null,Boolean,Number和String。以及一種引用數(shù)據(jù)類(lèi)型Object。

其中Undefined和Null最為特殊,因?yàn)樗麄兪侵挥幸粋€(gè)值的數(shù)據(jù)類(lèi)型(undefined和null)。而且他們幾乎是同義的,他們之間有什么區(qū)別呢?(雖然并沒(méi)什么卵用,但面試卻最喜歡出這個(gè)題)。在這里做一下介紹。示例代碼

其實(shí)Underfined表示“缺失值”。表示有一個(gè)變量存在,即進(jìn)行了定義,但該變量沒(méi)有被賦值。

而Null表示一個(gè)空對(duì)象指針,根本不存在這個(gè)變量。Null更多地是起到語(yǔ)義作用。強(qiáng)調(diào)不存在這個(gè)變量。而且在實(shí)際編程過(guò)程中,除非主動(dòng)為變量賦值為null,否則很少出現(xiàn)變量為null的情況。另外null的作用是提前標(biāo)示該變量已無(wú)用,讓GC回收機(jī)制能早點(diǎn)回收該資源。

還有一個(gè)常見(jiàn)的面試題:請(qǐng)寫(xiě)出typeof null的值。如示例代碼所示typeof null的值為object。有沒(méi)有小伙伴會(huì)好奇為什么其他4中基本數(shù)據(jù)類(lèi)型的類(lèi)型值都是其自身。而null的類(lèi)型卻為object

其實(shí)這與JS的設(shè)計(jì)有關(guān)。JS類(lèi)型值是存在32 BIT空間里面的,在這32位中有1-3位表示其類(lèi)型值,其它位表示真實(shí)數(shù)值。其中表示object的標(biāo)記位正好是低三位都是0。000: object. The data is a reference to an object.

而JS里的Null是機(jī)器碼NULL空指針(空指針以全0標(biāo)示)故其標(biāo)示為也是0,最終體現(xiàn)的類(lèi)型還是object。曾經(jīng)有提案 typeof null === "null"。但提案被拒絕了。

基本數(shù)據(jù)類(lèi)型和引用數(shù)據(jù)類(lèi)型的區(qū)別:基本數(shù)據(jù)類(lèi)型是按值訪問(wèn)的。即該變量就存在了實(shí)際值。而引用數(shù)據(jù)類(lèi)型保存的是則是對(duì)實(shí)際值的引用(即指向?qū)嶋H值的指針)。而我們知道這個(gè)有什么用呢?當(dāng)然有用,這涉及到對(duì)象復(fù)制(淺復(fù)制與深復(fù)制)的問(wèn)題。

我們來(lái)看一個(gè)例子示例代碼。可以看到,到直接使用b=a進(jìn)行賦值時(shí)。這時(shí)b獲取到是a變量的指針值,即此時(shí)b和a指向的是同一個(gè)地址值。所以當(dāng)修改b對(duì)象中的值時(shí),a對(duì)象中的值也會(huì)發(fā)生改變。這種只復(fù)制指針地址值的行為稱(chēng)為淺復(fù)制。相應(yīng)的,如果能返回獨(dú)立對(duì)象的值,我們稱(chēng)為深復(fù)制。這也是為什么array的復(fù)制需要用到concat函數(shù)而不是直接用“=”進(jìn)行復(fù)制。

另外,插播一個(gè)知識(shí)點(diǎn)。在進(jìn)行函數(shù)參數(shù)傳遞時(shí)。是通過(guò)按值傳遞的。即傳遞的是變量本身的值,而不是變量的引用。同樣我們看一下示例代碼。即在函數(shù)在進(jìn)行傳遞參數(shù)時(shí),會(huì)新建一個(gè)形參變量elem,并為其賦予實(shí)參變量的值。也許有同學(xué)會(huì)認(rèn)為,在修改objA的時(shí)候明明會(huì)影響函數(shù)外部值,為什么還能叫做按值傳遞呢?其實(shí)這恰恰是按值專(zhuān)遞的結(jié)果。因?yàn)檫@里傳遞的是objA這個(gè)指針對(duì)象的值,即對(duì)象的地址。那么elem指向的就行該對(duì)象。即objA和elem指向的是同一個(gè)值(淺復(fù)制)。所以外部會(huì)發(fā)生變化

引用類(lèi)型和按值傳遞的概念很重要,后面講函數(shù)特性及this指針的時(shí)候會(huì)用到。

另外,還有一個(gè)有趣的知識(shí)點(diǎn)。var a = "aaaa";這里定義的a明明只是一個(gè)基本數(shù)據(jù)類(lèi)型。而不是object,為什么會(huì)有a.substr這樣的函數(shù)呢?示例代碼。其實(shí)JS引擎在讀取基本數(shù)據(jù)類(lèi)型時(shí),會(huì)在后臺(tái)創(chuàng)建一個(gè)對(duì)應(yīng)的類(lèi)對(duì)象(稱(chēng)為基本包裝類(lèi)型)。從而使我們更加方便地對(duì)該變量進(jìn)行操作。但這個(gè)基本操作類(lèi)型的生存時(shí)間非常短,在相應(yīng)的函數(shù)調(diào)用完成后就會(huì)自動(dòng)銷(xiāo)毀,變回基本數(shù)據(jù)類(lèi)型。所以對(duì)其添加變量的操作是無(wú)效的。

JS的垃圾回收機(jī)制

首先科普一下GC是什么意思。GC是垃圾回收的英文縮寫(xiě)GC(Gabage collection)。(額,本人之前一直以為什么是高大上的東西....)

JavaScript有自動(dòng)垃圾回收機(jī)制,也就是說(shuō)執(zhí)行環(huán)境會(huì)負(fù)責(zé)管理代碼執(zhí)行過(guò)程中使用的內(nèi)存,在開(kāi)發(fā)過(guò)程中就無(wú)需考慮內(nèi)存回收問(wèn)題。

JavaScript垃圾回收的機(jī)制很簡(jiǎn)單:找出不再使用的變量,然后釋放掉其占用的內(nèi)存,但是這個(gè)過(guò)程不是實(shí)時(shí)的,因?yàn)槠溟_(kāi)銷(xiāo)比較大,所以垃圾回收器會(huì)按照固定的時(shí)間間隔周期性的執(zhí)行。

JS的內(nèi)存回收機(jī)制一般有兩種實(shí)現(xiàn)方式,分別是"標(biāo)記清除""和"引用計(jì)數(shù)""方式。

其中"標(biāo)記清除"是最常見(jiàn)的回收方式,當(dāng)某變量進(jìn)入到執(zhí)行上下文(作用域)時(shí)。JS引擎會(huì)將該變量標(biāo)記為“進(jìn)入環(huán)境”狀態(tài),當(dāng)該環(huán)境所在的執(zhí)行上下文(作用域)執(zhí)行完畢時(shí)。里面的變量將被標(biāo)記為“離開(kāi)環(huán)境”狀態(tài)。然后當(dāng)JS引擎周期性地檢測(cè)內(nèi)存時(shí)。會(huì)將標(biāo)記為“離開(kāi)環(huán)境”的變量所占內(nèi)存清空。以起到回收內(nèi)存的目的。

另外一種垃圾回收實(shí)現(xiàn)方式是"引用計(jì)數(shù)"。即JS引擎會(huì)跟蹤每一個(gè)變量的被引用次數(shù)。當(dāng)被其他變量引用時(shí),計(jì)數(shù)就加一。但引用結(jié)束后,就將計(jì)數(shù)減一。當(dāng)引用次數(shù)為0時(shí),進(jìn)行內(nèi)存回收。但這種方法有可能會(huì)導(dǎo)致內(nèi)存的泄露。示例代碼

在日常開(kāi)發(fā)中,我們可以通過(guò)將變量的值設(shè)置為null的方法,將該變量的引用計(jì)數(shù)清為0。主動(dòng)回收內(nèi)存資源

作用域鏈介紹及其實(shí)現(xiàn)原理 前言的前言
作用域是JS中相當(dāng)重要的一部分內(nèi)容。是理解JS其他高級(jí)內(nèi)容(如:閉包,this指針,自執(zhí)行函數(shù))的基礎(chǔ)。如果能把JS作用域的原理及其應(yīng)用理解透,那相當(dāng)于已經(jīng)拿下半個(gè)JS了。
前言

首先,我們先來(lái)看這么一段代碼。示例代碼。這段代碼很簡(jiǎn)單,相信大家都能猜到預(yù)期的結(jié)果,外部函數(shù)不能訪問(wèn)內(nèi)部函數(shù)定義的變量,所以最后一個(gè)輸出elem2 is not defined。但是,有同學(xué)好奇過(guò)為什么外部函數(shù)就不能訪問(wèn)內(nèi)部函數(shù)的變量嗎?其底層的實(shí)現(xiàn)原理是什么?

作用域及作用域鏈介紹

在介紹上面的問(wèn)題之前,我們先來(lái)介紹作用域這個(gè)概念。

作用域的定義

定義什么的我們就直接略過(guò)了,我們用人話來(lái)說(shuō):是指函數(shù)中定義過(guò)的內(nèi)容的集合,即定義了的全部?jī)?nèi)容加在一起就是作用域(除了程序員主動(dòng)定義的,還有程序內(nèi)部幫我們定義的變量,例如函數(shù)中arguments變量)。

作用域有這么幾個(gè)特點(diǎn)

每定義一個(gè)函數(shù),都會(huì)產(chǎn)生一個(gè)對(duì)應(yīng)的作用域

作用域的層級(jí)關(guān)系與函數(shù)定義時(shí)所處的層級(jí)關(guān)系相同

各層作用域間通過(guò)單向鏈?zhǔn)浇Y(jié)構(gòu)組織起來(lái),形成作用域鏈,鏈接方向?yàn)橛蓛?nèi)向外。

變量的訪問(wèn)是從當(dāng)前所在作用域起,沿著不斷向外訪問(wèn),直到訪問(wèn)到為止,所以作用域間的訪問(wèn)范圍是單向的,是內(nèi)部作用域可以訪問(wèn)外部作用域。

結(jié)論:所以在上面的例子中,外部函數(shù)不能訪問(wèn)內(nèi)部函數(shù)的變量

彩蛋

相信大家在看書(shū)的時(shí)候,經(jīng)常會(huì)看到“作用域鏈”,“執(zhí)行環(huán)境”,“執(zhí)行上下文”這樣的字眼。其實(shí)這三個(gè)詞匯的意思是一樣的。都是指在執(zhí)行這段代碼(通常以函數(shù)的形式存在)的時(shí)候,所能訪問(wèn)的資源集合。

很簡(jiǎn)單,對(duì)吧。但這個(gè)概念后面會(huì)反復(fù)用到。

閉包 前言

講閉包之前,我們先來(lái)看一段示例代碼。大家覺(jué)得這段代碼的運(yùn)行結(jié)果是什么呢?

解析

如控制臺(tái)所示,log出來(lái)的結(jié)果是“elem a”。funA函數(shù)已經(jīng)執(zhí)行完了。elemA這個(gè)變量應(yīng)該會(huì)被回收釋放啊?那么為什么還會(huì)輸出a呢?我們使用JS的其中一種回收機(jī)制“引用計(jì)數(shù)”發(fā)來(lái)分析一下。

我們把變量elemA的內(nèi)存記為memoryA。memoryA首先會(huì)被elemA引用,引用計(jì)數(shù)為1。

在funB函數(shù)中使用了elemA,memoryA引用計(jì)數(shù)加一,為2

funA執(zhí)行完畢?;厥說(shuō)unA中的elemA。memoryA引用計(jì)數(shù)減一。還剩1,不為零。所以JS回收機(jī)制不會(huì)回收memoryA的內(nèi)存空間。所以在執(zhí)行c()實(shí)際是執(zhí)行funB的時(shí)候。還能訪問(wèn)到memoryA的內(nèi)存。所以輸出為"elem a"。

像這種函數(shù)本身已執(zhí)行完,但由于其內(nèi)部變量仍被使用。而得不到釋放的現(xiàn)象。我們就稱(chēng)作為“閉包”。那么怎么釋放該內(nèi)存呢?繼續(xù)減少其引用計(jì)數(shù)即可。就如代碼中所示。執(zhí)行funB函數(shù)。memoryA的引用計(jì)數(shù)再減一。為0?;厥諜C(jī)制即可回收該內(nèi)存。

我們可以通過(guò)作用域鏈的角度,使用“標(biāo)記清除”法再分析一遍。在定義elemA的時(shí)候,elemA標(biāo)記為“進(jìn)入環(huán)境”(即進(jìn)入其本身的作用域)。在funA執(zhí)行完成后,原本其作用域需要回收的,但是由于funB使用到elemA變量。所以funA的作用域沒(méi)能回收,所以elemA仍在“作用域鏈中”,沒(méi)能被標(biāo)記為“離開(kāi)環(huán)境”。所以沒(méi)能被釋放。

接下來(lái),我們通過(guò)一些小練習(xí)強(qiáng)化一下對(duì)閉包的理解。示例代碼

為什么輸出的兩遍都是2?我們來(lái)分析一下。

setTimeout函數(shù)里面的i是在那個(gè)作用域里面?是在setTimeout函數(shù)外部的test函數(shù)里面。所以這里形成了閉包。即使test執(zhí)行完畢,i變量也不會(huì)被釋放。

setTimeout函數(shù)和test函數(shù)那個(gè)函數(shù)先被執(zhí)行?很明顯setTimeout中的函數(shù)是后于test函數(shù)執(zhí)行的。所以當(dāng)setTimeout函數(shù)執(zhí)行的時(shí)候。i以及被自增為2了。所以?xún)蛇叺妮敵鰹?。

再來(lái)一個(gè)更難的。我們結(jié)合作用域和閉包來(lái)看一道題示例代碼

這不是形成了閉包嗎?為什么輸出的是2而不是3呢?

我們回過(guò)頭看一下作用域鏈的定義。函數(shù)的作用域?qū)蛹?jí)是在定義函數(shù)的時(shí)候就被固定下來(lái)的,與函數(shù)定義時(shí)的層級(jí)關(guān)系相同。所以funA和funB的作用域是處于同一級(jí)的。不存在閉包關(guān)系。所以funA中的輸出的A的值是外部定義的A的值。所以是2而不是3。

this指針 this指針的介紹

什么是this指針。

this指針是指向某個(gè)對(duì)象空間的指針,一般情況下是指向(和強(qiáng)類(lèi)型語(yǔ)言一樣)定義當(dāng)前作用域所在的對(duì)象示例代碼。如示例代碼所示,logName函數(shù)中的this指針指向了objA對(duì)象(logName的作用域是在objA對(duì)象中定義的)。所以輸出了aaa。

接著看上面的示例代碼,我們?cè)诤竺娑x了一個(gè)沒(méi)有l(wèi)ogName函數(shù)的對(duì)象objB,但沒(méi)有定義logName函數(shù)。那需要輸出objB中的name要這么辦呢?我們能不能向objA對(duì)象借用一下logName函數(shù)?讓objA里面的this指針指向objB對(duì)象呢?

JS中this指針的特點(diǎn)

不同于強(qiáng)語(yǔ)言的是,JS中的this指針是可以通過(guò)修改,動(dòng)態(tài)變化的。我們可以通過(guò)修改this指針來(lái)實(shí)現(xiàn)上面的需求。如示例代碼。

比較兩個(gè)例子,可以發(fā)現(xiàn),第二個(gè)代碼多加了bind(objB)。是這里改變了this指針的指向嗎?是的。其實(shí)在JS中。有三個(gè)函數(shù)可以改變this指針的指向。分別是bind,call,和apply函數(shù)。

bind,call,apply的介紹和比較

先來(lái)看看這三個(gè)函數(shù)的使用示例示例代碼。

首先,三個(gè)里面,最突出的函數(shù)就是bind。為什么唯獨(dú)bind函數(shù)不是直接使用。還需要在外面添加一個(gè)setTimeout呢?如果不加setTimeout會(huì)怎么樣呢?示例代碼。在不加setTimeout的時(shí)候沒(méi)有輸出任何東西。而加了setTimeout才會(huì)輸出。其實(shí)那是因?yàn)槭褂胋ind的時(shí)候,是僅僅修改this指針,并不會(huì)執(zhí)行函數(shù)。在setTimeout中,計(jì)時(shí)后,才由setTimeout去調(diào)用執(zhí)行這個(gè)函數(shù)。

接著,call和apply直接執(zhí)行了函數(shù)。他們的區(qū)別是什么呢?他們唯一的區(qū)別是傳參方式的不同,call函數(shù)以枚舉(即直接列出來(lái))的方式傳參。而apply是以數(shù)組的方式傳參的。我們?cè)囈幌抡{(diào)轉(zhuǎn)過(guò)來(lái)傳參示例代碼

彩蛋,除了使用bind來(lái)修改this指針以外。我們還可以使用它來(lái)返回一個(gè)函數(shù),往后再去執(zhí)行。示例代碼。其實(shí)這個(gè)實(shí)例中并沒(méi)什么卵用,只是這個(gè)實(shí)例說(shuō)明兩個(gè)問(wèn)題。

當(dāng)bind函數(shù)的第一個(gè)參數(shù)傳null時(shí),表示不改變函數(shù)中this的指向。

當(dāng)函數(shù)前面的參數(shù)已經(jīng)被賦值時(shí),再使用bind時(shí),是從剩余沒(méi)有賦值的函數(shù)參數(shù)開(kāi)始賦值的。

this指針的應(yīng)用

將this的指針指向正確的值。這個(gè)內(nèi)容后面再講。

利用this指針借用某對(duì)象的函數(shù),前面的幾個(gè)例子就是利用了this指針借用函數(shù)。在介紹更多例子之前,我們先插入一個(gè)偽數(shù)組的概念

偽數(shù)組是指哪些只有l(wèi)ength和中括索引功能,沒(méi)有數(shù)組相應(yīng)功能函數(shù)的數(shù)據(jù)結(jié)構(gòu)。例如示例代碼。如這個(gè)例子中pList1就沒(méi)有slice的函數(shù)。

在前端中最經(jīng)常接觸到的偽數(shù)組有函數(shù)中的隱藏變量 arguments 和用 getElementsByTagName獲得到的元素集合。

這時(shí)我們就可以利用到this指針去借用數(shù)組中的函數(shù),實(shí)現(xiàn)我們想要的目的。示例代碼

另外怎么把偽數(shù)組轉(zhuǎn)化為真數(shù)組呢?其實(shí)只要在上一個(gè)例子上再修改一下就可以了。示例代碼

在此基礎(chǔ)上,我們還可以利用this指針實(shí)現(xiàn)一部分類(lèi)功能。示例代碼

this指針與作用域的關(guān)系(主要是window)

其實(shí)如果沒(méi)有該死的window對(duì)象的話,原本this指針和作用域是沒(méi)有太大關(guān)系的

先看代碼,示例代碼。?。。。烤尤惠敵龅氖荋anMeiMei而不是LiLei?為什么會(huì)輸出HanMeiMei?這是不是意味著this指針發(fā)生變化了?我們log一下this指針的值.

示例代碼。果然,this指針真的是指向了window??WTF?我們明明沒(méi)有修改過(guò)this指針的值啊?為什么this的指向改變了?

在這里就要補(bǔ)充一下的this指針的定義了。上面講到(一般情況下是指向定義當(dāng)前作用域時(shí)所在的對(duì)象,即在那個(gè)對(duì)象內(nèi)定義就是指向誰(shuí)。)。但實(shí)際上,this是指向通過(guò)點(diǎn)操作符(如objA.funA())調(diào)用本函數(shù)的那個(gè)對(duì)象。即誰(shuí)調(diào)用我,我就指向誰(shuí)。示例代碼如在這里,objB并沒(méi)有定義logName函數(shù)。只是定義了一個(gè)變量并賦值為函數(shù)的引用。這時(shí)使用objB.logName實(shí)際上調(diào)用的是objA對(duì)象里面的函數(shù)。而log出來(lái)的對(duì)象就是objB。

再回到setTimeout函數(shù)。我們前面log過(guò)this,發(fā)現(xiàn)this是指向window的。這證明在setTimeout中的是window對(duì)象去調(diào)用logName的。即相當(dāng)于window.logName();發(fā)現(xiàn)問(wèn)題了嗎?HanMeiMei既是作用域中的變量,也是window對(duì)象中的變量。

實(shí)際上,所有在全局作用域(注意僅僅是全局作用域)中定義的變量都是window對(duì)象下的變量。都可以通過(guò)window對(duì)象進(jìn)行訪問(wèn)。所以一旦沒(méi)有通過(guò)對(duì)象去調(diào)用某函數(shù),而是直接運(yùn)行的話(如不是objA.fun()而是直接fun()),等價(jià)于在window下調(diào)用(fun()等價(jià)于window.fun())。this指針的值都指向window。

再由于所有在全局作用域(注意僅僅是全局作用域)中定義的變量都是window對(duì)象下的變量所以logName中的值指指向了window.name。也就是作用域中的name值。作用域就是通過(guò)window與this指針掛上了關(guān)系。

this指針和閉包的比較

這是一個(gè)性能優(yōu)化的探討問(wèn)題。

有時(shí)候我們會(huì)遇到這樣的情況。使用閉包和this指針都可以實(shí)現(xiàn)同樣的功能。例如示例代碼。使用這兩種方式均可以成功log出name的值。那這時(shí)候我們使用哪個(gè)好呢?

由于log函數(shù)會(huì)輸出到控制臺(tái),執(zhí)行速度慢,我們通過(guò)修改name的值來(lái)模擬內(nèi)部操作示例代碼可以看到。直接使用閉包的性能更佳。性能是使用bind的5倍以上。由于時(shí)間原因。原理我就不再這里介紹了。有興趣的同學(xué)可以通過(guò)這個(gè)鏈接看看.為什么閉包比this更快

至此,本教程中,最困難的兩個(gè)點(diǎn)(this指針與JS線程)中的其中一個(gè)已經(jīng)介紹完了。接下來(lái)休息一下,穿插幾個(gè)比較簡(jiǎn)單的概念。

自執(zhí)行函數(shù) 什么是自執(zhí)行函數(shù)

這個(gè)術(shù)語(yǔ)看起來(lái)很高大上。其實(shí)說(shuō)到底就是一個(gè)定義完了馬上就執(zhí)行的函數(shù)。其實(shí)這不是JS的新特性。而是利用JS特性做出來(lái)的效果。即JS特性的巧妙應(yīng)用。它的表現(xiàn)形式是這樣的。示例代碼.其中第一個(gè)()是用于隔離第二個(gè)括號(hào),使其function(){}函數(shù)定義完,避免語(yǔ)法錯(cuò)誤。而第二個(gè)()是為了執(zhí)行剛剛定義好的函數(shù)。

自執(zhí)行函數(shù)的作用

第一個(gè),也是百度答案最多的一個(gè)避免污染全局作用域。

到底是怎么污染全局作用域呢?示例代碼。這里假設(shè)調(diào)用了兩個(gè)JS的情況,原本LaoWang要向HanMeiMei表白的,但是由于第二個(gè)文件中,name的值被修改成了LiLei,導(dǎo)致LaoWang表錯(cuò)白。恩,小明是爽了。隔壁老王就糟了。

那怎么用自執(zhí)行函數(shù)來(lái)解決呢?很簡(jiǎn)單,用自執(zhí)行函數(shù)把每個(gè)人自己寫(xiě)的JS代碼包裹起來(lái)就可以了;示例代碼.

原理:利用到了作用域的概念。由于每自執(zhí)行函數(shù)本身形成了一個(gè)作用域。而這兩個(gè)作用域是處于同一級(jí)的?;ゲ挥绊懀瑥亩苊饬宋廴救肿饔糜?。這個(gè)技巧在多人協(xié)作的項(xiàng)目里面很有用。

第二個(gè),也是用得比較少的一個(gè)構(gòu)建私有屬性。

使用強(qiáng)類(lèi)型語(yǔ)言的同學(xué)應(yīng)該都知道私有的概念。而在JS中,沒(méi)有類(lèi)的概念(在ES5之前是沒(méi)有的,但ES6新增了類(lèi)的概念),從而也沒(méi)有了私有屬性。但我們可以利用自執(zhí)行函數(shù)構(gòu)建一個(gè)。示例代碼。這樣就避免了使用直接調(diào)用name變量。實(shí)現(xiàn)了私有屬性的功能。

原理:前面對(duì)閉包及作用域理解了的同學(xué),相信你們已經(jīng)可以猜測(cè)背后的原理了。這里使用到了閉包(使得name不被回收),作用域(使得返回的對(duì)象中的函數(shù)能訪問(wèn)name變量),已經(jīng)自執(zhí)行函數(shù)(沒(méi)有留下函數(shù)的引用,使得外界不能訪問(wèn))的特點(diǎn)。

聲明提前 前言

人們常說(shuō)JS是解釋性語(yǔ)言,語(yǔ)句都是執(zhí)行到哪里才進(jìn)行解析的。但實(shí)際上真的是這樣嗎?

什么是聲明提前

JS聲明提前是指,在作用域中(即在函數(shù)中啦),變量的聲明總是優(yōu)先于其他語(yǔ)句被執(zhí)行的。(即總是先執(zhí)行聲明語(yǔ)句,再執(zhí)行其他語(yǔ)句)。示例代碼

但如果把var name;改成var name = "LiLei"又會(huì)怎么樣呢?示例代碼。說(shuō)好的提前呢?其實(shí)JS引擎在解析這段代碼的時(shí)候會(huì)把var name = "LiLei"分成兩條語(yǔ)句來(lái)執(zhí)行。一個(gè)是變量定義,一個(gè)是變量賦值。所以實(shí)際上登記于這樣示例代碼

聲明對(duì)于函數(shù)同樣適用示例代碼。而且注意點(diǎn)也是一樣的。示例代碼

什么時(shí)候容易出現(xiàn)錯(cuò)誤

講這個(gè)點(diǎn)之前,先插入兩個(gè)待會(huì)要用到的概念

沒(méi)有塊級(jí)作用域

在JS中是沒(méi)有塊級(jí)作用域的概念的。for,while等控制語(yǔ)句不構(gòu)成塊級(jí)作用域。示例代碼

沒(méi)有通過(guò)var定義的變量均是定義全局變量

如標(biāo)題所說(shuō)。 沒(méi)有通過(guò)var定義的變量都是全局變量,都在全局作用域中找得到。示例代碼。當(dāng)這兩個(gè)概念在加載聲明提前上就很惡心了。

例子示例代碼。這個(gè)例子比較特殊,在chrome中可能回報(bào)錯(cuò)。建議大家用其他瀏覽器打開(kāi)。我這里使用safari打開(kāi)。執(zhí)行結(jié)果是HanMeiMei。給大家一點(diǎn)時(shí)間整理一下思路。其實(shí)它等價(jià)于。

當(dāng)然這些例子都比較極端。而且看起來(lái)很傻。其實(shí)只是想告訴大家。當(dāng)大家在看法中遇到很奇怪的bug的時(shí)候??梢钥紤]是不是由于聲明提前引起的了。

JS線程問(wèn)題 前言

這是前面提到過(guò)在本教程中,最復(fù)雜的兩個(gè)知識(shí)點(diǎn)(this指針與JS線程)中的JS線程問(wèn)題。雖然我們?nèi)粘i_(kāi)發(fā)中可能不會(huì)主動(dòng)提到這個(gè)概念,但其實(shí)我們經(jīng)常會(huì)用到。譬如ajax異步請(qǐng)求,事件處理,計(jì)時(shí)器延遲執(zhí)行等都涉及到JS線程的概念。學(xué)習(xí)好本概念雖然不會(huì)像this指針那樣解決很多問(wèn)題,但有助于加深我們對(duì)JS底層的理解。有助于理清邏輯關(guān)系。

什么是線程

首先,我們先來(lái)了解一下什么是線程。百度一下。恩,第一句進(jìn)程什么的我們就略過(guò),先不管啦。重點(diǎn)是后面那句。線程是程序執(zhí)行流的最小單元。用人話翻譯就是說(shuō),一次只能執(zhí)行一段代碼的執(zhí)行器。同一時(shí)間內(nèi)只能完成一項(xiàng)任務(wù)。后一項(xiàng)任務(wù)必須等待前面的任務(wù)執(zhí)行完成才能被執(zhí)行。

單線程語(yǔ)言,顧名思義,是指一次只能執(zhí)行一項(xiàng)任務(wù)的語(yǔ)言。

多線程語(yǔ)言,是指一次能執(zhí)行多項(xiàng)任務(wù)的語(yǔ)言。典型的多線程語(yǔ)言有C,C++等強(qiáng)類(lèi)型語(yǔ)言。

JS是單線語(yǔ)言。介紹到這里的時(shí)候,不知道小伙伴們會(huì)不會(huì)有這樣的疑惑。不對(duì)啊,JS能執(zhí)行異步操作(例如AJAX操作)的啊,異步操作不就是允許后面的代碼先執(zhí)行嗎?這和單線程的概念相沖突啊。JS怎么會(huì)是單線程的呢?恩,我們帶著這個(gè)問(wèn)題往下看。

頁(yè)面加載流程解析

為什么js文件要放在body標(biāo)簽的底部,而不建議放在頭部。因?yàn)楫?dāng)JS文件加載過(guò)程太慢的時(shí)候,會(huì)阻礙后面標(biāo)簽的執(zhí)行。恩,這個(gè)知識(shí)點(diǎn)相信大家都知道。但為什么JS文件的加載會(huì)影響HTML標(biāo)簽的解析呢?它底層的原理是什么呢?

先問(wèn)大家一個(gè)小問(wèn)題,你知道在HTML頁(yè)面里面,怎么樣做到不引人script標(biāo)簽去執(zhí)行JS代碼?

先拋出兩個(gè)概念,瀏覽器的核心是兩部分:渲染引擎和JavaScript解釋器(又稱(chēng)JavaScript引擎)。

其中渲染引擎負(fù)責(zé)解析HTML標(biāo)簽,將標(biāo)簽轉(zhuǎn)化為HTML視圖。渲染引擎處理頁(yè)面,通常分成四個(gè)階段

解析代碼:HTML代碼解析為DOM,CSS代碼解析為CSSOM(CSS Object Model)

對(duì)象合成:將DOM和CSSOM合成一棵渲染樹(shù)(render tree)

布局:計(jì)算出渲染樹(shù)的布局(layout)

繪制:將渲染樹(shù)繪制到屏幕

JavaScript引擎的主要作用是,讀取網(wǎng)頁(yè)中的JavaScript代碼,對(duì)其處理后運(yùn)行。

渲染引擎和JS引擎分別使用不同的線程

其實(shí)整個(gè)HTML頁(yè)面的加載過(guò)程是這樣的。

瀏覽器一邊下載HTML網(wǎng)頁(yè),一邊開(kāi)始解析。

解析過(guò)程中,發(fā)現(xiàn)script標(biāo)簽。

暫停解析,網(wǎng)頁(yè)渲染的控制權(quán)轉(zhuǎn)交給JavaScript引擎。

如果script標(biāo)簽引用了外部腳本,就下載外部腳本,否則就直接執(zhí)行腳本。

執(zhí)行完畢,控制權(quán)交還渲染引擎,恢復(fù)往下解析HTML網(wǎng)頁(yè)

也就是說(shuō),加載外部腳本時(shí),瀏覽器會(huì)暫停頁(yè)面渲染,等待腳本下載并執(zhí)行完成后,再繼續(xù)渲染。既然渲染引擎和JS引擎是用不同的線程去執(zhí)行的。那應(yīng)該可以并行執(zhí)行啊。例如渲染線程繼續(xù)解析標(biāo)簽,JS引擎去執(zhí)行JS語(yǔ)句。為什么不這樣做呢?

原因是JavaScript可以修改DOM(比如使用document.write方法),所以必須把控制權(quán)讓給它,否則會(huì)導(dǎo)致復(fù)雜的線程競(jìng)賽的問(wèn)題。(例如同時(shí)對(duì)同一個(gè)DIV進(jìn)行修改,那以那個(gè)為準(zhǔn)?)

彩蛋,恩,回答前面問(wèn)的那個(gè)問(wèn)題。其實(shí)在html頁(yè)面中,在標(biāo)簽里面設(shè)置的每一個(gè)事件。都是一個(gè)JS執(zhí)行段。都會(huì)以事件回調(diào)的方式執(zhí)行里面的代碼。示例代碼。

JS事件循環(huán)(Event Loop)

注意,這里講的是JS的事件循環(huán)機(jī)制,不是JS的事件傳遞機(jī)制,通過(guò)本節(jié)的學(xué)習(xí),你將明白JS異步是怎么實(shí)現(xiàn)的.

首先,需要明確一個(gè)概念:JS是單線程的,但并不意味著瀏覽器也是單線程的。實(shí)際上瀏覽器是多線程的(例如前面講到的渲染引擎就是其中一個(gè)線程),JS通過(guò)和瀏覽器后臺(tái)線程的配合,實(shí)現(xiàn)了JS的事件機(jī)制。

我們以示例代碼為例。JS的執(zhí)行流程是這樣的。

首先JS在底層維護(hù)了一個(gè)是消息隊(duì)列的隊(duì)列。

JS執(zhí)行到addEventListener時(shí),將回調(diào)函數(shù)的地址交給監(jiān)聽(tīng)頁(yè)面操作的其中一個(gè)瀏覽器后臺(tái)線程(假設(shè)叫做監(jiān)聽(tīng)線程)。

當(dāng)監(jiān)聽(tīng)的事件被觸發(fā)的時(shí)候,監(jiān)聽(tīng)線程將之前JS交代的回調(diào)函數(shù)地址放入到消息隊(duì)列當(dāng)中。

重點(diǎn)來(lái)了,JS引擎是怎么讀取消息列表的呢?JS引擎是先把JS同步操作全部執(zhí)行完畢(即JS文件中的代碼全部執(zhí)行完畢,我們先用“主邏輯操作”),才會(huì)去按順序調(diào)用消息隊(duì)列的回調(diào)函數(shù)地址。而且這個(gè)從消息隊(duì)列中調(diào)用回調(diào)函數(shù)的過(guò)程是循環(huán)執(zhí)行的。

從上面的分析中,我們可以得到以下結(jié)論:

全部回調(diào)操作都在主邏輯操作完成后才被執(zhí)行的.

由于消息隊(duì)列是以隊(duì)列的形式保存起來(lái)的。而隊(duì)列本身是一個(gè)先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),所以會(huì)優(yōu)先調(diào)用隊(duì)列排在前面的回調(diào)函數(shù),(由于JS的執(zhí)行是單線程的,一次只能執(zhí)行一個(gè)代碼段)所以只有前一個(gè)回調(diào)函數(shù)被執(zhí)行完了,第二個(gè)回調(diào)函數(shù)才能被執(zhí)行。所以回調(diào)函數(shù)的執(zhí)行順序是在被加入到消息隊(duì)列的那一刻決定的。

另外,除了前面提到的監(jiān)聽(tīng)線程,瀏覽器還有處理定時(shí)器的進(jìn)程(如setTimeout)、處理用戶(hù)輸入的進(jìn)程(input)、處理網(wǎng)絡(luò)通信的進(jìn)程(AJAX)等等

setTimeout問(wèn)題

同樣的setTimeout函數(shù)也可以使用上面的過(guò)程進(jìn)行分析。只不過(guò)把上面的監(jiān)聽(tīng)線程換成處理定時(shí)器的瀏覽器線程(假設(shè)叫做定時(shí)器線程)。即

JS執(zhí)行到setTimeout時(shí),將回調(diào)函數(shù)的地址交給定時(shí)器線程。

定時(shí)間線程進(jìn)行計(jì)時(shí),當(dāng)計(jì)時(shí)結(jié)束后。定時(shí)器線程將所攜帶的回調(diào)函數(shù)地址放入消息隊(duì)列中。

JS引擎把主邏輯執(zhí)行完成后,調(diào)用消息隊(duì)列中的回調(diào)函數(shù)。

前面兩步都沒(méi)有問(wèn)題,但到最后一步就可能會(huì)出現(xiàn)問(wèn)題了。

由于定時(shí)器線程和JS引擎是使用不同線程,同時(shí)進(jìn)行的。而JS引擎去讀取消息隊(duì)列之前需要先將主操作執(zhí)行完。那么一旦主操作的執(zhí)行時(shí)間大于定時(shí)器的計(jì)時(shí)時(shí)間。那么回調(diào)函數(shù)的時(shí)間等待時(shí)間將大于程序所設(shè)置的計(jì)時(shí)時(shí)間。示例代碼

另外,除了主操作會(huì)延遲計(jì)時(shí)器的回調(diào)函數(shù)執(zhí)行。由于JS引擎在讀取消息隊(duì)列的時(shí)候是按順序讀取的。這意味著排在前面的回調(diào)函數(shù)也可能會(huì)推遲排在后面的計(jì)時(shí)器回調(diào)函數(shù)的執(zhí)行示例代碼

插播一個(gè)知識(shí)HTML5標(biāo)準(zhǔn)規(guī)定了setTimeout()的第二個(gè)參數(shù)的最小值(最短間隔),不得低于4毫秒,如果低于這個(gè)值,就會(huì)自動(dòng)增加。在此之前,老版本的瀏覽器都將最短間隔設(shè)為10毫秒。示例代碼。當(dāng)然了,實(shí)際運(yùn)行時(shí)間還需要結(jié)合電腦本身的運(yùn)行情況。再加上console.log本身需要消耗一定的時(shí)間。所以每次的運(yùn)行時(shí)間可能都會(huì)有變化。在這是也只是告訴大家會(huì)有這個(gè)規(guī)定。

setTimeout的妙用。

除了平常的計(jì)時(shí)作用,我們還可以利用消息隊(duì)列必須在主邏輯之后被執(zhí)行的特點(diǎn),結(jié)合setTimeout把某段代碼放在最后執(zhí)行(即主邏輯之后)。示例代碼setTimeout(fn,0)的含義是,指定某個(gè)任務(wù)在主線程最早可得的空閑時(shí)間執(zhí)行。

HTML5新內(nèi)容:worker介紹

web Worker這是H5新出的一個(gè)內(nèi)容,使用這個(gè)API,我們可以簡(jiǎn)單地創(chuàng)建后臺(tái)運(yùn)行的線程。但JS本質(zhì)上還是單線程的。

我們先來(lái)看看他的基本使用方法示例代碼。注意,worker的調(diào)用是異步的。所以after post優(yōu)先執(zhí)行。

需要注意的是worker只能進(jìn)行數(shù)據(jù)操作,不能調(diào)用DOM,和BOM。它里面連window對(duì)象都沒(méi)有。這個(gè)我就不進(jìn)行深入講解的。這些都可以在網(wǎng)上找得到。我想把哪些不怎么容易找得到的內(nèi)容給大家講解一下。

worker實(shí)現(xiàn)的不是真正意義上的線程,它完全受主線程所控制的。示例代碼【注意,這里執(zhí)行將執(zhí)行死循環(huán),小伙伴們根據(jù)自己的情況考慮要不要運(yùn)行】??梢钥吹剑婚_(kāi)始worker可以被正常執(zhí)行,但當(dāng)JS主線程被的死循環(huán)執(zhí)行的時(shí)候,worker馬上停止了工作,貌似JS和worker同一時(shí)間只能執(zhí)行一個(gè)。而真正的多線程運(yùn)行結(jié)果應(yīng)該是script和worker隨機(jī)交替出現(xiàn)的。(其實(shí)這里的分析是不夠全面的)

前面只給到JS主線程貌似阻塞了worker的線程。那么反過(guò)來(lái)。worker會(huì)不會(huì)阻塞JS主線程的操作呢?我們來(lái)看示例代碼。【注意,這里也是死循環(huán)的】(其實(shí)大家應(yīng)該已經(jīng)猜到了運(yùn)行的結(jié)果,如果worker會(huì)阻塞JS主邏輯的操作,那要worker還有什么用?)可以看到,worker線程并不會(huì)阻塞JS主線程的執(zhí)行。這意味著worker很有用。我們可以將一些很耗時(shí)的操作放到worker中執(zhí)行。保證主頁(yè)面的流暢運(yùn)行。譬如對(duì)大型的圖片或附件進(jìn)行壓縮,加密操作等。參考網(wǎng)站,這是一個(gè)分解質(zhì)因數(shù)的網(wǎng)站,之前高等數(shù)學(xué)的老師告訴我們。分解質(zhì)因數(shù)的難度復(fù)雜度是O(n1/4)。這意味著整個(gè)分解過(guò)程是很耗時(shí)間的。而這個(gè)頁(yè)面使用了worker在后臺(tái)進(jìn)行分解。所以在等待的過(guò)程中,頁(yè)面是不卡的。

worker的本質(zhì)分析

我們來(lái)分析一下worker的本質(zhì)是什么,其實(shí)worker本質(zhì)上和瀏覽器的計(jì)時(shí)器線程等是沒(méi)有區(qū)別的。是瀏覽器新建立的一個(gè)線程,用于執(zhí)行特定的任務(wù)。

而worker實(shí)際上和JS主線程是可以同步執(zhí)行的,是可以組成多線段的。之前貌似JS阻塞了worker的原因,只是因?yàn)閏onsole對(duì)象不能同時(shí)被多個(gè)線程所擁有。而JS主線程的優(yōu)先級(jí)比worker高,所以從worker中搶走了console對(duì)象的控制器。造成了這個(gè)現(xiàn)象發(fā)生。

而第二個(gè)例子則證明了worker和JS主線程的并行性。因?yàn)樵谧鲂薷臄?shù)字的操作時(shí)。console的輸出沒(méi)有被打斷。

所以證明worker是可以實(shí)現(xiàn)在數(shù)據(jù)計(jì)算上的多線程。但由于它本身不完全具備JS的全部功能,如不能操作DOM,BOM。所以普遍被認(rèn)為worker實(shí)現(xiàn)的是ECMAScript的多線程,JS從本質(zhì)上是單線程的。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/81870.html

相關(guān)文章

  • JavaScript進(jìn)階之路

    摘要:前端入門(mén)的門(mén)檻相對(duì)較低,學(xué)習(xí)曲線是越來(lái)越陡峭,由淺入深,可以分為四個(gè)階段。第二階段高級(jí)程序設(shè)計(jì)有的書(shū)是用來(lái)成為經(jīng)典的,比如犀牛書(shū)還有些書(shū)是用來(lái)超越經(jīng)典的,顯然這本書(shū)就是。接下來(lái)可以看看教程,看看源代碼,嘗試著寫(xiě)一寫(xiě)這些效果。 前端入門(mén)的門(mén)檻相對(duì)較低,學(xué)習(xí)曲線是越來(lái)越陡峭,由淺入深,可以分為四個(gè)階段。 第一階段:《JavaScript DOM編程藝術(shù)》    看這本書(shū)之前,請(qǐng)先確認(rèn)你對(duì)J...

    Lowky 評(píng)論0 收藏0
  • 說(shuō)說(shuō)我的web前端之路,分享些前端的好書(shū)

    摘要:推薦高性能網(wǎng)站建設(shè)指南高性能網(wǎng)站建設(shè)進(jìn)階指南理由在讀完前幾本書(shū)之后我們對(duì)前端的性能和自己的代碼的效率已經(jīng)達(dá)到相當(dāng)?shù)母叨攘?,然后我們?cè)诮佑|一些前端工程師的一些精髓。   WEB前端研發(fā)工程師,在國(guó)內(nèi)算是一個(gè)朝陽(yáng)職業(yè),這個(gè)領(lǐng)域沒(méi)有學(xué)校的正規(guī)教育,大多數(shù)人都是靠自己自學(xué)成才。本文主要介紹自己從事web開(kāi)發(fā)以來(lái)(從大二至今)看過(guò)的書(shū)籍和自己的成長(zhǎng)過(guò)程,目的是給想了解JavaScript或者是剛...

    PascalXie 評(píng)論0 收藏0
  • 說(shuō)說(shuō)我的web前端之路,分享些前端的好書(shū)

    摘要:推薦高性能網(wǎng)站建設(shè)指南高性能網(wǎng)站建設(shè)進(jìn)階指南理由在讀完前幾本書(shū)之后我們對(duì)前端的性能和自己的代碼的效率已經(jīng)達(dá)到相當(dāng)?shù)母叨攘耍缓笪覀冊(cè)诮佑|一些前端工程師的一些精髓。   WEB前端研發(fā)工程師,在國(guó)內(nèi)算是一個(gè)朝陽(yáng)職業(yè),這個(gè)領(lǐng)域沒(méi)有學(xué)校的正規(guī)教育,大多數(shù)人都是靠自己自學(xué)成才。本文主要介紹自己從事web開(kāi)發(fā)以來(lái)(從大二至今)看過(guò)的書(shū)籍和自己的成長(zhǎng)過(guò)程,目的是給想了解JavaScript或者是剛...

    Integ 評(píng)論0 收藏0
  • 說(shuō)說(shuō)我的web前端之路,分享些前端的好書(shū)

    摘要:推薦高性能網(wǎng)站建設(shè)指南高性能網(wǎng)站建設(shè)進(jìn)階指南理由在讀完前幾本書(shū)之后我們對(duì)前端的性能和自己的代碼的效率已經(jīng)達(dá)到相當(dāng)?shù)母叨攘?,然后我們?cè)诮佑|一些前端工程師的一些精髓。   WEB前端研發(fā)工程師,在國(guó)內(nèi)算是一個(gè)朝陽(yáng)職業(yè),這個(gè)領(lǐng)域沒(méi)有學(xué)校的正規(guī)教育,大多數(shù)人都是靠自己自學(xué)成才。本文主要介紹自己從事web開(kāi)發(fā)以來(lái)(從大二至今)看過(guò)的書(shū)籍和自己的成長(zhǎng)過(guò)程,目的是給想了解JavaScript或者是剛...

    sugarmo 評(píng)論0 收藏0
  • JavaScript書(shū)籍測(cè)評(píng)

    摘要:前言今天和大家一起聊聊的推薦書(shū)籍,每一本都是精選,做前端開(kāi)發(fā)的朋友們?nèi)绻麤](méi)讀過(guò),可以嘗試一下。如果怕麻煩,也可以關(guān)注曉舟報(bào)告,發(fā)送獲取書(shū)籍,四個(gè)字,就可以得到電子書(shū)的提取碼。 前言 今天和大家一起聊聊JavaScript的推薦書(shū)籍,每一本都是精選,做前端開(kāi)發(fā)的朋友們?nèi)绻麤](méi)讀過(guò),可以嘗試一下。下面給大家簡(jiǎn)單介紹了書(shū)的內(nèi)容,還有讀書(shū)的方法,希望可以幫大家提升讀書(shū)效率。 一、《JavaScr...

    X1nFLY 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<