JS 面試題匯總

說一下JS 中的數(shù)據(jù)類型有哪些

JS 數(shù)據(jù)類型包括 基本 / 引用 / 特殊 數(shù)據(jù)類型:

1.基本數(shù)據(jù)類型:String、Number、Boolean
2.引用數(shù)據(jù)類型:Object、Array、Function
3.特殊數(shù)據(jù)類型:Null、Undefined
4.原始數(shù)據(jù)類型 Symbol (ES6)
5.獨(dú)一無二的值,即 Symbol('1’) != Symbol('1’)

追問:判斷 JS 數(shù)據(jù)類型有幾種方法

常用的有 typeof、instanceof,
不常用的有 constructor、 prototype / toString

1.typeof 是個(gè)一元運(yùn)算,放在任意類型的運(yùn)算數(shù)之前,返回一個(gè) 字符串 說明運(yùn)算數(shù)的類型。
可檢測(cè)出的類型有:
'number'、'string'、'boolean'、'object'
'undefined','function'、'symbol'
其中對(duì)象"object" 包括:Object、Array、new RegExp()、new Date() 和 Null 特殊類型
缺點(diǎn):判斷普通類型沒有問題,但不能準(zhǔn)確判斷 引用數(shù)據(jù)類型

2.instanceof 運(yùn)算符用來檢測(cè)一個(gè)對(duì)象在其原型鏈中是否存在一個(gè)構(gòu)造函數(shù)的 prototype 屬性
通俗講 instanceof 檢測(cè)的是原型,檢測(cè)左邊的對(duì)象是否是右邊類的實(shí)例

 [] instanceof Array ==> true

注意:instanceof 能夠判斷出 [] 是 Array 的實(shí)例,也是 Object 的實(shí)例
因?yàn)?[].proto 指向 Array.prototype,而 Array.prototype.proto 又指向了 Object.prototype,最終 Object.prototype.proto 指向了 null 原型鏈結(jié)束。
類似的還有 new Date(),new Error() 和 new 自定義類()
歸納:所有對(duì)象都是 Object 的實(shí)例 或 Object是一切對(duì)象的父對(duì)象

3.根據(jù)對(duì)象的 constructor 判斷
原理:每個(gè)構(gòu)造函數(shù)都有一個(gè) constructor 屬,指回它本身

[].coconstructor === Array ==> true

判斷 數(shù)字、字符串、函數(shù) 和 日期時(shí),必須得用關(guān)鍵字 new 創(chuàng)建才行
因?yàn)橹挥袠?gòu)造函數(shù)才有 constructor 屬性,還有兩點(diǎn)需要注意:

null 和 undefined 是無效的對(duì)象,因此不會(huì)有 constructor 存在,
函數(shù)的 constructor 是不穩(wěn)定的,當(dāng)重寫 prototype 后,
原有的 constructor 引用會(huì)丟失,constructor 會(huì)默認(rèn)為 Object

4.使用 toString 判斷
toString() 是 Object 的原型方法,該方法默認(rèn)返回當(dāng)前對(duì)象的 [[Class]] 。
這是一個(gè)內(nèi)部屬性,其格式為 [object Xxx] ,其中 Xxx 就是對(duì)象的類型。
對(duì)于 Object 對(duì)象,直接調(diào)用 toString() 就能返回 [object Object] 。
而對(duì)于其他對(duì)象,則需要通過 call / apply 來調(diào)用才能返回正確的類型信息。

Object.prototype.toString.call(undefined) ===  '[object Undefined]'
Object.prototype.toString.call(null) ===  '[object Null]'
Object.prototype.toString.call(123) === '[object Number]'

5.JQuery 提供的 jquery.type()
返回說明操作數(shù)的字符串

jQuery.type(123) === "number"
jQuery.type(undefined) === "undefined"
jQuery.type(null ) === "null "
Query.type(new Date()) === "date"
jQuery.type(new Error()) === "error"

追問:null 和 undefined 有啥區(qū)別?

null:是 Null類型,表示一個(gè) 空對(duì)象指針 或 尚未存在的對(duì)象
即該處不應(yīng)該有值,使用typeof運(yùn)算得到 object ,是個(gè)特殊對(duì)象值,轉(zhuǎn)為數(shù)值為 0。
也可以理解是表示程序級(jí)的、正常的或在意料之中的值的空缺

  1. 作為函數(shù)的參數(shù),表示該函數(shù)的參數(shù)不是對(duì)象
  2. 作為對(duì)象原型鏈的終點(diǎn)
    注意:null 不是一個(gè)對(duì)象,但 typeof null === object 原因是不同的對(duì)象在底層都會(huì)表示為二進(jìn)制,在 JS 中如果二進(jìn)制的前三位都為 0,就會(huì)被判斷為object類型,null 的二進(jìn)制全為 0,自然前三位也是 0,所以 typeof null === 'objcet'

undefined:是Undefined 類型,表示一個(gè) 的原始值 或 缺少值,
即此處應(yīng)該有一個(gè)值,但還沒有定義,使用 typeof undefined === 'undefined',轉(zhuǎn)為數(shù)值為 NaN。
它是在 ECMAScript 第三版引入的預(yù)定義全局變量,為了區(qū)分空指針對(duì)象未初始化的變量。
也可以理解是表示系統(tǒng)級(jí)的、出乎意料的類似錯(cuò)誤的值的空缺
1.變量被聲但沒有賦值時(shí)
2.調(diào)用函數(shù)時(shí),應(yīng)該提供的參數(shù)沒有提供時(shí)
3.對(duì)象沒有賦值的屬性時(shí),屬性值為 undefined
4.函數(shù)沒有返回值時(shí),默認(rèn)返回值為 undefined

追問: JS 有哪些內(nèi)置對(duì)象

數(shù)據(jù)封裝類對(duì)象:Object、Array、Boolean、Number、String
其他對(duì)象:Function、Arguments、Math、Date、RegExp、Error

追問:說說你對(duì)原型和原型鏈的理解

原型: 每一個(gè)構(gòu)造函數(shù)都會(huì)自動(dòng)帶一個(gè) prototype 屬性,是個(gè)指針,指向一個(gè)對(duì)象,就是 原型對(duì)象
原型對(duì)象 上默認(rèn)有一個(gè)屬性constructor ,也是個(gè)指針,指向構(gòu)造函數(shù)本身

  • 優(yōu)點(diǎn):原型對(duì)象上所有的 屬性 和 方法 都能被構(gòu)造函數(shù)的 實(shí)例對(duì)象 共享訪問。
  • 缺點(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ì)一起更改( 這也是 Vue 中 data 為什么是一個(gè)函數(shù)的原因 )。

原型鏈: 每個(gè)實(shí)例對(duì)象都有一個(gè)原型__proto__,這個(gè)原型還可以有它自己的原型,以此類推,形成一個(gè)鏈?zhǔn)浇Y(jié)構(gòu)即原型鏈。
每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象prototype,原型對(duì)象上包含一個(gè)指向構(gòu)造函數(shù)的指針 constructor
而每個(gè)實(shí)例都包含著一個(gè)指向原型對(duì)象的內(nèi)部指針 __proto__。
可以通過內(nèi)部指針 __proto__訪問到原型對(duì)象,原型對(duì)象通過 constructor 找到構(gòu)造函數(shù)。
如果 A對(duì)象 在 B 對(duì)象的原型鏈上,可以說它們是 B對(duì)象繼承了 A對(duì)象。
原型鏈作用:如果試圖訪問對(duì)象的某個(gè)屬性,會(huì)首先在 對(duì)象內(nèi)部 尋找該屬性,直至找不到,然后才在該對(duì)象的原型里去找這個(gè)屬性,以此類推。

new 關(guān)鍵字創(chuàng)建一個(gè)實(shí)例都做了什么?

1.像普通對(duì)象一樣,形成自己的私有作用域( 形參賦值,變量提升 )
2.創(chuàng)建一個(gè)新對(duì)象,將 this 指向這個(gè)新對(duì)象( 構(gòu)造函數(shù)的作用域賦給新對(duì)象 )
3.執(zhí)行構(gòu)造函數(shù)中的代碼,為這個(gè)新對(duì)象添加屬性、方法
4.返回這個(gè)新對(duì)象( 新對(duì)象為構(gòu)造函數(shù)的實(shí)例 )
手寫一個(gè) new 原理如下:

function myNew(fn, ...arg){
    // 創(chuàng)建一個(gè)對(duì)象,讓它的原型鏈指向 fn.prototype
    
    // 普通方法
    // let obj = {};
    // obj.__proto__ = fn.prototype;
    
    // 使用 Object.create([A對(duì)象]):創(chuàng)建一個(gè)空對(duì)象 obj,并讓 obj.__proto__ 等于 A對(duì)象
    let obj = Object.create(fn.prototype);

    fn.call(obj, ...arg);
    return obj;
}

可以用 instanceof 測(cè)試構(gòu)造函數(shù)的prototype屬性是否出現(xiàn)在實(shí)例對(duì)象的原型鏈中
也可以用 obj.hasOwnProperty(prop)測(cè)試對(duì)象自身屬性中是否具有指定的屬性

追問:call / apply / bind 有啥區(qū)別

都是替換函數(shù)中不想要的this
callapply 是臨時(shí)的且立即執(zhí)行,
bind 是永久綁定不立即執(zhí)行,返回一個(gè)新函數(shù),需要時(shí)再去執(zhí)行這個(gè)新函數(shù)。

call: call( thisObj, obj1, obj2... )
要求傳入函數(shù)的參數(shù)必須單獨(dú)傳入

apply: apply(t hisObj, [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ù)提前永久綁定

說說 ES6、ES7、ES8 的新特性

ES6的特性:

1.類(class)
2.模塊化(Module)導(dǎo)出(export)導(dǎo)入(import)
3.箭頭(Arrow)函數(shù)
4.函數(shù)參數(shù)默認(rèn)值
5.模板字符串
6.延展操作符(Spread operator) 和 剩余運(yùn)算符(rest operator)
7.ES6中允許我們?cè)谠O(shè)置一個(gè)對(duì)象的屬性的時(shí)候不指定屬性名
8.Promise 異步編程的解決方案
9.支持 let 與 const 塊級(jí)作用域

ES7的特性

1.includes() 函數(shù)用來判斷一個(gè)數(shù)組是否包含一個(gè)指定的值,返回true / false
2.指數(shù)操作符在ES7中引入了指數(shù)運(yùn)算符,具有與Math.pow(..)等效的計(jì)算結(jié)果

ES8的特性

1.加入了對(duì) async/await 的支持,也就我們所說的異步函數(shù)
2.Object.values() 是一個(gè)與 Object.keys()類似的新函數(shù),但返回的是 Object 自身屬性的所有值,不包括繼承的值
3.Object.entries() 函數(shù)返回一個(gè)給定對(duì)象自身可枚舉屬性的鍵值對(duì)的數(shù)組
4.String.padStart(targetLength,[padString])String.padEnd(targetLength,padString])
5.Object.getOwnPropertyDescriptors() 函數(shù)用來獲取一個(gè)對(duì)象的所有自身屬性的描述符,如果沒有任何自身屬性,則返回空對(duì)象。

require 和 import 區(qū)別

import 和 require都是被模塊化所使用。

1.遵循規(guī)范

  • require 是AMD規(guī)范引入方式
  • import是es6的語法標(biāo)準(zhǔn),如要兼容瀏覽器的話必須轉(zhuǎn)化成es5的語法

2.調(diào)用時(shí)間

  • require是運(yùn)行時(shí)調(diào)用,所以require理論上可以運(yùn)用在代碼的任何地方
  • import是編譯時(shí)調(diào)用,所以必須放在文件開頭

3.本質(zhì)

  • require是賦值過程,其實(shí)require的結(jié)果就是對(duì)象、數(shù)字、字符串、函數(shù)等,再把require的結(jié)果賦值給某個(gè)變量
  • import是解構(gòu)過程,但是目前所有的引擎都還沒有實(shí)現(xiàn)import,我們使用babel支持ES6,也僅僅是將ES6轉(zhuǎn)碼為ES5再執(zhí)行,import語法會(huì)被轉(zhuǎn)碼為require

4.性能

  • require的性能相對(duì)于import稍低,因?yàn)閞equire是在運(yùn)行時(shí)才引入模塊并且還賦值給某個(gè)變量
  • import只需要依據(jù)import中的接口在編譯時(shí)引入指定模塊所以性能稍高

追問:Es6 Module 和 Common.js 的區(qū)別

CommonJS
  • 對(duì)于基本數(shù)據(jù)類型,屬于復(fù)制,會(huì)被模塊緩存??稍诹硪粋€(gè)模塊可以對(duì)該模塊輸出的變量重新賦值。
  • 對(duì)于復(fù)雜數(shù)據(jù)類型,屬于淺拷貝。由于兩個(gè)模塊引用的對(duì)象指向同一個(gè)內(nèi)存空間,因此對(duì)該模塊的值做修改時(shí)會(huì)影響另一個(gè)模塊。
  • 當(dāng)使用 require 命令加載某個(gè)模塊時(shí),就會(huì)運(yùn)行整個(gè)模塊的代碼。
  • common.js 同一個(gè)模塊無論加載多少次,都只會(huì)在第一次加載時(shí)運(yùn)行一次,以后再加載就返回第一次運(yùn)行的結(jié)果,除非手動(dòng)清除系統(tǒng)緩存。
  • 循環(huán)加載時(shí),屬于加載時(shí)執(zhí)行。即腳本代碼在 require 的時(shí)候,就會(huì)全部執(zhí)行。一旦出現(xiàn)某個(gè)模塊被 "循環(huán)加載",就只輸出已經(jīng)執(zhí)行的部分,還未執(zhí)行的部分不會(huì)輸出
ES6 Module 模塊

ES6 模塊中的值屬于動(dòng)態(tài)只讀引用。

  • 只讀:不允許修改引入變量的值,import 的變量是只讀的( 包括 基本/復(fù)雜 數(shù)據(jù)類型 )。當(dāng)模塊遇到 import 命令時(shí),就會(huì)生成一個(gè)只讀引用。等到腳本真正執(zhí)行時(shí),再根據(jù)這個(gè)只讀引用,到被加載的那個(gè)模塊里面去取值。
  • 動(dòng)態(tài):原始值發(fā)生變化,import 加載的值也會(huì)發(fā)生變化( 包括 基本/復(fù)雜 數(shù)據(jù)類型)。
    循環(huán)加載時(shí),ES6 模塊是動(dòng)態(tài)引用( 只要兩個(gè)模塊之間存在某個(gè)引用,代碼就能執(zhí)行 )。
綜上:
  • common.js 是 module.exports / exports 導(dǎo)出,require 導(dǎo)入;ES6 則是 export 導(dǎo)出,import 導(dǎo)入。
  • common.js 是運(yùn)行時(shí)加載模塊,ES6 是在靜態(tài)編譯期間就確定模塊的依賴。
  • ES6 在編譯期間會(huì)將所有 import 提升到頂部,common.js 不會(huì)提升 require。
  • common.js 導(dǎo)出的是一個(gè)值拷貝,會(huì)對(duì)加載結(jié)果進(jìn)行緩存,一旦內(nèi)部再修改這個(gè)值,則不會(huì)同步到外部。ES6 是導(dǎo)出的一個(gè)引用,內(nèi)部修改可以同步到外部。
  • 兩者的循環(huán)導(dǎo)入的實(shí)現(xiàn)原理不同,common.js 是當(dāng)模塊遇到循環(huán)加載時(shí),返回的是當(dāng)前已經(jīng)執(zhí)行的部分的值,而不是代碼全部執(zhí)行后的值,兩者可能會(huì)有差異。ES6 模塊是動(dòng)態(tài)引用,如果使用 import 從一個(gè)模塊加載變量(即import foo from 'foo'),那些變量不會(huì)被緩存,而是成為一個(gè)指向被加載模塊的引用。
  • common.js 中頂層的 this 指向這個(gè)模塊本身,而 ES6 中頂層 this 指向 undefined。

事件委托是什么,原理是什么

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

作用:
  • 提高性能:每一個(gè)函數(shù)都會(huì)占用內(nèi)存空間,只需添加一個(gè)事件處理程序代理所有事件,所占用的內(nèi)存空間更少;
  • 動(dòng)態(tài)監(jiān)聽:使用事件委托可以自動(dòng)綁定動(dòng)態(tài)添加的元素,即新增的節(jié)點(diǎn)不需要主動(dòng)添加也可以具有和其它元素一樣的事件。
如何 阻止冒泡 和 默認(rèn)事件
停止冒泡: 
window.event ? window.event.cancelBubble = true : e.stopPropagation();
阻止默認(rèn)事件: 
window.event ? window.event.returnValue = false : e.preventDefault();

追問:說前端中的事件流

事件發(fā)生時(shí)在元素節(jié)點(diǎn)之間按照特定的順序傳播的過程叫做DOM事件流
共分為三大階段:

捕獲階段(事件從 Document 節(jié)點(diǎn) 自上而下 向目標(biāo)節(jié)點(diǎn)傳播的階段)
目標(biāo)階段(真正的目標(biāo)節(jié)點(diǎn)正在處理事件的階段)
冒泡階段(事件從目標(biāo)節(jié)點(diǎn) 自下而上 向 Document 節(jié)點(diǎn)傳播的階段)

事件冒泡:從事件源逐級(jí)向上傳播到 DOM 最頂層節(jié)點(diǎn)的過程。
事件捕獲:從 DOM 最頂層節(jié)點(diǎn)逐級(jí)向下傳播到事件源的過程。

追問:說說事件隊(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ì)。

任務(wù)隊(duì)列的本質(zhì)

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

主線程(執(zhí)行棧)和 任務(wù)隊(duì)列 先進(jìn)先出 的通信稱為 事件循環(huán)( Event Loop )
主要分為:
宏任務(wù)(macro-task):DOM事件綁定,定時(shí)器,Ajax回調(diào)
微任務(wù)(micro-task):Promise,MutationObserver (html5新特性)
事件循環(huán)機(jī)制:主線程 =>所有微任務(wù) ->宏任務(wù)
先進(jìn)先執(zhí)行,如果里面有微任務(wù),則下一步先執(zhí)行微任務(wù),否則繼續(xù)執(zhí)行宏任務(wù)

setTimeout()
將事件插入到了事件隊(duì)列,必須等到當(dāng)前代碼(執(zhí)行棧)執(zhí)行完,主線程才會(huì)去執(zhí)行它指定的回調(diào)函數(shù)。
當(dāng)主線程時(shí)間執(zhí)行過長(zhǎng),無法保證回調(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) 的效果差不多。

追問:說說堆棧

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

內(nèi)存 一般儲(chǔ)存 引用數(shù)據(jù)類型,JavaScript 不允許直接訪問 堆內(nèi)存 中的位置,需要從 棧中 獲取該對(duì)象的地址引用/指針,再從 堆內(nèi)存 中獲取數(shù)據(jù)。存儲(chǔ)值大小不定,可動(dòng)態(tài)調(diào)整,主要用來存放對(duì)象??臻g大,但是運(yùn)行效率相對(duì)較低,無序存儲(chǔ)可根據(jù)引用直接獲取。

說下代碼執(zhí)行結(jié)果

let obj = {}, a = 0, b = '0';
obj[a] = 123;
obj[b] = 456;
console.log(obj); // {0: 456} 

對(duì)象存在 堆 中,數(shù)字屬性 和 字符串屬性相等
let obj = {}, a = Symbol(1), b = Symbol(1);
obj[a] = 123;
obj[b] = 456;
console.log(obj); // {Symbol(1): 123, Symbol(1): 456}

Symbol 表示獨(dú)一無二的值,即 Symbol(1) != Symbol(1)
let obj = {}, a = {name: '張三'}, b = {name: '李四'};
obj[a] = 123;
obj[b] = 456;
console.log(obj); // {[object Object]: 456}

把對(duì)象作為另一個(gè)對(duì)象的屬性時(shí),會(huì) 調(diào)用 toString 轉(zhuǎn)換為字符串

追問:對(duì)象和數(shù)組有啥區(qū)別

對(duì)象:是包含已命名的值的無序集合,也被稱為關(guān)聯(lián)數(shù)組
數(shù)組:是包含已編碼的值的有序集合

  • 創(chuàng)建方式不同,數(shù)組是[] / new Array,對(duì)象是{} / new Object。
  • 調(diào)用方式不同,數(shù)組是 arr[下標(biāo)],對(duì)象是 obj.加屬性名 / [屬性名]。
  • 數(shù)組是有序數(shù)據(jù)的集合,對(duì)象是無序。
  • 數(shù)組的數(shù)據(jù)沒有名稱,只有下標(biāo),對(duì)象的數(shù)據(jù)需要指定名稱。
  • 數(shù)組的元素可以重復(fù),對(duì)象的屬性是唯一的。
  • 數(shù)組的有長(zhǎng)度,而對(duì)象沒有。

追問:數(shù)組常用的操作方法有哪些

操作數(shù)組:push,splice,join,concat
遍歷數(shù)組:map,forEach,reduce
篩選數(shù)組:filter,some,find,findIndex

追問:如何快速合并兩個(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

追問:map 和 forEach 有何區(qū)別

相同點(diǎn):

  • 都是循環(huán)遍歷數(shù)組中的每一項(xiàng)

  • forEach 和 map方法里每次執(zhí)行匿名函數(shù)都支持3個(gè)參數(shù),
    參數(shù)分別是item(當(dāng)前每一項(xiàng)),index(索引值),arr(原數(shù)組)

  • 匿名函數(shù)中的 this都是指向 window( 在 Vue 中指向 Vue 實(shí)例)
    不同點(diǎn):

  • map() 返回一個(gè)新數(shù)組,原數(shù)組不會(huì)改變,可鏈?zhǔn)秸{(diào)用

  • forEach() 返回值為 undefined,可鏈?zhǔn)秸{(diào)用
    場(chǎng)景:

如只是單純的遍歷可用 forEach()
如操作原數(shù)組得到新數(shù)組可用 map()

追問:什么是數(shù)組扁平化,實(shí)現(xiàn)扁平化的方法有哪些?

數(shù)組扁平化:一個(gè)多維數(shù)組變?yōu)橐痪S數(shù)組,方法如下:
1.flat( ES 6)
flat() 方法會(huì)按照一個(gè)可指定的深度遞歸遍歷數(shù)組,并將所有元素與遍歷到的子數(shù)組中的元素合并為一個(gè)新數(shù)組返回。

let newArray = arr.flat([depth]);
depth值可選: 指定要提取嵌套數(shù)組的結(jié)構(gòu)深度,默認(rèn)值為 1,不確定層級(jí)也可寫 `Infinity`。

2.reduce

function flatten(arr) {  
    return arr.reduce((result, item)=> {
        return result.concat(Array.isArray(item) ? flatten(item) : item);
    }, []);
}

3.String & split

function flatten(arr) {
    return arr.toString().split(',').map(function(item) {
        return Number(item);
    })
} 

4.join & split

function flatten(arr) {
    return arr.join(',').split(',').map(function(item) {
        return parseInt(item);
    })
}

5.擴(kuò)展運(yùn)算符

[].concat(...[1, 2, 3, [4, 5]]);  // [1, 2, 3, 4, 5]

也可以做一個(gè)遍歷,若 arr 中含有數(shù)組則使用一次擴(kuò)展運(yùn)算符,直至沒有為止,如下:
擴(kuò)展運(yùn)算符每次只能展開一層數(shù)組

function flatten(arr) {
    while(arr.some(item=>Array.isArray(item))) {
        arr = [].concat(...arr);
    }
    return arr;
}

6.遞歸

function flatten(arr) { 
  var res = [];
  arr.map(item => {
    if(Array.isArray(item)) {
      res = res.concat(flatten(item));
    } else {
      res.push(item);
    }
  }); 
  return res;
}

追問:說說緩存 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,不能和服務(wù)器做交互。
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

說說深拷貝 和 淺拷貝

淺拷貝:只復(fù)制指向某個(gè)對(duì)象的指針,而不復(fù)制對(duì)象本身,新舊對(duì)象還是共享同一塊內(nèi)存。如果其中一個(gè)對(duì)象改變了這個(gè)地址,就會(huì)影響到另一個(gè)對(duì)象。

  • 直接用=賦值
  • Object.assign
    只是在根屬性(對(duì)象的第一層級(jí))創(chuàng)建了一個(gè)新的對(duì)象,但是對(duì)于屬性的值是仍是對(duì)象的話依然是淺拷貝。
    Object.assign 還有一些注意的點(diǎn)是:
    (1)不會(huì)拷貝對(duì)象繼承的屬性
    (2)不可枚舉的屬性
    (3)屬性的數(shù)據(jù)屬性/訪問器屬性
    (4)可以拷貝Symbol類型
  • for in 循環(huán)只遍歷第一層

深拷貝:將一個(gè)對(duì)象從內(nèi)存中完整的拷貝一份出來,從堆內(nèi)存中開辟一個(gè)新的區(qū)域存放新對(duì)象,且修改新對(duì)象不會(huì)影響原對(duì)象。

  • 用 JSON.stringify 把對(duì)象轉(zhuǎn)換成字符串,再用 JSON.parse 把字符串轉(zhuǎn)換成新的對(duì)象
    注意:屬性值為函數(shù)時(shí)該屬性會(huì)丟失,為正則時(shí)會(huì)轉(zhuǎn)為空對(duì)象,為new Date()時(shí)會(huì)轉(zhuǎn)為字符串
  • 采用遞歸去拷貝所有層級(jí)屬性
  • 用 Slice 實(shí)現(xiàn)對(duì)數(shù)組的深拷貝
  • 使用擴(kuò)展運(yùn)算符實(shí)現(xiàn)深拷貝
// 遞歸算法實(shí)現(xiàn)深克隆
function deepClone(obj){
  if(obj === null) return null;
  if(typeof obj !=='object') return obj;
  if(obj instanceof RegExp) return new RegExp(obj);
  if(obj instanceof Date) return new Date(obj);

  // 克隆的結(jié)果和之前保持相同的所屬類
  let newObj = new obj.constructor;
  for(let key in obj){
    if(obj.hasOwnProperty(key)){
        newObj[key] = deepFn(obj[key]);
    }
  }
  return newObj
}

.說說 DOM 和 BOM

ECMAScript (核心) : 描述了 JS 的語法 和 基本對(duì)象。
文檔對(duì)象模型 (DOM): 處理 網(wǎng)頁內(nèi)容 的方法和接口。
W3C 的標(biāo)準(zhǔn)( 所有瀏覽器公共遵守的標(biāo)準(zhǔn) )
瀏覽器對(duì)象模型 (BOM): 與 瀏覽器交互 的方法和接口。
各個(gè)瀏覽器廠商根據(jù) DOM 在各自瀏覽器上的實(shí)現(xiàn)( 不同廠商之間實(shí)現(xiàn)存在差異 )

DOM 的 API :

節(jié)點(diǎn)創(chuàng)建型 API:

document.createElement(),document.createTextNode(),parent.cloneNode(true)
document.createDocumentFragment() 創(chuàng)建文檔片段,解決大量添加節(jié)點(diǎn)造成的回流問題

頁面修改型 API:

parent.appendChild(child),parent.removeChild(child)
parent.replcaeChild(newChild,oldChild)
parent.insertBefore(newNode, referenceNode)

節(jié)點(diǎn)查詢型 API:

document.getElementById()
document.getElementsByTagName() 返回即時(shí)的 HTMLCollection 類型
document.getElementsByName() 根據(jù)指定的 name 屬性獲取元素,返回即時(shí)的 NodeList
document.getElementsByClassName() 返回即時(shí)的 HTMLCollection
document.querySelector() 獲取匹配到的第一個(gè)元素,采用的是深度優(yōu)先搜索
docuemnt.querySelectorAll() 返回非即時(shí)的 NodeList,也就是說結(jié)果不會(huì)隨著文檔樹的變化而變化

節(jié)點(diǎn)關(guān)系型 API:

父關(guān)系型:
node.parentNode()
兄弟關(guān)系型:
node.previouSibling() 返回節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)(包括元素節(jié)點(diǎn),文本節(jié)點(diǎn),注釋節(jié)點(diǎn))
node.previousElementSibling() 返回前一個(gè)元素節(jié)點(diǎn)
node.nextSibling() 返回下一個(gè)節(jié)點(diǎn)
node.nextElementSibling() 返回下一個(gè)元素節(jié)點(diǎn)

子關(guān)系型

parent.childNodes() 返回一個(gè)即時(shí)的NodeList,包括了文本節(jié)點(diǎn)和注釋節(jié)點(diǎn)
parent.children() 一個(gè)即時(shí)的HTMLCollection,子節(jié)點(diǎn)都是Element
parent.firsrtNode(),parent.lastNode(),hasChildNodes()

元素屬性型 API:

element.setAttribute(“name”,“value”) 為元素添加屬性
element.getAtrribute(“name”) 獲取元素的屬性

元素樣式型 API:

window.getComputedStyle(element) 返回一個(gè)CSSStyleDeclaration,可以從中訪問元素的任意樣式屬性。
element.getBoundingClientRect() 返回一個(gè)DOMRect對(duì)象,里面** 包括了元素相對(duì)于可視區(qū)的位置 top,left**,以及元素的大小,單位為純數(shù)字。可用于判斷某元素是否出現(xiàn)在了可視區(qū)域

BOM的 API :

  • location對(duì)象
    .href、.search、.hash、.port、.hostname、pathname
  • history對(duì)象
    .go(n)(前進(jìn)或后退指定的頁面數(shù))、history.back(后退一頁)、.forward(前進(jìn)一頁)
  • navigator對(duì)象
    navigator:包含了用戶瀏覽器的信息
    navigator.userAgent:返回用戶代理頭的字符串表示(就是包括瀏覽器版本信息等的字符串)
    navigator.cookieEnabled:返回瀏覽器是否支持(啟用) cookie

window對(duì)象方法:

  • alert() -- 顯示帶有一段消息和一個(gè)確認(rèn)按鈕的警告彈出框。
  • confirm() -- 顯示帶有一段消息以及確認(rèn)按鈕和取消按鈕的警告彈出框。
  • prompt() -- 顯示帶有一段消息以及可提示用戶輸入的對(duì)話框和確認(rèn),取消的警告彈出框。
  • open() -- 打開一個(gè)新的瀏覽器窗口或查找一個(gè)已命名的窗口。
  • close() -- 關(guān)閉瀏覽器窗口。
  • setInterval() -- 按照指定的周期(以毫秒計(jì))來調(diào)用函數(shù)或計(jì)算表達(dá)式。每隔多長(zhǎng)時(shí)間執(zhí)行一下這個(gè)函數(shù)
  • clearInterval() -- 取消由 setInterval() 設(shè)置的 timeout。
  • setTimeout() -- 在指定的毫秒數(shù)后調(diào)用函數(shù)或計(jì)算表達(dá)式。
  • clearTimeout() -- 取消由 setTimeout() 方法設(shè)置的 timeout。
  • scrollTo() -- 把內(nèi)容滾動(dòng)到指定的坐標(biāo)。

.js 延遲加載的方式有哪些?

  • fer
    會(huì)告訴瀏覽器立即下載,但延遲整個(gè)頁面都解析完畢之后再執(zhí)行
    按順序依次執(zhí)行
  • async
    不讓頁面等待腳本下載和執(zhí)行,從而異步加載頁面其他內(nèi)容。
    將會(huì)在下載后盡快執(zhí)行,不能保證腳本會(huì)按順序執(zhí)行( 在onload 事件之前完成 )。
  • 動(dòng)態(tài)創(chuàng)建DOM方式(創(chuàng)建script,插入到DOM中,加載完畢后callBack)
  • 使用 setTimeout 延遲方法
  • 讓 JS 最后加載

.說說跨域

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

解決方案:

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

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

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

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

一般是使用for in 來遍歷對(duì)象,for of 遍歷數(shù)組

.instanceof 的原理是什么?

function myInstanceof(left, right) {
  let prototype = right.prototype
  left = left.__proto__
  while (true) {
    if (left === null || left === undefined)
      return false
    if (prototype === left)
      return true
    left = left.__proto__
  }
}
思路:
首先獲取類型的原型
然后獲得對(duì)象的原型
然后一直循環(huán)判斷對(duì)象的原型是否等于類型的原型,直到對(duì)象原型為 null,因?yàn)樵玩溩罱K為 null

. setInterval 存在的問題

定時(shí)器的代碼執(zhí)行部分不斷的被調(diào)入任務(wù)隊(duì)列中,如果定時(shí)器的執(zhí)行時(shí)間比間隔時(shí)間長(zhǎng),最終可能導(dǎo)致定時(shí)器堆疊在一起執(zhí)行。

js 引擎為了解決這個(gè)問題,采用的方式是若任務(wù)隊(duì)列中存在這個(gè)定期器,則不會(huì)將新的定時(shí)器放入任務(wù)隊(duì)列,這樣做的弊端是可能導(dǎo)致某些間隔被跳過。

解決方法:循環(huán)調(diào)用setTimeout來實(shí)現(xiàn)setInterval:(即用setTimeout來實(shí)現(xiàn)setInterval

 setTimeout(function fn(){
    ...
    setTimeout(fn,delay)
},delay)

列舉幾條 JS 的基本代碼規(guī)范

  • 變量和函數(shù)命名要見名知意
  • 當(dāng)命名對(duì)象、函數(shù)和實(shí)例時(shí)使用駝峰命名規(guī)則
  • 請(qǐng)使用 === / !== 來值的比較
  • 對(duì)字符串使用單引號(hào) ''(因?yàn)榇蠖鄷r(shí)候我們的字符串。特別html會(huì)出現(xiàn)")
  • switch 語句必須帶有 default 分支
  • 語句結(jié)束一定要加分號(hào)
  • for 循環(huán)必須使用大括號(hào)
  • 使用 /*.../ 進(jìn)行多行注釋,包括描述,指定類型以及參數(shù)值和返回值

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

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

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

  • 程序開始執(zhí)行前: 程序會(huì)創(chuàng)建全局作用域?qū)ο體indow
  • 定義函數(shù)時(shí)
    在window中創(chuàng)建函數(shù)名變量引用函數(shù)對(duì)象
    函數(shù)對(duì)象的隱藏屬性scope指回函數(shù)來自的全局作用域?qū)ο體indow
  • 調(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>
  • 函數(shù)調(diào)用后
    函數(shù)作用域?qū)ο驛O釋放
    導(dǎo)致AO中局部變量釋放

作用

  • 保存所有的變量
  • 控制變量的使用順序: 先用局部,局部沒有才延作用域鏈向下查找。
?著作權(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),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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