ES6面試、複習乾貨知識點彙總(全)

StevenLikeWatermelon發表於2018-12-10

近期在複習ES6,針對ES6新的知識點,以問答形式整理一個全面知識和問題彙總。(全乾貨,適合對ES6有一定理解的同學複習,以及ES6面試。)

一、問:ES6是什麼,為什麼要學習它,不學習ES6會怎麼樣?

答: ES6是新一代的JS語言標準,對分JS語言核心內容做了升級優化,規範了JS使用標準,新增了JS原生方法,使得JS使用更加規範,更加優雅,更適合大型應用的開發。學習ES6是成為專業前端正規軍的必經之路。不學習ES6也可以寫程式碼打鬼子,但是最多隻能當個游擊隊長。

二、問:ES5、ES6和ES2015有什麼區別?

答: ES2015特指在2015年釋出的新一代JS語言標準,ES6泛指下一代JS語言標準,包含ES2015、ES2016、ES2017、ES2018等。現階段在絕大部分場景下,ES2015預設等同ES6。ES5泛指上一代語言標準。ES2015可以理解為ES5和ES6的時間分界線。

三、問:babel是什麼,有什麼作用?

答:babel是一個 ES6 轉碼器,可以將 ES6 程式碼轉為 ES5 程式碼,以便相容那些還沒支援ES6的平臺。

四、問:let有什麼用,有了var為什麼還要用let?

答: 在ES6之前,宣告變數只能用var,var方式宣告變數其實是很不合理的,準確的說,是因為ES5裡面沒有塊級作用域是很不合理的,甚至可以說是一個語言層面的bug(這也是很多c++、java開發人員看不懂,也瞧不起JS語言的劣勢之一)。沒有塊級作用域回來帶很多難以理解的問題,比如for迴圈var變數洩露,變數覆蓋等問題。let 宣告的變數擁有自己的塊級作用域,且修復了var宣告變數帶來的變數提升問題

五、問:舉一些ES6對String字串型別做的常用升級優化?

答:

1、優化部分:

ES6新增了字串模板,在拼接大段字串時,用反斜槓(`)取代以往的字串相加的形式,能保留所有空格和換行,使得字串拼接看起來更加直觀,更加優雅。

2、升級部分:

ES6在String原型上新增了includes()方法,用於取代傳統的只能用indexOf查詢包含字元的方法(indexOf返回-1表示沒查到不如includes方法返回false更明確,語義更清晰), 此外還新增了startsWith(), endsWith(), padStart(),padEnd(),repeat()等方法,可方便的用於查詢,補全字串。

六、問:舉一些ES6對Array陣列型別做的常用升級優化?

答:

1、優化部分:

a. 陣列解構賦值。ES6可以直接以let [a,b,c] = [1,2,3]形式進行變數賦值,在宣告較多變數時,不用再寫很多let(var),且對映關係清晰,且支援賦預設值。

b. 擴充套件運算子。ES6新增的擴充套件運算子(...)(重要),可以輕鬆的實現陣列和鬆散序列的相互轉化,可以取代arguments物件和apply方法,輕鬆獲取未知引數個數情況下的引數集合。(尤其是在ES5中,arguments並不是一個真正的陣列,而是一個類陣列的物件,但是擴充套件運算子的逆運算卻可以返回一個真正的陣列)。擴充套件運算子還可以輕鬆方便的實現陣列的複製和解構賦值(let a = [2,3,4]; let b = [...a])。

2、升級部分:

ES6在Array原型上新增了find()方法,用於取代傳統的只能用indexOf查詢包含陣列專案的方法,且修復了indexOf查詢不到NaN的bug([NaN].indexOf(NaN) === -1).此外還新增了copyWithin(), includes(), fill(),flat()等方法,可方便的用於字串的查詢,補全,轉換等。

七、問:舉一些ES6對Number數字型別做的常用升級優化?

答:

1、優化部分:

ES6在Number原型上新增了isFinite(), isNaN()方法,用來取代傳統的全域性isFinite(), isNaN()方法檢測數值是否有限、是否是NaN。ES5的isFinite(), isNaN()方法都會先將非數值型別的引數轉化為Number型別再做判斷,這其實是不合理的,最造成isNaN('NaN') === true的奇怪行為--'NaN'是一個字串,但是isNaN卻說這就是NaN。而Number.isFinite()和Number.isNaN()則不會有此類問題(Number.isNaN('NaN') === false)。(isFinite()同上)

2、升級部分:

ES6在Math物件上新增了Math.cbrt(),trunc(),hypot()等等較多的科學計數法運算方法,可以更加全面的進行立方根、求和立方根等等科學計算。

八、問:舉一些ES6對Object型別做的常用升級優化?(重要)

答:

1、優化部分:

a. 物件屬性變數式宣告。ES6可以直接以變數形式宣告物件屬性或者方法,。比傳統的鍵值對形式宣告更加簡潔,更加方便,語義更加清晰。

let [apple, orange] = ['red appe', 'yellow orange'];
let myFruits = {apple, orange};    // let myFruits = {apple: 'red appe', orange: 'yellow orange'};
複製程式碼

尤其在物件解構賦值(見優化部分b.)或者模組輸出變數時,這種寫法的好處體現的最為明顯:

let {keys, values, entries} = Object;
let MyOwnMethods = {keys, values, entries}; // let MyOwnMethods = {keys: keys, values: values, entries: entries}
複製程式碼

可以看到屬性變數式宣告屬性看起來更加簡潔明瞭。方法也可以採用簡潔寫法:

let es5Fun = {
    method: function(){}
}; 
let es6Fun = {
    method(){}
}
複製程式碼

b. 物件的解構賦值。 ES6物件也可以像陣列解構賦值那樣,進行變數的解構賦值:

let {apple, orange} = {apple: 'red appe', orange: 'yellow orange'};
複製程式碼

c. 物件的擴充套件運算子(...)。 ES6物件的擴充套件運算子和陣列擴充套件運算子用法本質上差別不大,畢竟陣列也就是特殊的物件。物件的擴充套件運算子一個最常用也最好用的用處就在於可以輕鬆的取出一個目標物件內部全部或者部分的可遍歷屬性,從而進行物件的合併和分解

let {apple, orange, ...otherFruits} = {apple: 'red apple', orange: 'yellow orange', grape: 'purple grape', peach: 'sweet peach'}; 
// otherFruits  {grape: 'purple grape', peach: 'sweet peach'}
// 注意: 物件的擴充套件運算子用在解構賦值時,擴充套件運算子只能用在最有一個引數(otherFruits後面不能再跟其他引數)
let moreFruits = {watermelon: 'nice watermelon'};
let allFruits = {apple, orange, ...otherFruits, ...moreFruits};
複製程式碼

d. super 關鍵字。ES6在Class類裡新增了類似this的關鍵字super。同this總是指向當前函式所在的物件不同,super關鍵字總是指向當前函式所在物件的原型物件

2、升級部分:

a. ES6在Object原型上新增了is()方法,做兩個目標物件的相等比較,用來完善'==='方法。'==='方法中NaN === NaN //false其實是不合理的,Object.is修復了這個小bug。(Object.is(NaN, NaN) // true)

b. ES6在Object原型上新增了assign()方法,用於物件新增屬性或者多個物件合併。

const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
複製程式碼

注意: assign合併的物件target只能合併source1、source2中的自身屬性,並不會合併source1、source2中的繼承屬性,也不會合並不可列舉的屬性且無法正確複製get和set屬性(會直接執行get/set函式,取return的值)

c. ES6在Object原型上新增了getOwnPropertyDescriptors()方法,此方法增強了ES5中getOwnPropertyDescriptor()方法,可以獲取指定物件所有自身屬性的描述物件。結合defineProperties()方法,可以完美複製物件,包括複製get和set屬性。

d. ES6在Object原型上新增了getPrototypeOf()和setPrototypeOf()方法,用來獲取或設定當前物件的prototype物件。這個方法存在的意義在於,ES5中獲取設定prototype對像是通過__proto__屬性來實現的,然而__proto__屬性並不是ES規範中的明文規定的屬性,只是瀏覽器各大產商“私自”加上去的屬性,只不過因為適用範圍廣而被預設使用了,再非瀏覽器環境中並不一定就可以使用,所以為了穩妥起見,獲取或設定當前物件的prototype物件時,都應該採用ES6新增的標準用法。

d. ES6在Object原型上還新增了Object.keys(),Object.values(),Object.entries()方法,用來獲取物件的所有鍵、所有值和所有鍵值對陣列。

九、問:舉一些ES6對Function函式型別做的常用升級優化?(重要)

答:

1、優化部分:

a. 箭頭函式(核心)。箭頭函式是ES6核心的升級項之一,箭頭函式裡沒有自己的this,這改變了以往JS函式中最讓人難以理解的this執行機制。主要優化點:

Ⅰ.    箭頭函式內的this指向的是函式定義時所在的物件,而不是函式執行時所在的物件。ES5函式裡的this總是指向函式執行時所在的物件,這使得在很多情況下this的指向變得很難理解,尤其是非嚴格模式情況下,this有時候會指向全域性物件,這甚至也可以歸結為語言層面的bug之一。ES6的箭頭函式優化了這一點,它的內部沒有自己的this,這也就導致了this總是指向上一層的this,如果上一層還是箭頭函式,則繼續向上指,直到指向到有自己this的函式為止,並作為自己的this。

Ⅱ.    箭頭函式不能用作建構函式,因為它沒有自己的this,無法例項化。

Ⅲ.   也是因為箭頭函式沒有自己的this,所以箭頭函式 內也不存在arguments物件。(可以用擴充套件運算子代替)

b. 函式預設賦值。ES6之前,函式的形參是無法給預設值得,只能在函式內部通過變通方法實現。ES6以更簡潔更明確的方式進行函式預設賦值。

function es6Fuc (x, y = 'default') {
    console.log(x, y);
}
es6Fuc(4) // 4, default
複製程式碼

2、升級部分:

ES6新增了雙冒號運算子,用來取代以往的bind,call,和apply。(瀏覽器暫不支援,Babel已經支援轉碼)

foo::bar;
// 等同於
bar.bind(foo);

foo::bar(...arguments);
// 等同於
bar.apply(foo, arguments);
複製程式碼

十、問:Symbol是什麼,有什麼作用?

答: Symbol是ES6引入的第七種原始資料型別(說法不準確,應該是第七種資料型別,Object不是原始資料型別之一,已更正),所有Symbol()生成的值都是獨一無二的,可以從根本上解決物件屬性太多導致屬性名衝突覆蓋的問題。物件中Symbol()屬性不能被for...in遍歷,但是也不是私有屬性。

十一、問:Set是什麼,有什麼作用?

答: Set是ES6引入的一種類似Array的新的資料結構,Set例項的成員類似於陣列item成員,區別是Set例項的成員都是唯一,不重複的。這個特性可以輕鬆地實現陣列去重

十二、問:Map是什麼,有什麼作用?

答: Map是ES6引入的一種類似Object的新的資料結構,Map可以理解為是Object的超集,打破了以傳統鍵值對形式定義物件,物件的key不再侷限於字串,也可以是Object。可以更加全面的描述物件的屬性。

十三、問:Proxy是什麼,有什麼作用?

答: Proxy是ES6新增的一個建構函式,可以理解為JS語言的一個代理,用來改變JS預設的一些語言行為,包括攔截預設的get/set等底層方法,使得JS的使用自由度更高,可以最大限度的滿足開發者的需求。比如通過攔截物件的get/set方法,可以輕鬆地定製自己想要的key或者value。下面的例子可以看到,隨便定義一個myOwnObj的key,都可以變成自己想要的函式。

function createMyOwnObj() {
	//想把所有的key都變成函式,或者Promise,或者anything
	return new Proxy({}, {
		get(target, propKey, receiver) {
			return new Promise((resolve, reject) => {
				setTimeout(() => {
					let randomBoolean = Math.random() > 0.5;
					let Message;
					if (randomBoolean) {
						Message = `你的${propKey}運氣不錯,成功了`;
						resolve(Message);
					} else {
						Message = `你的${propKey}運氣不行,失敗了`;
						reject(Message);
					}
				}, 1000);
			});
		}
	});
}

let myOwnObj = createMyOwnObj();

myOwnObj.hahaha.then(result => {
	console.log(result) //你的hahaha運氣不錯,成功了
}).catch(error => {
	console.log(error) //你的hahaha運氣不行,失敗了
})

myOwnObj.wuwuwu.then(result => {
	console.log(result) //你的wuwuwu運氣不錯,成功了
}).catch(error => {
	console.log(error) //你的wuwuwu運氣不行,失敗了
})
複製程式碼

十四、問:Reflect是什麼,有什麼作用?

答: Reflect是ES6引入的一個新的物件,他的主要作用有兩點,一是將原生的一些零散分佈在Object、Function或者全域性函式裡的方法(如apply、delete、get、set等等),統一整合到Reflect上,這樣可以更加方便更加統一的管理一些原生API。其次就是因為Proxy可以改寫預設的原生API,如果一旦原生API別改寫可能就找不到了,所以Reflect也可以起到備份原生API的作用,使得即使原生API被改寫了之後,也可以在被改寫之後的API用上預設的API。

十五、問:Promise是什麼,有什麼作用?

答: Promise是ES6引入的一個新的物件,他的主要作用是用來解決JS非同步機制裡,回撥機制產生的“回撥地獄”。它並不是什麼突破性的API,只是封裝了非同步回撥形式,使得非同步回撥可以寫的更加優雅,可讀性更高,而且可以鏈式呼叫。

十六、問:Iterator是什麼,有什麼作用?(重要)

答: Iterator是ES6中一個很重要概念,它並不是物件,也不是任何一種資料型別。因為ES6新增了Set、Map型別,他們和Array、Object型別很像,Array、Object都是可以遍歷的,但是Set、Map都不能用for迴圈遍歷,解決這個問題有兩種方案,一種是為Set、Map單獨新增一個用來遍歷的API,另一種是為Set、Map、Array、Object新增一個統一的遍歷API,顯然,第二種更好,ES6也就順其自然的需要一種設計標準,來統一所有可遍歷型別的遍歷方式。Iterator正是這樣一種標準。或者說是一種規範理念。

就好像JavaScript是ECMAScript標準的一種具體實現一樣,Iterator標準的具體實現是Iterator遍歷器。Iterator標準規定,所有部署了key值為[Symbol.iterator],且[Symbol.iterator]的value是標準的Iterator介面函式(標準的Iterator介面函式: 該函式必須返回一個物件,且物件中包含next方法,且執行next()能返回包含value/done屬性的Iterator物件)的物件,都稱之為可遍歷物件,next()後返回的Iterator物件也就是Iterator遍歷器

//obj就是可遍歷的,因為它遵循了Iterator標準,且包含[Symbol.iterator]方法,方法函式也符合標準的Iterator介面規範。
//obj.[Symbol.iterator]() 就是Iterator遍歷器
let obj = {
  data: [ 'hello', 'world' ],
  [Symbol.iterator]() {
    const self = this;
    let index = 0;
    return {
      next() {
        if (index < self.data.length) {
          return {
            value: self.data[index++],
            done: false
          };
        } else {
          return { value: undefined, done: true };
        }
      }
    };
  }
};
複製程式碼

ES6給Set、Map、Array、String都加上了[Symbol.iterator]方法,且[Symbol.iterator]方法函式也符合標準的Iterator介面規範,所以Set、Map、Array、String預設都是可以遍歷的。

//Array
let array = ['red', 'green', 'blue'];
array[Symbol.iterator]() //Iterator遍歷器
array[Symbol.iterator]().next() //{value: "red", done: false}

//String
let string = '1122334455';
string[Symbol.iterator]() //Iterator遍歷器
string[Symbol.iterator]().next() //{value: "1", done: false}

//set
let set = new Set(['red', 'green', 'blue']);
set[Symbol.iterator]() //Iterator遍歷器
set[Symbol.iterator]().next() //{value: "red", done: false}

//Map
let map = new Map();
let obj= {map: 'map'};
map.set(obj, 'mapValue');
map[Symbol.iterator]().next()  {value: Array(2), done: false}
複製程式碼

十七、問:for...in 和for...of有什麼區別?

答: 如果看到問題十六,那麼就很好回答。問題十六提到了ES6統一了遍歷標準,制定了可遍歷物件,那麼用什麼方法去遍歷呢?答案就是用for...of。ES6規定,有所部署了載了Iterator介面的物件(可遍歷物件)都可以通過for...of去遍歷,而for..in僅僅可以遍歷物件。

這也就意味著,陣列也可以用for...of遍歷,這極大地方便了陣列的取值,且避免了很多程式用for..in去遍歷陣列的惡習

上面提到的擴充套件運算子本質上也就是for..of迴圈的一種實現。

十八、Generator函式是什麼,有什麼作用?

答: 如果說JavaScript是ECMAScript標準的一種具體實現、Iterator遍歷器是Iterator的具體實現,那麼Generator函式可以說是Iterator介面的具體實現方式。

執行Generator函式會返回一個遍歷器物件,每一次Generator函式裡面的yield都相當一次遍歷器物件的next()方法,並且可以通過next(value)方法傳入自定義的value,來改變Generator函式的行為。

Generator函式可以通過配合Thunk 函式更輕鬆更優雅的實現非同步程式設計控制流管理

十九、async函式是什麼,有什麼作用?

答: async函式可以理解為內建自動執行器的Generator函式語法糖,它配合ES6的Promise近乎完美的實現了非同步程式設計解決方案。

二十、Class、extends是什麼,有什麼作用?

答: ES6 的class可以看作只是一個ES5生成例項物件的建構函式的語法糖。它參考了java語言,定義了一個類的概念,讓物件原型寫法更加清晰,物件例項化更像是一種物件導向程式設計。Class類可以通過extends實現繼承。它和ES5建構函式的不同點:

a. 類的內部定義的所有方法,都是不可列舉的。

///ES5
function ES5Fun (x, y) {
	this.x = x;
	this.y = y;
}
ES5Fun.prototype.toString = function () {
	 return '(' + this.x + ', ' + this.y + ')';
}
var p = new ES5Fun(1, 3);
p.toString();
Object.keys(ES5Fun.prototype); //['toString']

//ES6
class ES6Fun {
	constructor (x, y) {
		this.x = x;
		this.y = y;
	}
	toString () {
		return '(' + this.x + ', ' + this.y + ')';
	}
}

Object.keys(ES6Fun.prototype); //[]
複製程式碼

b.ES6的class類必須用new命令操作,而ES5的建構函式不用new也可以執行。

c.ES6的class類不存在變數提升,必須先定義class之後才能例項化,不像ES5中可以將建構函式寫在例項化之後。

d.ES5 的繼承,實質是先創造子類的例項物件this,然後再將父類的方法新增到this上面。ES6 的繼承機制完全不同,實質是先將父類例項物件的屬性和方法,加到this上面(所以必須先呼叫super方法),然後再用子類的建構函式修改this。

二十一、module、export、import是什麼,有什麼作用?

答: module、export、import是ES6用來統一前端模組化方案的設計思路和實現方案。export、import的出現統一了前端模組化的實現方案,整合規範了瀏覽器/服務端的模組化方法,用來取代傳統的AMD/CMD、requireJS、seaJS、commondJS等等一系列前端模組不同的實現方案,使前端模組化更加統一規範,JS也能更加能實現大型的應用程式開發。

import引入的模組是靜態載入(編譯階段載入)而不是動態載入(執行時載入)。

import引入export匯出的介面值是動態繫結關係,即通過該介面,可以取到模組內部實時的值。

二十二、日常前端程式碼開發中,有哪些值得用ES6去改進的程式設計優化或者規範?

答:

1、常用箭頭函式來取代var self = this;的做法。

2、常用let取代var命令。

3、常用陣列/物件的結構賦值來命名變數,結構更清晰,語義更明確,可讀性更好。

4、在長字串多變數組合場合,用模板字串來取代字串累加,能取得更好地效果和閱讀體驗。

5、用Class類取代傳統的建構函式,來生成例項化物件。

6、在大型應用開發中,要保持module模組化開發思維,分清模組之間的關係,常用import、export方法。

(完)

(洋洋灑灑幾乎全是手動打字,如果有錯誤之處,還請指正!)

相關文章