前端高階面試題之JS

. Javascript有哪幾種數(shù)據(jù)類型

  1. 基本數(shù)據(jù)類型:
    字符串 String
    數(shù)字 Number
    布爾 Boolean
  2. 復(fù)合數(shù)據(jù)類型:
    數(shù)組 Array
    對(duì)象 Object
  3. 特殊數(shù)據(jù)類型:
    Null 空對(duì)象
    Undefined 未定義

判斷js中的數(shù)據(jù)類型的幾種方法?
typeof、instanceof、 constructor、 prototype、 $.type()/jquery.type()

2. Js的事件委托是什么,原理是什么

事件委托: 利用事件冒泡,只指定一個(gè)事件處理程序,就可以管理某一類型的所有事件。
原理:事件委托是利用事件的冒泡原理來實(shí)現(xiàn)的
事件冒泡: 就是事件從最深的節(jié)點(diǎn)開始,然后逐步向上傳播事件。

. 如何阻止冒泡 和 默認(rèn)事件

冒泡型事件:事件按照從最特定的事件目標(biāo)到最不特定的事件目標(biāo)(document對(duì)象)的順序觸發(fā)。
默認(rèn)事件 : 瀏覽器默認(rèn)行為

停止冒泡: 
window.event ? window.event.cancelBubble = true : e.stopPropagation();
阻止默認(rèn)事件: 
window.event ? window.event.returnValue = false : e.preventDefault();

. 怎么添加、移除、復(fù)制、創(chuàng)建、和查找節(jié)點(diǎn)

(1). 創(chuàng)建新節(jié)點(diǎn)
createDocumentFragment() //創(chuàng)建一個(gè)DOM片段
createElement() //創(chuàng)建一個(gè)具體的元素
createTextNode() //創(chuàng)建一個(gè)文本節(jié)點(diǎn)

(2). 添加、移除、替換、插入
appendChild()
removeChild()
replaceChild()
insertBefore()

(3). 查找
getElementsByTagName() //通過標(biāo)簽名稱
getElementsByName() //通過元素的Name屬性的值
getElementById() //通過元素Id,唯一性 ==> previousSibling nextSibling firstChild lastChild
querySelector() //選擇器

. 如何快速合并兩個(gè)數(shù)組?

(a). arrA.concat(arrB)
(b). Array.prototype.push.apply(arrA,arrB);
(c). Array.prototype.concat.apply(arrA,arrB);
(d). Array.prototype.concat.call(arrA,arrB);
(e). 數(shù)組轉(zhuǎn)成字符串拼接在切割成數(shù)組, 或者是循環(huán)其中一個(gè)數(shù)組等...
性能自測(cè)對(duì)比:
Array.prototype.concat.call > Array.prototype.concat.apply > concat > Array.prototype.push.apply

. 隱式轉(zhuǎn)換的場(chǎng)景有哪些?

  1. 如果x不是正常值(比如拋出一個(gè)錯(cuò)誤),中斷執(zhí)行。
  2. 如果y不是正常值,中斷執(zhí)行。
  3. 如果Type(x)與Type(y)相同,執(zhí)行嚴(yán)格相等運(yùn)算x === y。
  4. 如果x是null,y是undefined,返回true。
  5. 如果x是undefined,y是null,返回true。
  6. 如果Type(x)是數(shù)值,Type(y)是字符串,返回x == ToNumber(y)的結(jié)果。
  7. 如果Type(x)是字符串,Type(y)是數(shù)值,返回ToNumber(x) == y的結(jié)果。
  8. 如果Type(x)是布爾值,返回ToNumber(x) == y的結(jié)果。
  9. 如果Type(y)是布爾值,返回x == ToNumber(y)的結(jié)果。
  10. 如果Type(x)是字符串或數(shù)值或Symbol值,Type(y)是對(duì)象,返回x == ToPrimitive(y)的結(jié)果。
    Number( {toString() {return 1} } ) 為1
    對(duì)象和其他類型比較的時(shí)候 對(duì)象轉(zhuǎn)化成為PrimitiveValue
  11. 如果Type(x)是對(duì)象,Type(y)是字符串或數(shù)值或Symbol值,返回ToPrimitive(x) == y的結(jié)果。
  12. 不滿足上述情況返回false。

.說一說閉包

有權(quán)限訪問其它函數(shù)作用域內(nèi)變量的一個(gè)函數(shù)。

在js中,變量分為全局變量和局部變量,局部變量的作用域?qū)儆诤瘮?shù)作用域,在函數(shù)執(zhí)行完以后作用域就會(huì)被銷毀,內(nèi)存也會(huì)被回收,但是由于閉包是建立在函數(shù)內(nèi)部的子函數(shù),由于其可訪問上級(jí)作用域的原因,即使上級(jí)函數(shù)執(zhí)行完,作用域也不會(huì)被銷毀,此時(shí)的子函數(shù)——也就是閉包,便擁有了訪問上級(jí)作用域中變量的權(quán)限,即使上級(jí)函數(shù)執(zhí)行完以后作用域內(nèi)的值也不會(huì)被銷毀。

閉包解決了什么?
本質(zhì)上,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來的一座橋梁。
由于閉包可以緩存上級(jí)作用域,這樣函數(shù)外部就可以訪問到函數(shù)內(nèi)部的變量。
也起到保護(hù)全局不受污染又能隱藏變量的作用。

.call / apply / bind 有啥區(qū)別

都是替換函數(shù)中不想要的this, call 和 apply 是臨時(shí)的, bind 是永久的
call: call(thisObj, obj1, obj2...)
要求傳入函數(shù)的參數(shù)必須單獨(dú)傳入
apply: apply(thisObj, [argArray])
要求傳入函數(shù)的參數(shù)必須放入數(shù)組中整體傳入
apply會(huì)將數(shù)組打散為單個(gè)參數(shù)值分別傳入
bind: 永久綁定函數(shù)中的this, 作用如下:

  1. 創(chuàng)建一個(gè)和原函數(shù)功能完全一樣的新函數(shù).
  2. 將新函數(shù)中的this永久綁定為指定對(duì)象
  3. 將新函數(shù)中的部分固定參數(shù)提前永久綁定

call 與 apply 的 thisObj: 在fun函數(shù)運(yùn)行時(shí)指定的this值。需要注意的是,指定的 this 值并不一定是該函 數(shù)執(zhí)行時(shí)真正的this值,如果這個(gè)函數(shù)處于非嚴(yán)格模式下,則指定為 null 和 undefined的this值會(huì)自動(dòng)指向全局對(duì)象(瀏覽器中就是window對(duì)象),同時(shí)值為原始值(數(shù)字,字符串,布爾值)的this會(huì)指向該原始值的自動(dòng)包裝對(duì)象。

.說一說重載

重載: 相同函數(shù)名,不同參數(shù)列表的多個(gè)函數(shù),在調(diào)用時(shí)可自動(dòng)根據(jù)傳入?yún)?shù)的不同,選擇對(duì)應(yīng)的函數(shù)執(zhí)行。
嚴(yán)格意義上講 js 中是沒有重載的,因?yàn)楹蠖x的函數(shù)會(huì)覆蓋前面的同名函數(shù),但是我們可以通過一些方法來模擬重載。

第一種方法:

利用函數(shù)內(nèi)部的argument, 在內(nèi)部用switch語句,根據(jù)傳入?yún)?shù)的個(gè)數(shù)或類型調(diào)用不同的case語句,從而功能上達(dá)到重載的效果。

第二種方法:
運(yùn)用閉包原理,既然 js 后面的函數(shù)會(huì)覆蓋前面的同名函數(shù),就強(qiáng)行讓所有的函數(shù)都留在內(nèi)存里,等需要的時(shí)候再去找它。

function methodFn(obj,name,func){
    var old = obj[name];
    console.log(old instanceof Function);
    obj[name] = function(){
        console.log(arguments.length+" "+fnc.length);
        if(arguments.length === fnc.length){
            return fnc.apply(this,arguments);
         }else if(typeof old === "function"){
            return old.apply(this,arguments);
         }
     }
}

.什么是作用域鏈(scope chain)

作用域鏈: 由各級(jí)作用域?qū)ο筮B續(xù)引用,形成的鏈?zhǔn)浇Y(jié)構(gòu)

函數(shù)的聲明周期:

  1. 程序開始執(zhí)行前: 程序會(huì)創(chuàng)建全局作用域?qū)ο體indow
  2. 定義函數(shù)時(shí)
    在window中創(chuàng)建函數(shù)名變量引用函數(shù)對(duì)象
    函數(shù)對(duì)象的隱藏屬性scope指回函數(shù)來自的全局作用域?qū)ο體indow
  3. 調(diào)用函數(shù)時(shí)
    創(chuàng)建本次函數(shù)調(diào)用時(shí)使用的AO對(duì)象
    在AO對(duì)象中添加函數(shù)的局部變量
    設(shè)置AO的隱藏屬性parent 指向函數(shù)的祖籍作用域?qū)ο??!獔?zhí)行時(shí),如果AO中沒有的變量可延parnet向祖籍作用域?qū)ο笳摇?/li>
  4. 函數(shù)調(diào)用后
    函數(shù)作用域?qū)ο驛O釋放
    導(dǎo)致AO中局部變量釋放

作用域鏈: 2項(xiàng)任務(wù):

  1. 保存所有的變量
  2. 控制變量的使用順序: 先用局部,局部沒有才延作用域鏈向下查找。

.什么是原型鏈

原型: 每創(chuàng)建一個(gè)函數(shù),該函數(shù)都會(huì)自動(dòng)帶有一個(gè) prototype 屬性。該屬性是一個(gè)指針,指向一個(gè)對(duì)象,該對(duì)象稱之為 原型對(duì)象。其所有的屬性和方法都能被構(gòu)造函數(shù)的實(shí)例對(duì)象共享訪問。原型對(duì)象 上默認(rèn)有一個(gè)屬性 constructor ,也是一個(gè)指針,指向其相關(guān)聯(lián)的構(gòu)造函數(shù)。

每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象 prototype,原型對(duì)象上包含著一個(gè)指向構(gòu)造函數(shù)的指針constructor ,而實(shí)例都包含著一個(gè)指向原型對(duì)象的內(nèi)部指針 __proto__。可以通過內(nèi)部指針 __proto__ 訪問到原型對(duì)象,原型對(duì)象可以通過 constructor 找到構(gòu)造函數(shù)。

原型類型有個(gè)缺點(diǎn):多個(gè)實(shí)例對(duì)引用類型的操作會(huì)被篡改。
因?yàn)槊看螌?shí)例化引用類型的數(shù)據(jù)都指向同一個(gè)地址,所以它們讀/寫的是同一個(gè)數(shù)據(jù),當(dāng)一個(gè)實(shí)例對(duì)其進(jìn)行操作,其他實(shí)例的數(shù)據(jù)就會(huì)一起更改。

原型鏈: 每個(gè)對(duì)象都有一個(gè)原型 __proto__,這個(gè)原型還可以有它自己的原型,以此類推,形成一個(gè)鏈?zhǔn)浇Y(jié)構(gòu)即原型鏈。
如果一個(gè)對(duì)象存在另一個(gè)對(duì)象的原型鏈上,我們可以說:它們是繼承關(guān)系
原型鏈作用: 如果試圖訪問對(duì)象(實(shí)例instance)的某個(gè)屬性,會(huì)首先在對(duì)象內(nèi)部尋找該屬性,直至找不到,然后才在該對(duì)象的原型(instance.prototype)里去找這個(gè)屬性,以此類推
new關(guān)鍵字做了什么?

  1. 創(chuàng)建一個(gè)新對(duì)象
  2. 將構(gòu)造函數(shù)的作用域賦給新對(duì)象(因此this就指向了這個(gè)新對(duì)象)
  3. 執(zhí)行構(gòu)造函數(shù)中的代碼(為這個(gè)新對(duì)象添加屬性、方法)
  4. 如果函數(shù)沒有返回其他對(duì)象,那么new表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新對(duì)象。
  5. 我們稱這個(gè)新對(duì)象為構(gòu)造函數(shù)的實(shí)例。
    instanceof: 用于測(cè)試構(gòu)造函數(shù)的 prototype 屬性是否出現(xiàn)在對(duì)象的原型鏈中
    obj.hasOwnProperty(prop): 指示對(duì)象自身屬性中是否具有指定的屬性

.緩存 SessionStorage,LocalStorage,Cookie

sessionStorage 是會(huì)話級(jí)別存儲(chǔ),只要會(huì)話結(jié)束關(guān)閉窗口,sessionStorage 立即被銷毀。
localStorage 是持久化的本地存儲(chǔ),除非主動(dòng)刪除數(shù)據(jù),否則數(shù)據(jù)是永遠(yuǎn)不會(huì)過期的。
sessionStroagelocalStroage 存儲(chǔ)大小可以達(dá)到 5M。
cookie 的數(shù)據(jù)會(huì)始終在同源 http 請(qǐng)求中攜帶,在瀏覽器和服務(wù)器之間來回傳遞。單個(gè)cookie 不能超過4K,只在設(shè)置的 cookie 過期時(shí)間之前有效,即使窗口關(guān)閉或?yàn)g覽器關(guān)閉 。很多瀏覽器都限制一個(gè)站點(diǎn)最多保存20個(gè) Cookie

.說說跨域

跨域:指一個(gè)域下的文檔或腳本試圖去請(qǐng)求另一個(gè)域下的資源,由于瀏覽器同源策略限制而產(chǎn)生。
同源策略: 同協(xié)議+同端口+同域名。即便兩個(gè)不同的域名指向同一個(gè)ip地址,也非同源。如果缺少了同源策略,瀏覽器很容易受到XSS、CSFR等攻擊。
解決方案:

  1. Vue 配置代理類 proxy
  2. jsonp 利用標(biāo)簽沒有跨越的特點(diǎn),單只能實(shí)現(xiàn)get請(qǐng)求不能post請(qǐng)求
  3. CORS 跨域資源共享,只服務(wù)端設(shè)置Access-Control-Allow-Origin即可,前端無須設(shè)置
  4. nginx代理轉(zhuǎn)發(fā)
  5. window.name + iframe跨域: 通過iframe的src屬性由外域轉(zhuǎn)向本地域,跨域數(shù)據(jù)即由iframe的window.name從外域傳遞到本地域
  6. location.hash + iframe: a欲與b跨域相互通信,通過中間頁c來實(shí)現(xiàn)。 三個(gè)頁面,不同域之間利用iframe的location.hash傳值,相同域之間直接js訪問來通信。
  7. document.domain + iframe跨域(僅限主域相同,子域不同的跨域應(yīng)用場(chǎng)景):兩個(gè)頁面都通過js強(qiáng)制設(shè)置document.domain為基礎(chǔ)主域,就實(shí)現(xiàn)了同域;

.垃圾回收機(jī)制

標(biāo)記清除法(常用): (1).標(biāo)記階段:垃圾回收器會(huì)從根對(duì)象開始遍歷。每一個(gè)可以從根對(duì)象訪問到的對(duì)象都會(huì)被添加一個(gè)標(biāo)識(shí),并稱為可到達(dá)對(duì)象; (2).清除階段:對(duì)堆內(nèi)存從頭到尾進(jìn)行線性遍歷,如發(fā)現(xiàn)有對(duì)象沒有被標(biāo)識(shí)為可到達(dá)對(duì)象,則將此對(duì)象占用的內(nèi)存回收,并且將原來標(biāo)記為可到達(dá)對(duì)象的標(biāo)識(shí)清除,以便進(jìn)行下一次垃圾回收操作;

優(yōu)點(diǎn): 實(shí)現(xiàn)簡單
缺點(diǎn): 可能會(huì)造成大量的內(nèi)存碎片

引用計(jì)數(shù)清除法: (1).引用計(jì)數(shù)的含義就是跟蹤記錄每個(gè)值被引用的次數(shù),當(dāng)聲明了一個(gè)變量并將一個(gè)引用類型賦值給該變量時(shí),這個(gè)值的引用次數(shù)就是1。相反,如果包含對(duì)這個(gè)值引用的變量又取得了另外一個(gè)值,這個(gè)值的引用次數(shù)就減1。 (2).當(dāng)這個(gè)引用次數(shù)變成0時(shí),則說明沒有辦法再訪問這個(gè)值了,就可以將其所占的內(nèi)存空間給回收。這樣,垃圾收集器下次再運(yùn)行時(shí),就會(huì)釋放那些引用次數(shù)為0的值所占的內(nèi)存。

優(yōu)點(diǎn): 可即刻回收垃圾
缺點(diǎn): 計(jì)數(shù)器值的增減處理繁重,實(shí)現(xiàn)繁瑣復(fù)雜,循環(huán)引用無法回收

.箭頭函數(shù)與普通 function 的區(qū)別

1.是匿名函數(shù),不能作為構(gòu)造函數(shù),不能使用new, 沒有construct方法
2.不可使用arguments對(duì)象,該對(duì)象在函數(shù)體內(nèi)不存在。如要用,可用 rest 參數(shù)代替
3.不綁定this,會(huì)捕獲其所在的上下文的this值,作為自己的this值
4.箭頭函數(shù)沒有原型,也就是沒有prototype屬性
5.箭頭函數(shù)的 this 永遠(yuǎn)指向其上下文的 this ,任何方法都改變不了其指向,如 call() , bind() , apply()
6.如果箭頭函數(shù)被包含在一個(gè)非箭頭函數(shù)內(nèi),this值就與該函數(shù)相等,否則this值就是全局對(duì)象
7.箭頭函數(shù)不能當(dāng)做Generator函數(shù),不能使用yield關(guān)鍵字

.事件隊(duì)列

JavaScript語言的一大特點(diǎn)就是單線程,同一個(gè)時(shí)間只能做一件事。
作為瀏覽器腳本語言,JavaScript 的主要用途是與用戶互動(dòng),以及操作DOM。這決定了它只能是單線程,否則會(huì)帶來很復(fù)雜的同步問題。比如,假定JavaScript同時(shí)有兩個(gè)線程,一個(gè)線程在某個(gè)DOM節(jié)點(diǎn)上添加內(nèi)容,另一個(gè)線程刪除了這個(gè)節(jié)點(diǎn),這時(shí)瀏覽器應(yīng)該以哪個(gè)線程為準(zhǔn)?
為了利用多核CPU的計(jì)算能力,HTML5提出Web Worker標(biāo)準(zhǔn),允許JavaScript腳本創(chuàng)建多個(gè)線程,但是子線程完全受主線程控制,且不得操作DOM。所以,這個(gè)新標(biāo)準(zhǔn)并沒有改變JavaScript單線程的本質(zhì)。
setTimeout()
將事件插入到了事件隊(duì)列,必須等到當(dāng)前代碼(執(zhí)行棧)執(zhí)行完,主線程才會(huì)去執(zhí)行它指定的回調(diào)函數(shù)。
當(dāng)主線程時(shí)間執(zhí)行過長,無法保證回調(diào)會(huì)在事件指定的時(shí)間執(zhí)行。
瀏覽器端每次setTimeout會(huì)有4ms的延遲,當(dāng)連續(xù)執(zhí)行多個(gè)setTimeout,有可能會(huì)阻塞進(jìn)程,造成性能問題。
setImmediate()
事件插入到事件隊(duì)列尾部,主線程和事件隊(duì)列的函數(shù)執(zhí)行完成之后立即執(zhí)行。和setTimeout(fn,0)的效果差不多。
服務(wù)端node提供的方法。瀏覽器端最新的api也有類似實(shí)現(xiàn):window.setImmediate,但支持的瀏覽器很少。
process.nextTick()
插入到事件隊(duì)列尾部,但在下次事件隊(duì)列之前會(huì)執(zhí)行。也就是說,它指定的任務(wù)總是發(fā)生在所有異步任務(wù)之前,當(dāng)前主線程的末尾。
大致流程:當(dāng)前”執(zhí)行?!钡奈膊卡C>下一次Event Loop(主線程讀取”任務(wù)隊(duì)列”)之前–>觸發(fā)process指定的回調(diào)函數(shù)。
服務(wù)器端node提供的辦法。用此方法可以用于處于異步延遲的問題。
可以理解為:此次不行,預(yù)約下次優(yōu)先執(zhí)行。

所有任務(wù)可以分成兩種,一種是同步任務(wù)(synchronous),另一種是異步任務(wù)(asynchronous)。同步任務(wù)指的是,在主線程上排隊(duì)執(zhí)行的任務(wù),只有前一個(gè)任務(wù)執(zhí)行完畢,才能執(zhí)行后一個(gè)任務(wù);異步任務(wù)指的是,不進(jìn)入主線程、而進(jìn)入"任務(wù)隊(duì)列"(task queue)的任務(wù),只有"任務(wù)隊(duì)列"通知主線程,某個(gè)異步任務(wù)可以執(zhí)行了,該任務(wù)才會(huì)進(jìn)入主線程執(zhí)行。

(1)所有同步任務(wù)都在主線程上執(zhí)行,形成一個(gè)執(zhí)行棧(execution context stack)。
(2)主線程之外,還存在一個(gè)"任務(wù)隊(duì)列"(task queue)。只要異步任務(wù)有了運(yùn)行結(jié)果,就在"任務(wù)隊(duì)列"之中放置一個(gè)事件。
(3)一旦"執(zhí)行棧"中的所有同步任務(wù)執(zhí)行完畢,系統(tǒng)就會(huì)讀取"任務(wù)隊(duì)列",看看里面有哪些事件。那些對(duì)應(yīng)的異步任務(wù),于是結(jié)束等待狀態(tài),進(jìn)入執(zhí)行棧,開始執(zhí)行。
(4)主線程不斷重復(fù)上面的第三步。

  1. "主線程"(執(zhí)行棧)和任務(wù)隊(duì)列<先進(jìn)先出>(主要是各種I/O操作)的通信,被稱為事件循環(huán)
  2. 宏任務(wù)task(macro-task):script(整體代碼),setTimeout,setInterval,setImmediate,I/O,UI render
  3. 微任務(wù)jobs(micro-task):process.nextTick,Promise,Async/Await(實(shí)際就是promise),MutationObserver(html5新特性)
  4. 事件循環(huán)機(jī)制:主線程 ->所有微任務(wù) ->宏任務(wù)(先進(jìn)先執(zhí)行,如果里面有微任務(wù),則下一步先執(zhí)行微任務(wù),否則繼續(xù)執(zhí)行宏任務(wù))

.構(gòu)造函數(shù) 和 class的區(qū)別

  1. 類的內(nèi)部所有定義的方法,都是不可枚舉的
  2. 類和模塊的內(nèi)部,默認(rèn)就是嚴(yán)格模式(this 實(shí)際指向的是undefined),非嚴(yán)格模式指向window,所以不需要使用use strict指定運(yùn)行模式
  3. 類不存在變量提升(hoist)
  4. 類的方法內(nèi)部如果含有this,它默認(rèn)指向類的實(shí)例
  5. 類的靜態(tài)方法不會(huì)被實(shí)例繼承,靜態(tài)方法里的this指的是類,而不是他的實(shí)例
  6. 類的繼承extends,在constructor中使用super,讓this指向當(dāng)前類,等同于Parent.prototype.constructor.call(this)
  7. 類的數(shù)據(jù)類型就是函數(shù),類本身就指向構(gòu)造函數(shù);使用的時(shí)候,也是直接對(duì)類使用new命令,跟構(gòu)造函數(shù)的用法完全一致;Object.assign方法可以很方便地一次向類添加多個(gè)方法;prototype對(duì)象的constructor屬性,直接指向“類”的本身,這與 ES5 的行為一致;

.說說繼承

  1. 原型鏈繼承(在實(shí)例化一個(gè)類時(shí),新創(chuàng)建的對(duì)象復(fù)制了父類構(gòu)造函數(shù)的屬性和方法,并將proto指向父類的原型對(duì)象,當(dāng)在子類上找不到對(duì)應(yīng)的屬性和方法時(shí),將會(huì)在父類實(shí)例上去找。)
    缺點(diǎn)1:引用缺陷(修改其中一個(gè)Small實(shí)例的父類變量會(huì)影響所有繼承Big的實(shí)例)
    缺點(diǎn)2:無法為不同的實(shí)例初始化繼承來的屬性
  2. 構(gòu)造函數(shù)繼承(在子類的構(gòu)造函數(shù)中執(zhí)行父類的構(gòu)造函數(shù),并為其綁定子類的this,讓父類的構(gòu)造函數(shù)把成員屬性和方法都掛到子類的this上)
    缺點(diǎn):無法訪問原型上的方法
  3. small.getAge() //small.getAge is not a function
    組合式繼承(將原型鏈繼承和構(gòu)造函數(shù)繼承組合到一起, 綜合了原型鏈繼承和構(gòu)造函數(shù)繼承的優(yōu)點(diǎn))
    小缺點(diǎn):調(diào)用了兩次父類構(gòu)造函數(shù)
  4. extends繼承(class和extends是es6新增的,class創(chuàng)建一個(gè)類,extends實(shí)現(xiàn)繼承)

.for in 和 for of的區(qū)別

for in 遍歷的是數(shù)組的索引,for of 遍歷的是數(shù)組的元素值
一般是使用 for in 來遍歷對(duì)象,for of 遍歷數(shù)組

for inindex 索引為字符串型數(shù)字,不能直接進(jìn)行幾何運(yùn)算
for in 遍歷順序有可能不是按照實(shí)際數(shù)組的內(nèi)部順序
因?yàn)?for in 是遍歷可枚舉的屬性,也包括原型上的屬性。如果不想遍歷原型上的屬性,可以通過hasOwnProperty來判斷某個(gè)屬性是屬于原型還是實(shí)例上

for of 只是遍歷數(shù)組的內(nèi)部,不會(huì)遍歷原型上的屬性和索引
也可以通過ES5的 Object.keys(obj) 來獲取實(shí)例對(duì)象上的屬性組成的數(shù)組

.map 和 forEach有何區(qū)別

相同點(diǎn):

  1. 都是循環(huán)遍歷數(shù)組中的每一項(xiàng)
  2. forEach 和 map方法里每次執(zhí)行匿名函數(shù)都支持3個(gè)參數(shù),參數(shù)分別是item(當(dāng)前每一項(xiàng)),index(索引值),arr(原數(shù)組)
  3. 匿名函數(shù)中的 this 都是指向 window
  4. 只能遍歷數(shù)組, 都不會(huì)改變?cè)瓟?shù)組
    不同點(diǎn):
  5. map() 取出原數(shù)組中每個(gè)元素,執(zhí)行相同操作后,放入一個(gè)新數(shù)組中返回,不修改原數(shù)組,僅返回新數(shù)組
var 新數(shù)組 = arr.map(function(val,i,arr){
       return 新值;
     });
  1. forEach 對(duì)原數(shù)組中每個(gè)元素執(zhí)行相同操作,直接修改原數(shù)組,返回值為undefined, 不可以鏈?zhǔn)秸{(diào)用
    ···
    arr.forEach(function(val,i,arr){
    arr[i] = 新值;
    })
    ···

.filter 和 reduce

filter : 篩選出原數(shù)組中符合條件的元素組成新數(shù)組,原數(shù)組不變。

var subArr=arr.filter(function(val,i,arr){
    return 判斷條件
})

reduce: 將數(shù)組中每個(gè)元素的值,匯總成一個(gè)最終結(jié)果

var result=arr.reduce(function(prev,val,i,arr){
    return prev+val;//累加
},base);

.Virtual Dom 的優(yōu)勢(shì)在哪里?

重點(diǎn): VDOM 想解決的問題以及為什么頻繁的 DOM 操作會(huì)性能差?

首先我們需要知道:

DOM 引擎、JS 引擎 相互獨(dú)立,但又工作在同一線程(主線程) JS 代碼調(diào)用 DOM API 必須 掛起 JS 引擎、轉(zhuǎn)換傳入?yún)?shù)數(shù)據(jù)、激活 DOM 引擎,DOM 重繪后再轉(zhuǎn)換可能有的返回值,最后激活 JS 引擎并繼續(xù)執(zhí)行若有頻繁的 DOM API 調(diào)用,且瀏覽器廠商不做“批量處理”優(yōu)化, 引擎間切換的單位代價(jià)將迅速積累若其中有強(qiáng)制重繪的 DOM API 調(diào)用,重新計(jì)算布局、重新繪制圖像會(huì)引起更大的性能消耗。

其次是 VDOM 和真實(shí) DOM 的區(qū)別和優(yōu)化:

虛擬 DOM 不會(huì)立馬進(jìn)行排版與重繪操作
虛擬 DOM 進(jìn)行頻繁修改,然后一次性比較并修改真實(shí) DOM 中需要改的部分,最后在真實(shí) DOM 中進(jìn)行排版與重繪,減少過多DOM節(jié)點(diǎn)排版與重繪損耗
虛擬 DOM 有效降低大面積真實(shí) DOM 的重繪與排版,因?yàn)樽罱K與真實(shí) DOM 比較差異,可以只渲染局部。

.說說堆棧

棧內(nèi)存一般儲(chǔ)存基礎(chǔ)數(shù)據(jù)類型,遵循后進(jìn)先出的原則,大小固定并且有序;堆內(nèi)存一般儲(chǔ)存引用數(shù)據(jù)類型,JavaScript不允許直接訪問堆內(nèi)存中的位置,因此當(dāng)我們要訪問堆內(nèi)存中的引用數(shù)據(jù)類型時(shí),實(shí)際上我們首先是從棧中獲取了該對(duì)象的地址引用(或者地址指針),然后再從堆內(nèi)存中取得我們需要的數(shù)據(jù)

棧:存儲(chǔ)基礎(chǔ)數(shù)據(jù)類型;按值訪問;存儲(chǔ)的值大小固定;由系統(tǒng)自動(dòng)分配內(nèi)存空間;空間小,運(yùn)行效率高;先進(jìn)后出,后進(jìn)先出;棧中的DOM,ajax,setTimeout會(huì)依次進(jìn)入到隊(duì)列中,當(dāng)棧中代碼執(zhí)行完畢后,再將隊(duì)列中的事件放到執(zhí)行棧中依次執(zhí)行

堆: 存儲(chǔ)引用數(shù)據(jù)類型;按引用訪問;存儲(chǔ)的值大小不定,可動(dòng)態(tài)調(diào)整;主要用來存放對(duì)象;空間大,但是運(yùn)行效率相對(duì)較低;無序存儲(chǔ),可根據(jù)引用直接獲取

實(shí)現(xiàn)一個(gè)函數(shù)clone 可以對(duì) Javascript 中的五種主要數(shù)據(jù)類型(Number、string、Object、Array、Boolean)進(jìn)行復(fù)制

Object.prototype.clone = function(){
    var obj = this.constructor === Array? [] : {};
    for(var e in this){
        obj [e] = typeof this[e] === "object" ? this[e].clone() : this[e];
    }
    return obj ;
};

//重寫trim方法
if(!String.prototype.trim){
String.prototype.trim = function(){
return this.replace(/^\s+/,"").replace(/\s+/,""); } } //寫fntrim去掉左右空格 function fntrim(str){ return str.replace(/^\s+/,"").replace(/\s+/,"");
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容