174道JavaScript 面試知識點(diǎn)總結(jié)(上)

來源 | https://github.com/CavsZhouyou/
1、介紹 js 的基本數(shù)據(jù)類型。
js 一共有六種基本數(shù)據(jù)類型,分別是 Undefined、Null、Boolean、Number、String,還有在 ES6 中新增的 Symbol 和 ES10 中新增的 BigInt 類型。Symbol 代表創(chuàng)建后獨(dú)一無二且不可變的數(shù)據(jù)類型,它的出現(xiàn)我認(rèn)為主要是為了解決可能出現(xiàn)的全局變量沖突的問題。BigInt 是一種數(shù)字類型的數(shù)據(jù),它可以表示任意精度格式的整數(shù),使用 BigInt 可以安全地存儲和操作大整數(shù),即使這個數(shù)已經(jīng)超出了 Number 能夠表示的安全整數(shù)范圍。
涉及知識點(diǎn):
棧:原始數(shù)據(jù)類型(Undefined、Null、Boolean、Number、String)
堆:引用數(shù)據(jù)類型(對象、數(shù)組和函數(shù))
兩種類型的區(qū)別是:存儲位置不同。原始數(shù)據(jù)類型直接存儲在棧(stack)中的簡單數(shù)據(jù)段,占據(jù)空間小、大小固定,屬于被頻繁使用數(shù)據(jù),所以放入棧中存儲。引用數(shù)據(jù)類型存儲在堆(heap)中的對象,占據(jù)空間大、大小不固定。如果存儲在棧中,將會影響程序運(yùn)行的性能;引用數(shù)據(jù)類型在棧中存儲了指針,該指針指向堆中該實(shí)體的起始地址。當(dāng)解釋器尋找引用值時,會首先檢索其在棧中的地址,取得地址后從堆中獲得實(shí)體。
回答:
js 可以分為兩種類型的值,一種是基本數(shù)據(jù)類型,一種是復(fù)雜數(shù)據(jù)類型。基本數(shù)據(jù)類型....(參考1)復(fù)雜數(shù)據(jù)類型指的是 Object 類型,所有其他的如 Array、Date 等數(shù)據(jù)類型都可以理解為 Object 類型的子類。兩種類型間的主要區(qū)別是它們的存儲位置不同,基本數(shù)據(jù)類型的值直接保存在棧中,而復(fù)雜數(shù)據(jù)類型的值保存在堆中,通過使用在棧中保存對應(yīng)的指針來獲取堆中的值。
詳細(xì)資料可以參考:?《JavaScript 有幾種類型的值?》?《JavaScript 有幾種類型的值?能否畫一下它們的內(nèi)存圖;》
3、什么是堆?什么是棧?它們之間有什么區(qū)別和聯(lián)系?
堆和棧的概念存在于數(shù)據(jù)結(jié)構(gòu)中和操作系統(tǒng)內(nèi)存中。在數(shù)據(jù)結(jié)構(gòu)中,棧中數(shù)據(jù)的存取方式為先進(jìn)后出。而堆是一個優(yōu)先隊(duì)列,是按優(yōu)先級來進(jìn)行排序的,優(yōu)先級可以按照大小來規(guī)定。完全二叉樹是堆的一種實(shí)現(xiàn)方式。在操作系統(tǒng)中,內(nèi)存被分為棧區(qū)和堆區(qū)。棧區(qū)內(nèi)存由編譯器自動分配釋放,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。堆區(qū)內(nèi)存一般由程序員分配釋放,若程序員不釋放,程序結(jié)束時可能由垃圾回收機(jī)制回收。
詳細(xì)資料可以參考:?《什么是堆?什么是棧?他們之間有什么區(qū)別和聯(lián)系?》
4、內(nèi)部屬性 [[Class]] 是什么?
所有 typeof 返回值為 "object" 的對象(如數(shù)組)都包含一個內(nèi)部屬性 [[Class]](我們可以把它看作一個內(nèi)部的分類,而非傳統(tǒng)的面向?qū)ο笠饬x上的類)。這個屬性無法直接訪問,一般通過 Object.prototype.toString(..) 來查看。例如:Object.prototype.toString.call( [1,2,3] );// "[object Array]"Object.prototype.toString.call( /regex-literal/i );// "[object RegExp]"// 我們自己創(chuàng)建的類就不會有這份特殊待遇,因?yàn)?toString() 找不到 toStringTag 屬性時只好返回默認(rèn)的 Object 標(biāo)簽// 默認(rèn)情況類的[[Class]]返回[object Object]class Class1 {}Object.prototype.toString.call(new Class1()); // "[object Object]"// 需要定制[[Class]]class Class2 {get [Symbol.toStringTag]() {return "Class2";}}Object.prototype.toString.call(new Class2()); // "[object Class2]"
涉及知識點(diǎn):
全局的對象( global objects )或稱標(biāo)準(zhǔn)內(nèi)置對象,不要和 "全局對象(global object)" 混淆。這里說的全局的對象是說在全局作用域里的對象。全局作用域中的其他對象可以由用戶的腳本創(chuàng)建或由宿主程序提供。標(biāo)準(zhǔn)內(nèi)置對象的分類(1)值屬性,這些全局屬性返回一個簡單值,這些值沒有自己的屬性和方法。例如 Infinity、NaN、undefined、null 字面量(2)函數(shù)屬性,全局函數(shù)可以直接調(diào)用,不需要在調(diào)用時指定所屬對象,執(zhí)行結(jié)束后會將結(jié)果直接返回給調(diào)用者。例如 eval()、parseFloat()、parseInt() 等(3)基本對象,基本對象是定義或使用其他對象的基礎(chǔ)。基本對象包括一般對象、函數(shù)對象和錯誤對象。例如 Object、Function、Boolean、Symbol、Error 等(4)數(shù)字和日期對象,用來表示數(shù)字、日期和執(zhí)行數(shù)學(xué)計(jì)算的對象。例如 Number、Math、Date(5)字符串,用來表示和操作字符串的對象。例如 String、RegExp(6)可索引的集合對象,這些對象表示按照索引值來排序的數(shù)據(jù)集合,包括數(shù)組和類型數(shù)組,以及類數(shù)組結(jié)構(gòu)的對象。例如 Array(7)使用鍵的集合對象,這些集合對象在存儲數(shù)據(jù)時會使用到鍵,支持按照插入順序來迭代元素。例如 Map、Set、WeakMap、WeakSet(8)矢量集合,SIMD 矢量集合中的數(shù)據(jù)會被組織為一個數(shù)據(jù)序列。例如 SIMD 等(9)結(jié)構(gòu)化數(shù)據(jù),這些對象用來表示和操作結(jié)構(gòu)化的緩沖區(qū)數(shù)據(jù),或使用 JSON 編碼的數(shù)據(jù)。例如 JSON 等(10)控制抽象對象例如 Promise、Generator 等(11)反射例如 Reflect、Proxy(12)國際化,為了支持多語言處理而加入 ECMAScript 的對象。例如 Intl、Intl.Collator 等(13)WebAssembly(14)其他例如 arguments
回答:
js 中的內(nèi)置對象主要指的是在程序執(zhí)行前存在全局作用域里的由 js 定義的一些全局值屬性、函數(shù)和用來實(shí)例化其他對象的構(gòu)造函數(shù)對象。一般我們經(jīng)常用到的如全局變量值 NaN、undefined,全局函數(shù)如 parseInt()、parseFloat() 用來實(shí)例化對象的構(gòu)造函數(shù)如 Date、Object 等,還有提供數(shù)學(xué)計(jì)算的單體內(nèi)置對象如 Math 對象。
詳細(xì)資料可以參考:?《標(biāo)準(zhǔn)內(nèi)置對象的分類》?《JS 所有內(nèi)置對象屬性和方法匯總》
6、 undefined 與 undeclared 的區(qū)別?
已在作用域中聲明但還沒有賦值的變量,是 undefined 的。相反,還沒有在作用域中聲明過的變量,是 undeclared 的。對于 undeclared 變量的引用,瀏覽器會報(bào)引用錯誤,如 ReferenceError: b is not defined 。但是我們可以使用 typeof 的安全防范機(jī)制來避免報(bào)錯,因?yàn)閷τ?undeclared(或者 not defined )變量,typeof 會返回 "undefined"。
首先 Undefined 和 Null 都是基本數(shù)據(jù)類型,這兩個基本數(shù)據(jù)類型分別都只有一個值,就是 undefined 和 null。undefined 代表的含義是未定義,null 代表的含義是空對象。一般變量聲明了但還沒有定義的時候會返回 undefined,null主要用于賦值給一些可能會返回對象的變量,作為初始化。undefined 在 js 中不是一個保留字,這意味著我們可以使用 undefined 來作為一個變量名,這樣的做法是非常危險(xiǎn)的,它會影響我們對 undefined 值的判斷。但是我們可以通過一些方法獲得安全的 undefined 值,比如說 void 0。當(dāng)我們對兩種類型使用 typeof 進(jìn)行判斷的時候,Null 類型化會返回 “object”,這是一個歷史遺留的問題。當(dāng)我們使用雙等號對兩種類型的值進(jìn)行比較時會返回 true,使用三個等號時會返回 false。
詳細(xì)資料可以參考:?《JavaScript 深入理解之 undefined 與 null》
8、?如何獲取安全的 undefined 值?
因?yàn)?undefined 是一個標(biāo)識符,所以可以被當(dāng)作變量來使用和賦值,但是這樣會影響 undefined 的正常判斷。表達(dá)式 void ___ 沒有返回值,因此返回結(jié)果是 undefined。void 并不改變表達(dá)式的結(jié)果,只是讓表達(dá)式不返回值。按慣例我們用 void 0 來獲得 undefined。
在平常項(xiàng)目開發(fā)中,我們遵守一些這樣的基本規(guī)范,比如說:(1)一個函數(shù)作用域中所有的變量聲明應(yīng)該盡量提到函數(shù)首部,用一個 var 聲明,不允許出現(xiàn)兩個連續(xù)的 var 聲明,聲明時如果變量沒有值,應(yīng)該給該變量賦值對應(yīng)類型的初始值,便于他人閱讀代碼時,能夠一目了然的知道變量對應(yīng)的類型值。(2)代碼中出現(xiàn)地址、時間等字符串時需要使用常量代替。(3)在進(jìn)行比較的時候吧,盡量使用'===', '!=='代替'==', '!='。(4)不要在內(nèi)置對象的原型上添加方法,如 Array, Date。(5)switch 語句必須帶有 default 分支。(6)for 循環(huán)必須使用大括號。(7)if 語句必須使用大括號。
在 js 中我們是使用構(gòu)造函數(shù)來新建一個對象的,每一個構(gòu)造函數(shù)的內(nèi)部都有一個 prototype 屬性值,這個屬性值是一個對象,這個對象包含了可以由該構(gòu)造函數(shù)的所有實(shí)例共享的屬性和方法。當(dāng)我們使用構(gòu)造函數(shù)新建一個對象后,在這個對象的內(nèi)部將包含一個指針,這個指針指向構(gòu)造函數(shù)的 prototype 屬性對應(yīng)的值,在 ES5 中這個指針被稱為對象的原型。一般來說我們是不應(yīng)該能夠獲取到這個值的,但是現(xiàn)在瀏覽器中都實(shí)現(xiàn)了 __proto__ 屬性來讓我們訪問這個屬性,但是我們最好不要使用這個屬性,因?yàn)樗皇且?guī)范中規(guī)定的。ES5 中新增了一個 Object.getPrototypeOf() 方法,我們可以通過這個方法來獲取對象的原型。當(dāng)我們訪問一個對象的屬性時,如果這個對象內(nèi)部不存在這個屬性,那么它就會去它的原型對象里找這個屬性,這個原型對象又會有自己的原型,于是就這樣一直找下去,也就是原型鏈的概念。原型鏈的盡頭一般來說都是 Object.prototype 所以這就是我們新建的對象為什么能夠使用 toString() 等方法的原因。特點(diǎn):JavaScript 對象是通過引用來傳遞的,我們創(chuàng)建的每個新對象實(shí)體中并沒有一份屬于自己的原型副本。當(dāng)我們修改原型時,與之相關(guān)的對象也會繼承這一改變。
詳細(xì)資料可以參考:?《JavaScript 深入理解之原型與原型鏈》
11、js 獲取原型的方法?
p.__proto__
p.constructor.prototype
Object.getPrototypeOf(p)
12、?在 js 中不同進(jìn)制數(shù)字的表示方式
以 0X、0x 開頭的表示為十六進(jìn)制。
以 0、0O、0o 開頭的表示為八進(jìn)制。
以 0B、0b 開頭的表示為二進(jìn)制格式。
13、js 中整數(shù)的安全范圍是多少?
安全整數(shù)指的是,在這個范圍內(nèi)的整數(shù)轉(zhuǎn)化為二進(jìn)制存儲的時候不會出現(xiàn)精度丟失,能夠被“安全”呈現(xiàn)的最大整數(shù)是 2^53 - 1,即9007199254740991,在 ES6 中被定義為 Number.MAX_SAFE_INTEGER。最小整數(shù)是-9007199254740991,在 ES6 中被定義為 Number.MIN_SAFE_INTEGER。如果某次計(jì)算的結(jié)果得到了一個超過 JavaScript 數(shù)值范圍的值,那么這個值會被自動轉(zhuǎn)換為特殊的 Infinity 值。如果某次計(jì)算返回了正或負(fù)的 Infinity 值,那么該值將無法參與下一次的計(jì)算。判斷一個數(shù)是不是有窮的,可以使用 isFinite 函數(shù)來判斷。
NaN 意指“不是一個數(shù)字”(not a number),NaN 是一個“警戒值”(sentinel value,有特殊用途的常規(guī)值),用于指出數(shù)字類型中的錯誤情況,即“執(zhí)行數(shù)學(xué)運(yùn)算沒有成功,這是失敗后返回的結(jié)果”。typeof NaN; // "number"NaN 是一個特殊值,它和自身不相等,是唯一一個非自反(自反,reflexive,即 x === x 不成立)的值。而 NaN != NaN為 true。
函數(shù) isNaN 接收參數(shù)后,會嘗試將這個參數(shù)轉(zhuǎn)換為數(shù)值,任何不能被轉(zhuǎn)換為數(shù)值的的值都會返回 true,因此非數(shù)字值傳入也會返回 true ,會影響 NaN 的判斷。函數(shù) Number.isNaN 會首先判斷傳入?yún)?shù)是否為數(shù)字,如果是數(shù)字再繼續(xù)判斷是否為 NaN ,這種方法對于 NaN 的判斷更為準(zhǔn)確。
Array 構(gòu)造函數(shù)只帶一個數(shù)字參數(shù)的時候,該參數(shù)會被作為數(shù)組的預(yù)設(shè)長度(length),而非只充當(dāng)數(shù)組中的一個元素。這樣創(chuàng)建出來的只是一個空數(shù)組,只不過它的 length 屬性被設(shè)置成了指定的值。構(gòu)造函數(shù) Array(..) 不要求必須帶 new 關(guān)鍵字。不帶時,它會被自動補(bǔ)上。
17、?其他值到字符串的轉(zhuǎn)換規(guī)則?
規(guī)范的 9.8 節(jié)中定義了抽象操作 ToString ,它負(fù)責(zé)處理非字符串到字符串的強(qiáng)制類型轉(zhuǎn)換。(1)Null 和 Undefined 類型 ,null 轉(zhuǎn)換為 "null",undefined 轉(zhuǎn)換為 "undefined",(2)Boolean 類型,true 轉(zhuǎn)換為 "true",false 轉(zhuǎn)換為 "false"。(3)Number 類型的值直接轉(zhuǎn)換,不過那些極小和極大的數(shù)字會使用指數(shù)形式。(4)Symbol 類型的值直接轉(zhuǎn)換,但是只允許顯式強(qiáng)制類型轉(zhuǎn)換,使用隱式強(qiáng)制類型轉(zhuǎn)換會產(chǎn)生錯誤。(3)對普通對象來說,除非自行定義 toString() 方法,否則會調(diào)用 toString()(Object.prototype.toString())來返回內(nèi)部屬性 [[Class]] 的值,如"[object Object]"。如果對象有自己的 toString() 方法,字符串化時就會調(diào)用該方法并使用其返回值。
18、其他值到數(shù)字值的轉(zhuǎn)換規(guī)則?
有時我們需要將非數(shù)字值當(dāng)作數(shù)字來使用,比如數(shù)學(xué)運(yùn)算。為此 ES5 規(guī)范在 9.3 節(jié)定義了抽象操作 ToNumber。(1)Undefined 類型的值轉(zhuǎn)換為 NaN。(2)Null 類型的值轉(zhuǎn)換為 0。(3)Boolean 類型的值,true 轉(zhuǎn)換為 1,false 轉(zhuǎn)換為 0。(4)String 類型的值轉(zhuǎn)換如同使用 Number() 函數(shù)進(jìn)行轉(zhuǎn)換,如果包含非數(shù)字值則轉(zhuǎn)換為 NaN,空字符串為 0。(5)Symbol 類型的值不能轉(zhuǎn)換為數(shù)字,會報(bào)錯。(6)對象(包括數(shù)組)會首先被轉(zhuǎn)換為相應(yīng)的基本類型值,如果返回的是非數(shù)字的基本類型值,則再遵循以上規(guī)則將其強(qiáng)制轉(zhuǎn)換為數(shù)字。為了將值轉(zhuǎn)換為相應(yīng)的基本類型值,抽象操作 ToPrimitive 會首先(通過內(nèi)部操作 DefaultValue)檢查該值是否有valueOf() 方法。如果有并且返回基本類型值,就使用該值進(jìn)行強(qiáng)制類型轉(zhuǎn)換。如果沒有就使用 toString() 的返回值(如果存在)來進(jìn)行強(qiáng)制類型轉(zhuǎn)換。如果 valueOf() 和 toString() 均不返回基本類型值,會產(chǎn)生 TypeError 錯誤。
ES5 規(guī)范 9.2 節(jié)中定義了抽象操作 ToBoolean,列舉了布爾強(qiáng)制類型轉(zhuǎn)換所有可能出現(xiàn)的結(jié)果。以下這些是假值:? undefined? null? false? +0、-0 和 NaN? ""假值的布爾強(qiáng)制類型轉(zhuǎn)換結(jié)果為 false。從邏輯上說,假值列表以外的都應(yīng)該是真值。
{} 的 valueOf 結(jié)果為 {} ,toString 的結(jié)果為 "[object Object]"[] 的 valueOf 結(jié)果為 [] ,toString 的結(jié)果為 ""
21、?什么是假值對象?
瀏覽器在某些特定情況下,在常規(guī) JavaScript 語法基礎(chǔ)上自己創(chuàng)建了一些外來值,這些就是“假值對象”。假值對象看起來和普通對象并無二致(都有屬性,等等),但將它們強(qiáng)制類型轉(zhuǎn)換為布爾值時結(jié)果為 false 最常見的例子是 document.all,它
22、?~?操作符的作用?
~ 返回 2 的補(bǔ)碼,并且 ~ 會將數(shù)字轉(zhuǎn)換為 32 位整數(shù),因此我們可以使用 ~ 來進(jìn)行取整操作。~x 大致等同于 -(x+1)。
23、解析字符串中的數(shù)字和將字符串強(qiáng)制類型轉(zhuǎn)換為數(shù)字的返回結(jié)果都是數(shù)字,它們之間的區(qū)別是什么?
解析允許字符串(如 parseInt() )中含有非數(shù)字字符,解析按從左到右的順序,如果遇到非數(shù)字字符就停止。而轉(zhuǎn)換(如 Number ())不允許出現(xiàn)非數(shù)字字符,否則會失敗并返回 NaN。
24、+?操作符什么時候用于字符串的拼接?
根據(jù) ES5 規(guī)范 11.6.1 節(jié),如果某個操作數(shù)是字符串或者能夠通過以下步驟轉(zhuǎn)換為字符串的話,+ 將進(jìn)行拼接操作。如果其中一個操作數(shù)是對象(包括數(shù)組),則首先對其調(diào)用 ToPrimitive 抽象操作,該抽象操作再調(diào)用 [[DefaultValue]],以數(shù)字作為上下文。如果不能轉(zhuǎn)換為字符串,則會將其轉(zhuǎn)換為數(shù)字類型來進(jìn)行計(jì)算。簡單來說就是,如果 + 的其中一個操作數(shù)是字符串(或者通過以上步驟最終得到字符串),則執(zhí)行字符串拼接,否則執(zhí)行數(shù)字加法。那么對于除了加法的運(yùn)算符來說,只要其中一方是數(shù)字,那么另一方就會被轉(zhuǎn)為數(shù)字。
25、?什么情況下會發(fā)生布爾值的隱式強(qiáng)制類型轉(zhuǎn)換?
(1) if (..) 語句中的條件判斷表達(dá)式。(2) for ( .. ; .. ; .. ) 語句中的條件判斷表達(dá)式(第二個)。(3) while (..) 和 do..while(..) 循環(huán)中的條件判斷表達(dá)式。(4) ? : 中的條件判斷表達(dá)式。(5) 邏輯運(yùn)算符 ||(邏輯或)和 &&(邏輯與)左邊的操作數(shù)(作為條件判斷表達(dá)式)。
26、?||?和?&&?操作符的返回值?
|| 和 && 首先會對第一個操作數(shù)執(zhí)行條件判斷,如果其不是布爾值就先進(jìn)行 ToBoolean 強(qiáng)制類型轉(zhuǎn)換,然后再執(zhí)行條件判斷。對于 || 來說,如果條件判斷結(jié)果為 true 就返回第一個操作數(shù)的值,如果為 false 就返回第二個操作數(shù)的值。&& 則相反,如果條件判斷結(jié)果為 true 就返回第二個操作數(shù)的值,如果為 false 就返回第一個操作數(shù)的值。|| 和 && 返回它們其中一個操作數(shù)的值,而非條件判斷的結(jié)果
ES6 允許從符號到字符串的顯式強(qiáng)制類型轉(zhuǎn)換,然而隱式強(qiáng)制類型轉(zhuǎn)換會產(chǎn)生錯誤。Symbol 值不能夠被強(qiáng)制類型轉(zhuǎn)換為數(shù)字(顯式和隱式都會產(chǎn)生錯誤),但可以被強(qiáng)制類型轉(zhuǎn)換為布爾值(顯式和隱式結(jié)果都是 true )。
28、?==?操作符的強(qiáng)制類型轉(zhuǎn)換規(guī)則?
(1)字符串和數(shù)字之間的相等比較,將字符串轉(zhuǎn)換為數(shù)字之后再進(jìn)行比較。(2)其他類型和布爾類型之間的相等比較,先將布爾值轉(zhuǎn)換為數(shù)字后,再應(yīng)用其他規(guī)則進(jìn)行比較。(3)null 和 undefined 之間的相等比較,結(jié)果為真。其他值和它們進(jìn)行比較都返回假值。(4)對象和非對象之間的相等比較,對象先調(diào)用 ToPrimitive 抽象操作后,再進(jìn)行比較。(5)如果一個操作值為 NaN ,則相等比較返回 false( NaN 本身也不等于 NaN )。(6)如果兩個操作值都是對象,則比較它們是不是指向同一個對象。如果兩個操作數(shù)都指向同一個對象,則相等操作符返回 true,否則,返回 false。
詳細(xì)資料可以參考:?《JavaScript 字符串間的比較》
29、如何將字符串轉(zhuǎn)化為數(shù)字,例如 '12.3b'?
(1)使用 Number() 方法,前提是所包含的字符串不包含不合法字符。(2)使用 parseInt() 方法,parseInt() 函數(shù)可解析一個字符串,并返回一個整數(shù)。還可以設(shè)置要解析的數(shù)字的基數(shù)。當(dāng)基數(shù)的值為 0,或沒有設(shè)置該參數(shù)時,parseInt() 會根據(jù) string 來判斷數(shù)字的基數(shù)。(3)使用 parseFloat() 方法,該函數(shù)解析一個字符串參數(shù)并返回一個浮點(diǎn)數(shù)。(4)使用 + 操作符的隱式轉(zhuǎn)換。
詳細(xì)資料可以參考:?《詳解 JS 中 Number()、parseInt() 和 parseFloat() 的區(qū)別》
30、如何將浮點(diǎn)數(shù)點(diǎn)左邊的數(shù)每三位添加一個逗號,如 12000000.11 轉(zhuǎn)化為『12,000,000.11』?
// 方法一function format(number) {return number && number.replace(/(?!^)(?=(\d{3})+\.)/g, ",");}// 方法二function format1(number) {return Intl.NumberFormat().format(number)}// 方法三function format2(number) {return number.toLocaleString('en')
31、常用正則表達(dá)式
// (1)匹配 16 進(jìn)制顏色值var regex = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g;// (2)匹配日期,如 yyyy-mm-dd 格式var regex = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;// (3)匹配 qq 號var regex = /^[1-9][0-9]{4,10}$/g;// (4)手機(jī)號碼正則var regex = /^1[34578]\d{9}$/g;// (5)用戶名正則var regex = /^[a-zA-Z\$][a-zA-Z0-9_\$]{4,16}$/;
詳細(xì)資料可以參考:?《前端表單驗(yàn)證常用的 15 個 JS 正則表達(dá)式》?《JS 常用正則匯總》
32、生成隨機(jī)數(shù)的各種方法?
《JS - 生成隨機(jī)數(shù)的方法匯總(不同范圍、類型的隨機(jī)數(shù))》
33、如何實(shí)現(xiàn)數(shù)組的隨機(jī)排序?
// (1)使用數(shù)組 sort 方法對數(shù)組元素隨機(jī)排序,讓 Math.random() 出來的數(shù)與 0.5 比較,如果大于就返回 1 交換位置,如果小于就返回 -1,不交換位置。function randomSort(a, b) {return Math.random() > 0.5 ? -1 : 1;}// 缺點(diǎn):每個元素被派到新數(shù)組的位置不是隨機(jī)的,原因是 sort() 方法是依次比較的。// (2)隨機(jī)從原數(shù)組抽取一個元素,加入到新數(shù)組function randomSort(arr) {var result = [];while (arr.length > 0) {var randomIndex = Math.floor(Math.random() * arr.length);result.push(arr[randomIndex]);arr.splice(randomIndex, 1);}return result;}// (3)隨機(jī)交換數(shù)組內(nèi)的元素(洗牌算法類似)function randomSort(arr) {var index,randomIndex,temp,len = arr.length;for (index = 0; index < len; index++) {randomIndex = Math.floor(Math.random() * (len - index)) + index;temp = arr[index];arr[index] = arr[randomIndex];arr[randomIndex] = temp;}return arr;}// es6function randomSort(array) {let length = array.length;if (!Array.isArray(array) || length <= 1) return;for (let index = 0; index < length - 1; index++) {let randomIndex = Math.floor(Math.random() * (length - index)) + index;[array[index], array[randomIndex]] = [array[randomIndex], array[index]];}return array;}
詳細(xì)資料可以參考:?《Fisher and Yates 的原始版》?《javascript 實(shí)現(xiàn)數(shù)組隨機(jī)排序?》?《JavaScript 學(xué)習(xí)筆記:數(shù)組隨機(jī)排序》
34、javascript 創(chuàng)建對象的幾種方式?
我們一般使用字面量的形式直接創(chuàng)建對象,但是這種創(chuàng)建方式對于創(chuàng)建大量相似對象的時候,會產(chǎn)生大量的重復(fù)代碼。但 js和一般的面向?qū)ο蟮恼Z言不同,在 ES6 之前它沒有類的概念。但是我們可以使用函數(shù)來進(jìn)行模擬,從而產(chǎn)生出可復(fù)用的對象創(chuàng)建方式,我了解到的方式有這么幾種:(1)第一種是工廠模式,工廠模式的主要工作原理是用函數(shù)來封裝創(chuàng)建對象的細(xì)節(jié),從而通過調(diào)用函數(shù)來達(dá)到復(fù)用的目的。但是它有一個很大的問題就是創(chuàng)建出來的對象無法和某個類型聯(lián)系起來,它只是簡單的封裝了復(fù)用代碼,而沒有建立起對象和類型間的關(guān)系。(2)第二種是構(gòu)造函數(shù)模式。js 中每一個函數(shù)都可以作為構(gòu)造函數(shù),只要一個函數(shù)是通過 new 來調(diào)用的,那么我們就可以把它稱為構(gòu)造函數(shù)。執(zhí)行構(gòu)造函數(shù)首先會創(chuàng)建一個對象,然后將對象的原型指向構(gòu)造函數(shù)的 prototype 屬性,然后將執(zhí)行上下文中的 this 指向這個對象,最后再執(zhí)行整個函數(shù),如果返回值不是對象,則返回新建的對象。因?yàn)?this 的值指向了新建的對象,因此我們可以使用 this 給對象賦值。構(gòu)造函數(shù)模式相對于工廠模式的優(yōu)點(diǎn)是,所創(chuàng)建的對象和構(gòu)造函數(shù)建立起了聯(lián)系,因此我們可以通過原型來識別對象的類型。但是構(gòu)造函數(shù)存在一個缺點(diǎn)就是,造成了不必要的函數(shù)對象的創(chuàng)建,因?yàn)樵?js 中函數(shù)也是一個對象,因此如果對象屬性中如果包含函數(shù)的話,那么每次我們都會新建一個函數(shù)對象,浪費(fèi)了不必要的內(nèi)存空間,因?yàn)楹瘮?shù)是所有的實(shí)例都可以通用的。(3)第三種模式是原型模式,因?yàn)槊恳粋€函數(shù)都有一個 prototype 屬性,這個屬性是一個對象,它包含了通過構(gòu)造函數(shù)創(chuàng)建的所有實(shí)例都能共享的屬性和方法。因此我們可以使用原型對象來添加公用屬性和方法,從而實(shí)現(xiàn)代碼的復(fù)用。這種方式相對于構(gòu)造函數(shù)模式來說,解決了函數(shù)對象的復(fù)用問題。但是這種模式也存在一些問題,一個是沒有辦法通過傳入?yún)?shù)來初始化值,另一個是如果存在一個引用類型如 Array 這樣的值,那么所有的實(shí)例將共享一個對象,一個實(shí)例對引用類型值的改變會影響所有的實(shí)例。(4)第四種模式是組合使用構(gòu)造函數(shù)模式和原型模式,這是創(chuàng)建自定義類型的最常見方式。因?yàn)闃?gòu)造函數(shù)模式和原型模式分開使用都存在一些問題,因此我們可以組合使用這兩種模式,通過構(gòu)造函數(shù)來初始化對象的屬性,通過原型對象來實(shí)現(xiàn)函數(shù)方法的復(fù)用。這種方法很好的解決了兩種模式單獨(dú)使用時的缺點(diǎn),但是有一點(diǎn)不足的就是,因?yàn)槭褂昧藘煞N不同的模式,所以對于代碼的封裝性不夠好。(5)第五種模式是動態(tài)原型模式,這一種模式將原型方法賦值的創(chuàng)建過程移動到了構(gòu)造函數(shù)的內(nèi)部,通過對屬性是否存在的判斷,可以實(shí)現(xiàn)僅在第一次調(diào)用函數(shù)時對原型對象賦值一次的效果。這一種方式很好地對上面的混合模式進(jìn)行了封裝。(6)第六種模式是寄生構(gòu)造函數(shù)模式,這一種模式和工廠模式的實(shí)現(xiàn)基本相同,我對這個模式的理解是,它主要是基于一個已有的類型,在實(shí)例化時對實(shí)例化的對象進(jìn)行擴(kuò)展。這樣既不用修改原來的構(gòu)造函數(shù),也達(dá)到了擴(kuò)展對象的目的。它的一個缺點(diǎn)和工廠模式一樣,無法實(shí)現(xiàn)對象的識別。嗯我目前了解到的就是這么幾種方式。
詳細(xì)資料可以參考:?《JavaScript 深入理解之對象創(chuàng)建》
35、JavaScript 繼承的幾種實(shí)現(xiàn)方式?
我了解的 js 中實(shí)現(xiàn)繼承的幾種方式有:(1)第一種是以原型鏈的方式來實(shí)現(xiàn)繼承,但是這種實(shí)現(xiàn)方式存在的缺點(diǎn)是,在包含有引用類型的數(shù)據(jù)時,會被所有的實(shí)例對象所共享,容易造成修改的混亂。還有就是在創(chuàng)建子類型的時候不能向超類型傳遞參數(shù)。(2)第二種方式是使用借用構(gòu)造函數(shù)的方式,這種方式是通過在子類型的函數(shù)中調(diào)用超類型的構(gòu)造函數(shù)來實(shí)現(xiàn)的,這一種方法解決了不能向超類型傳遞參數(shù)的缺點(diǎn),但是它存在的一個問題就是無法實(shí)現(xiàn)函數(shù)方法的復(fù)用,并且超類型原型定義的方法子類型也沒有辦法訪問到。(3)第三種方式是組合繼承,組合繼承是將原型鏈和借用構(gòu)造函數(shù)組合起來使用的一種方式。通過借用構(gòu)造函數(shù)的方式來實(shí)現(xiàn)類型的屬性的繼承,通過將子類型的原型設(shè)置為超類型的實(shí)例來實(shí)現(xiàn)方法的繼承。這種方式解決了上面的兩種模式單獨(dú)使用時的問題,但是由于我們是以超類型的實(shí)例來作為子類型的原型,所以調(diào)用了兩次超類的構(gòu)造函數(shù),造成了子類型的原型中多了很多不必要的屬性。(4)第四種方式是原型式繼承,原型式繼承的主要思路就是基于已有的對象來創(chuàng)建新的對象,實(shí)現(xiàn)的原理是,向函數(shù)中傳入一個對象,然后返回一個以這個對象為原型的對象。這種繼承的思路主要不是為了實(shí)現(xiàn)創(chuàng)造一種新的類型,只是對某個對象實(shí)現(xiàn)一種簡單繼承,ES5 中定義的 Object.create() 方法就是原型式繼承的實(shí)現(xiàn)。缺點(diǎn)與原型鏈方式相同。(5)第五種方式是寄生式繼承,寄生式繼承的思路是創(chuàng)建一個用于封裝繼承過程的函數(shù),通過傳入一個對象,然后復(fù)制一個對象的副本,然后對象進(jìn)行擴(kuò)展,最后返回這個對象。這個擴(kuò)展的過程就可以理解是一種繼承。這種繼承的優(yōu)點(diǎn)就是對一個簡單對象實(shí)現(xiàn)繼承,如果這個對象不是我們的自定義類型時。缺點(diǎn)是沒有辦法實(shí)現(xiàn)函數(shù)的復(fù)用。(6)第六種方式是寄生式組合繼承,組合繼承的缺點(diǎn)就是使用超類型的實(shí)例做為子類型的原型,導(dǎo)致添加了不必要的原型屬性。寄生式組合繼承的方式是使用超類型的原型的副本來作為子類型的原型,這樣就避免了創(chuàng)建不必要的屬性。
詳細(xì)資料可以參考:?《JavaScript 深入理解之繼承》
36、寄生式組合繼承的實(shí)現(xiàn)?
function Person(name) {this.name = name;}Person.prototype.sayName = function() {console.log("My name is " + this.name + ".");};function Student(name, grade) {Person.call(this, name);this.grade = grade;}Student.prototype = Object.create(Person.prototype);Student.prototype.constructor = Student;Student.prototype.sayMyGrade = function() {console.log("My grade is " + this.grade + ".");
37、Javascript 的作用域鏈?
作用域鏈的作用是保證對執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問,通過作用域鏈,我們可以訪問到外層環(huán)境的變量和函數(shù)。作用域鏈的本質(zhì)上是一個指向變量對象的指針列表。變量對象是一個包含了執(zhí)行環(huán)境中所有變量和函數(shù)的對象。作用域鏈的前端始終都是當(dāng)前執(zhí)行上下文的變量對象。全局執(zhí)行上下文的變量對象(也就是全局對象)始終是作用域鏈的最后一個對象。當(dāng)我們查找一個變量時,如果當(dāng)前執(zhí)行環(huán)境中沒有找到,我們可以沿著作用域鏈向后查找。作用域鏈的創(chuàng)建過程跟執(zhí)行上下文的建立有關(guān)....
詳細(xì)資料可以參考:?《JavaScript 深入理解之作用域鏈》
38、談?wù)?This 對象的理解。
this 是執(zhí)行上下文中的一個屬性,它指向最后一次調(diào)用這個方法的對象。在實(shí)際開發(fā)中,this 的指向可以通過四種調(diào)用模式來判斷。
1.第一種是函數(shù)調(diào)用模式,當(dāng)一個函數(shù)不是一個對象的屬性時,直接作為函數(shù)來調(diào)用時,this 指向全局對象。
2.第二種是方法調(diào)用模式,如果一個函數(shù)作為一個對象的方法來調(diào)用時,this 指向這個對象。
3.第三種是構(gòu)造器調(diào)用模式,如果一個函數(shù)用 new 調(diào)用時,函數(shù)執(zhí)行前會新創(chuàng)建一個對象,this 指向這個新創(chuàng)建的對象。
4.第四種是 apply 、 call 和 bind 調(diào)用模式,這三個方法都可以顯示的指定調(diào)用函數(shù)的 this 指向。其中 apply 方法接收兩個參數(shù):一個是 this 綁定的對象,一個是參數(shù)數(shù)組。call 方法接收的參數(shù),第一個是 this 綁定的對象,后面的其余參數(shù)是傳入函數(shù)執(zhí)行的參數(shù)。也就是說,在使用 call() 方法時,傳遞給函數(shù)的參數(shù)必須逐個列舉出來。bind 方法通過傳入一個對象,返回一個 this 綁定了傳入對象的新函數(shù)。這個函數(shù)的 this 指向除了使用 new 時會被改變,其他情況下都不會改變。
這四種方式,使用構(gòu)造器調(diào)用模式的優(yōu)先級最高,然后是 apply 、 call 和 bind 調(diào)用模式,然后是方法調(diào)用模式,然后是函數(shù)調(diào)用模式。
《JavaScript 深入理解之 this 詳解》
39、 eval 是做什么的?
它的功能是把對應(yīng)的字符串解析成 JS 代碼并運(yùn)行。應(yīng)該避免使用 eval,不安全,非常耗性能(2次,一次解析成 js 語句,一次執(zhí)行)。
詳細(xì)資料可以參考:?《eval()》
40、什么是 DOM 和 BOM?
DOM 指的是文檔對象模型,它指的是把文檔當(dāng)做一個對象來對待,這個對象主要定義了處理網(wǎng)頁內(nèi)容的方法和接口。BOM 指的是瀏覽器對象模型,它指的是把瀏覽器當(dāng)做一個對象來對待,這個對象主要定義了與瀏覽器進(jìn)行交互的法和接口。BOM的核心是 window,而 window 對象具有雙重角色,它既是通過 js 訪問瀏覽器窗口的一個接口,又是一個 Global(全局)對象。這意味著在網(wǎng)頁中定義的任何對象,變量和函數(shù),都作為全局對象的一個屬性或者方法存在。window 對象含有 location 對象、navigator 對象、screen 對象等子對象,并且 DOM 的最根本的對象 document 對象也是 BOM 的 window 對象的子對象。
詳細(xì)資料可以參考:?《DOM, DOCUMENT, BOM, WINDOW 有什么區(qū)別?》?《Window 對象》?《DOM 與 BOM 分別是什么,有何關(guān)聯(lián)?》?《JavaScript 學(xué)習(xí)總結(jié)(三)BOM 和 DOM 詳解》
41、寫一個通用的事件偵聽器函數(shù)。
const EventUtils = {// 視能力分別使用dom0||dom2||IE方式 來綁定事件// 添加事件addEvent: function(element, type, handler) {if (element.addEventListener) {element.addEventListener(type, handler, false);} else if (element.attachEvent) {element.attachEvent("on" + type, handler);} else {element["on" + type] = handler;}},// 移除事件removeEvent: function(element, type, handler) {if (element.removeEventListener) {element.removeEventListener(type, handler, false);} else if (element.detachEvent) {element.detachEvent("on" + type, handler);} else {element["on" + type] = null;}},// 獲取事件目標(biāo)getTarget: function(event) {return event.target || event.srcElement;},// 獲取 event 對象的引用,取到事件的所有信息,確保隨時能使用 eventgetEvent: function(event) {return event || window.event;},// 阻止事件(主要是事件冒泡,因?yàn)?IE 不支持事件捕獲)stopPropagation: function(event) {if (event.stopPropagation) {event.stopPropagation();} else {event.cancelBubble = true;}},// 取消事件的默認(rèn)行為preventDefault: function(event) {if (event.preventDefault) {event.preventDefault();} else {event.returnValue = false;}}};
詳細(xì)資料可以參考:?《JS 事件模型》
42、事件是什么?IE 與火狐的事件機(jī)制有什么區(qū)別?如何阻止冒泡?
1.事件是用戶操作網(wǎng)頁時發(fā)生的交互動作,比如 click/move, 事件除了用戶觸發(fā)的動作外,還可以是文檔加載,窗口滾動和大小調(diào)整。事件被封裝成一個 event 對象,包含了該事件發(fā)生時的所有相關(guān)信息( event 的屬性)以及可以對事件進(jìn)行的操作( event 的方法)。
2.事件處理機(jī)制:IE 支持事件冒泡、Firefox 同時支持兩種事件模型,也就是:事件冒泡和事件捕獲。
3.event.stopPropagation() 或者 ie 下的方法 event.cancelBubble = true;
詳細(xì)資料可以參考:?《Javascript 事件模型系列(一)事件及事件的三種模型》?《Javascript 事件模型:事件捕獲和事件冒泡》
43、?三種事件模型是什么?
事件是用戶操作網(wǎng)頁時發(fā)生的交互動作或者網(wǎng)頁本身的一些操作,現(xiàn)代瀏覽器一共有三種事件模型。第一種事件模型是最早的 DOM0 級模型,這種模型不會傳播,所以沒有事件流的概念,但是現(xiàn)在有的瀏覽器支持以冒泡的方式實(shí)現(xiàn),它可以在網(wǎng)頁中直接定義監(jiān)聽函數(shù),也可以通過 js 屬性來指定監(jiān)聽函數(shù)。這種方式是所有瀏覽器都兼容的。第二種事件模型是 IE 事件模型,在該事件模型中,一次事件共有兩個過程,事件處理階段,和事件冒泡階段。事件處理階段會首先執(zhí)行目標(biāo)元素綁定的監(jiān)聽事件。然后是事件冒泡階段,冒泡指的是事件從目標(biāo)元素冒泡到 document,依次檢查經(jīng)過的節(jié)點(diǎn)是否綁定了事件監(jiān)聽函數(shù),如果有則執(zhí)行。這種模型通過 attachEvent 來添加監(jiān)聽函數(shù),可以添加多個監(jiān)聽函數(shù),會按順序依次執(zhí)行。第三種是 DOM2 級事件模型,在該事件模型中,一次事件共有三個過程,第一個過程是事件捕獲階段。捕獲指的是事件從 document 一直向下傳播到目標(biāo)元素,依次檢查經(jīng)過的節(jié)點(diǎn)是否綁定了事件監(jiān)聽函數(shù),如果有則執(zhí)行。后面兩個階段和 IE 事件模型的兩個階段相同。這種事件模型,事件綁定的函數(shù)是 addEventListener,其中第三個參數(shù)可以指定事件是否在捕獲階段執(zhí)行。
詳細(xì)資料可以參考:?《一個 DOM 元素綁定多個事件時,先執(zhí)行冒泡還是捕獲》
44、事件委托是什么?
事件委托本質(zhì)上是利用了瀏覽器事件冒泡的機(jī)制。因?yàn)槭录诿芭葸^程中會上傳到父節(jié)點(diǎn),并且父節(jié)點(diǎn)可以通過事件對象獲取到目標(biāo)節(jié)點(diǎn),因此可以把子節(jié)點(diǎn)的監(jiān)聽函數(shù)定義在父節(jié)點(diǎn)上,由父節(jié)點(diǎn)的監(jiān)聽函數(shù)統(tǒng)一處理多個子元素的事件,這種方式稱為事件代理。使用事件代理我們可以不必要為每一個子元素都綁定一個監(jiān)聽事件,這樣減少了內(nèi)存上的消耗。并且使用事件代理我們還可以實(shí)現(xiàn)事件的動態(tài)綁定,比如說新增了一個子節(jié)點(diǎn),我們并不需要單獨(dú)地為它添加一個監(jiān)聽事件,它所發(fā)生的事件會交給父元素中的監(jiān)聽函數(shù)來處理。
詳細(xì)資料可以參考:?《JavaScript 事件委托詳解》
45、?["1", "2", "3"].map(parseInt) 答案是多少?
parseInt() 函數(shù)能解析一個字符串,并返回一個整數(shù),需要兩個參數(shù) (val, radix),其中 radix 表示要解析的數(shù)字的基數(shù)。(該值介于 2 ~ 36 之間,并且字符串中的數(shù)字不能大于 radix 才能正確返回?cái)?shù)字結(jié)果值)。此處 map 傳了 3 個參數(shù) (element, index, array),默認(rèn)第三個參數(shù)被忽略掉,因此三次傳入的參數(shù)分別為 "1-0", "2-1", "3-2"因?yàn)樽址闹挡荒艽笥诨鶖?shù),因此后面兩次調(diào)用均失敗,返回 NaN ,第一次基數(shù)為 0 ,按十進(jìn)制解析返回 1。
詳細(xì)資料可以參考:?《為什么 ["1", "2", "3"].map(parseInt) 返回 [1,NaN,NaN]?》
46、什么是閉包,為什么要用它?
閉包是指有權(quán)訪問另一個函數(shù)作用域中變量的函數(shù),創(chuàng)建閉包的最常見的方式就是在一個函數(shù)內(nèi)創(chuàng)建另一個函數(shù),創(chuàng)建的函數(shù)可以訪問到當(dāng)前函數(shù)的局部變量。閉包有兩個常用的用途。閉包的第一個用途是使我們在函數(shù)外部能夠訪問到函數(shù)內(nèi)部的變量。通過使用閉包,我們可以通過在外部調(diào)用閉包函數(shù),從而在外部訪問到函數(shù)內(nèi)部的變量,可以使用這種方法來創(chuàng)建私有變量。函數(shù)的另一個用途是使已經(jīng)運(yùn)行結(jié)束的函數(shù)上下文中的變量對象繼續(xù)留在內(nèi)存中,因?yàn)殚]包函數(shù)保留了這個變量對象的引用,所以這個變量對象不會被回收。其實(shí)閉包的本質(zhì)就是作用域鏈的一個特殊的應(yīng)用,只要了解了作用域鏈的創(chuàng)建過程,就能夠理解閉包的實(shí)現(xiàn)原理。
詳細(xì)資料可以參考:?《JavaScript 深入理解之閉包》
47、 javascript 代碼中的 "use strict"; 是什么意思 ? 使用它區(qū)別是什么?
相關(guān)知識點(diǎn):
use strict 是一種 ECMAscript5 添加的(嚴(yán)格)運(yùn)行模式,這種模式使得 Javascript 在更嚴(yán)格的條件下運(yùn)行。設(shè)立"嚴(yán)格模式"的目的,主要有以下幾個:
消除 Javascript 語法的一些不合理、不嚴(yán)謹(jǐn)之處,減少一些怪異行為;
消除代碼運(yùn)行的一些不安全之處,保證代碼運(yùn)行的安全;
提高編譯器效率,增加運(yùn)行速度;
為未來新版本的 Javascript 做好鋪墊。
區(qū)別:
1.禁止使用 with 語句。
2.禁止 this 關(guān)鍵字指向全局對象。
3.對象不能有重名的屬性。
回答:
use strict 指的是嚴(yán)格運(yùn)行模式,在這種模式對 js 的使用添加了一些限制。比如說禁止 this 指向全局對象,還有禁止使用 with 語句等。設(shè)立嚴(yán)格模式的目的,主要是為了消除代碼使用中的一些不安全的使用方式,也是為了消除 js 語法本身的一些不合理的地方,以此來減少一些運(yùn)行時的怪異的行為。同時使用嚴(yán)格運(yùn)行模式也能夠提高編譯的效率,從而提高代碼的運(yùn)行速度。我認(rèn)為嚴(yán)格模式代表了 js 一種更合理、更安全、更嚴(yán)謹(jǐn)?shù)陌l(fā)展方向。
詳細(xì)資料可以參考:?《Javascript 嚴(yán)格模式詳解》
48、如何判斷一個對象是否屬于某個類?
第一種方式是使用 instanceof 運(yùn)算符來判斷構(gòu)造函數(shù)的 prototype 屬性是否出現(xiàn)在對象的原型鏈中的任何位置。第二種方式可以通過對象的 constructor 屬性來判斷,對象的 constructor 屬性指向該對象的構(gòu)造函數(shù),但是這種方式不是很安全,因?yàn)?constructor 屬性可以被改寫。第三種方式,如果需要判斷的是某個內(nèi)置的引用類型的話,可以使用 Object.prototype.toString() 方法來打印對象的[[Class]] 屬性來進(jìn)行判斷。
詳細(xì)資料可以參考:?《js 判斷一個對象是否屬于某一類》
49、instanceof 的作用?
// instanceof 運(yùn)算符用于判斷構(gòu)造函數(shù)的 prototype 屬性是否出現(xiàn)在對象的原型鏈中的任何位置。// 實(shí)現(xiàn):function myInstanceof(left, right) {let proto = Object.getPrototypeOf(left), // 獲取對象的原型prototype = right.prototype; // 獲取構(gòu)造函數(shù)的 prototype 對象// 判斷構(gòu)造函數(shù)的 prototype 對象是否在對象的原型鏈上while (true) {if (!proto) return false;if (proto === prototype) return true;proto = Object.getPrototypeOf(proto);}}
詳細(xì)資料可以參考:?《instanceof》
50、new 操作符具體干了什么呢?如何實(shí)現(xiàn)?
// (1)首先創(chuàng)建了一個新的空對象// (2)設(shè)置原型,將對象的原型設(shè)置為函數(shù)的 prototype 對象。// (3)讓函數(shù)的 this 指向這個對象,執(zhí)行構(gòu)造函數(shù)的代碼(為這個新對象添加屬性)// (4)判斷函數(shù)的返回值類型,如果是值類型,返回創(chuàng)建的對象。如果是引用類型,就返回這個引用類型的對象。// 實(shí)現(xiàn):function objectFactory() {let newObject = null,constructor = Array.prototype.shift.call(arguments),result = null;// 參數(shù)判斷if (typeof constructor !== "function") {console.error("type error");return;}// 新建一個空對象,對象的原型為構(gòu)造函數(shù)的 prototype 對象newObject = Object.create(constructor.prototype);// 將 this 指向新建對象,并執(zhí)行函數(shù)result = constructor.apply(newObject, arguments);// 判斷返回對象let flag =result && (typeof result === "object" || typeof result === "function");// 判斷返回結(jié)果return flag ? result : newObject;}// 使用方法// objectFactory(構(gòu)造函數(shù), 初始化參數(shù));
詳細(xì)資料可以參考:?《new 操作符具體干了什么?》?《JavaScript 深入之 new 的模擬實(shí)現(xiàn)》
51、Javascript 中,有一個函數(shù),執(zhí)行時對象查找時,永遠(yuǎn)不會去查找原型,這個函數(shù)是?
hasOwnProperty所有繼承了 Object 的對象都會繼承到 hasOwnProperty 方法。這個方法可以用來檢測一個對象是否含有特定的自身屬性,和in 運(yùn)算符不同,該方法會忽略掉那些從原型鏈上繼承到的屬性。
詳細(xì)資料可以參考:?《Object.prototype.hasOwnProperty()》
52、對于 JSON 的了解?
相關(guān)知識點(diǎn):
JSON 是一種數(shù)據(jù)交換格式,基于文本,優(yōu)于輕量,用于交換數(shù)據(jù)。JSON 可以表示數(shù)字、布爾值、字符串、null、數(shù)組(值的有序序列),以及由這些值(或數(shù)組、對象)所組成的對象(字符串與值的映射)。JSON 使用 JavaScript 語法,但是 JSON 格式僅僅是一個文本。文本可以被任何編程語言讀取及作為數(shù)據(jù)格式傳遞。
回答:
JSON 是一種基于文本的輕量級的數(shù)據(jù)交換格式。它可以被任何的編程語言讀取和作為數(shù)據(jù)格式來傳遞。在項(xiàng)目開發(fā)中,我們使用 JSON 作為前后端數(shù)據(jù)交換的方式。在前端我們通過將一個符合 JSON 格式的數(shù)據(jù)結(jié)構(gòu)序列化為 JSON 字符串,然后將它傳遞到后端,后端通過 JSON 格式的字符串解析后生成對應(yīng)的數(shù)據(jù)結(jié)構(gòu),以此來實(shí)現(xiàn)前后端數(shù)據(jù)的一個傳遞。因?yàn)?JSON 的語法是基于 js 的,因此很容易將 JSON 和 js 中的對象弄混,但是我們應(yīng)該注意的是 JSON 和 js 中的對象不是一回事,JSON 中對象格式更加嚴(yán)格,比如說在 JSON 中屬性值不能為函數(shù),不能出現(xiàn) NaN 這樣的屬性值等,因此大多數(shù)的 js 對象是不符合 JSON 對象的格式的。在 js 中提供了兩個函數(shù)來實(shí)現(xiàn) js 數(shù)據(jù)結(jié)構(gòu)和 JSON 格式的轉(zhuǎn)換處理,一個是 JSON.stringify 函數(shù),通過傳入一個符合 JSON 格式的數(shù)據(jù)結(jié)構(gòu),將其轉(zhuǎn)換為一個 JSON 字符串。如果傳入的數(shù)據(jù)結(jié)構(gòu)不符合 JSON 格式,那么在序列化的時候會對這些值進(jìn)行對應(yīng)的特殊處理,使其符合規(guī)范。在前端向后端發(fā)送數(shù)據(jù)時,我們可以調(diào)用這個函數(shù)將數(shù)據(jù)對象轉(zhuǎn)化為 JSON 格式的字符串。另一個函數(shù) JSON.parse() 函數(shù),這個函數(shù)用來將 JSON 格式的字符串轉(zhuǎn)換為一個 js 數(shù)據(jù)結(jié)構(gòu),如果傳入的字符串不是標(biāo)準(zhǔn)的 JSON 格式的字符串的話,將會拋出錯誤。當(dāng)我們從后端接收到 JSON 格式的字符串時,我們可以通過這個方法來將其解析為一個 js 數(shù)據(jù)結(jié)構(gòu),以此來進(jìn)行數(shù)據(jù)的訪問。
詳細(xì)資料可以參考:?《深入了解 JavaScript 中的 JSON 》
53、?[].forEach.call($$(""),function(a){a.style.outline="1px solid #"+(~~(Math.random()(1<<24))).toString(16)}) 能解釋一下這段代碼的意思嗎?
(1)選取頁面所有 DOM 元素。在瀏覽器的控制臺中可以使用$$()方法來獲取頁面中相應(yīng)的元素,這是現(xiàn)代瀏覽器提供的一個命令行 API 相當(dāng)于 document.querySelectorAll 方法。(2)循環(huán)遍歷 DOM 元素(3)給元素添加 outline 。由于渲染的 outline 是不在 CSS 盒模型中的,所以為元素添加 outline 并不會影響元素的大小和頁面的布局。(4)生成隨機(jī)顏色函數(shù)。Math.random()*(1<<24) 可以得到 0~2^24 - 1 之間的隨機(jī)數(shù),因?yàn)榈玫降氖且粋€浮點(diǎn)數(shù),但我們只需要整數(shù)部分,使用取反操作符 ~ 連續(xù)兩次取反獲得整數(shù)部分,然后再用 toString(16) 的方式,轉(zhuǎn)換為一個十六進(jìn)制的字符串。
詳細(xì)資料可以參考:?《通過一行代碼學(xué) JavaScript》
54、js 延遲加載的方式有哪些?
相關(guān)知識點(diǎn):
js 延遲加載,也就是等頁面加載完成之后再加載 JavaScript 文件。js 延遲加載有助于提高頁面加載速度。一般有以下幾種方式:
defer 屬性
async 屬性
動態(tài)創(chuàng)建 DOM 方式
使用 setTimeout 延遲方法
讓 JS 最后加載
回答:
js 的加載、解析和執(zhí)行會阻塞頁面的渲染過程,因此我們希望 js 腳本能夠盡可能的延遲加載,提高頁面的渲染速度。我了解到的幾種方式是:第一種方式是我們一般采用的是將 js 腳本放在文檔的底部,來使 js 腳本盡可能的在最后來加載執(zhí)行。第二種方式是給 js 腳本添加 defer 屬性,這個屬性會讓腳本的加載與文檔的解析同步解析,然后在文檔解析完成后再執(zhí)行這個腳本文件,這樣的話就能使頁面的渲染不被阻塞。多個設(shè)置了 defer 屬性的腳本按規(guī)范來說最后是順序執(zhí)行的,但是在一些瀏覽器中可能不是這樣。第三種方式是給 js 腳本添加 async 屬性,這個屬性會使腳本異步加載,不會阻塞頁面的解析過程,但是當(dāng)腳本加載完成后立即執(zhí)行 js 腳本,這個時候如果文檔沒有解析完成的話同樣會阻塞。多個 async 屬性的腳本的執(zhí)行順序是不可預(yù)測的,一般不會按照代碼的順序依次執(zhí)行。第四種方式是動態(tài)創(chuàng)建 DOM 標(biāo)簽的方式,我們可以對文檔的加載事件進(jìn)行監(jiān)聽,當(dāng)文檔加載完成后再動態(tài)的創(chuàng)建 script 標(biāo)簽來引入 js 腳本。
詳細(xì)資料可以參考:?《JS 延遲加載的幾種方式》?《HTML 5
