1、ES5、ES6和ES2015有什么區別?
ES2015
特指在2015
年發布的新一代JS
語言標准,ES6
泛指下一代JS語言標准,包含ES2015
、ES2016
、ES2017
、ES2018
等。現階段在絕大部分場景下,ES2015
默認等同ES6
。ES5
泛指上一代語言標准。ES2015
可以理解為ES5
和ES6
的時間分界線
2、babel是什么,有什么作用?
babel
是一個ES6
轉碼器,可以將ES6
代碼轉為ES5
代碼,以便兼容那些還沒支持ES6
的平台
3、let有什么用,有了var為什么還要用let?
在
ES6
之前,聲明變量只能用var
,var
方式聲明變量其實是很不合理的,准確的說,是因為ES5
里面沒有塊級作用域是很不合理的。沒有塊級作用域回來帶很多難以理解的問題,比如for
循環var
變量泄露,變量覆蓋等問題。let
聲明的變量擁有自己的塊級作用域,且修復了var
聲明變量帶來的變量提升問題。
4、舉一些ES6對String字符串類型做的常用升級優化?
優化部分
ES6
新增了字符串模板,在拼接大段字符串時,用反斜杠(
)`取代以往的字符串相加的形式,能保留所有空格和換行,使得字符串拼接看起來更加直觀,更加優雅
升級部分
ES6
在String
原型上新增了includes()
方法,用於取代傳統的只能用indexOf
查找包含字符的方法(indexOf
返回-1
表示沒查到不如includes
方法返回false
更明確,語義更清晰), 此外還新增了startsWith()
,endsWith(),
padStart()
,padEnd()
,repeat()
等方法,可方便的用於查找,補全字符串
5、舉一些ES6對Array數組類型做的常用升級優化
優化部分
- 數組解構賦值。
ES6
可以直接以let [a,b,c] = [1,2,3]
形式進行變量賦值,在聲明較多變量時,不用再寫很多let(var),
且映射關系清晰,且支持賦默認值 - 擴展運算符。
ES6
新增的擴展運算符(...
)(重要),可以輕松的實現數組和松散序列的相互轉化,可以取代arguments
對象和apply
方法,輕松獲取未知參數個數情況下的參數集合。(尤其是在ES5
中,arguments
並不是一個真正的數組,而是一個類數組的對象,但是擴展運算符的逆運算卻可以返回一個真正的數組)。擴展運算符還可以輕松方便的實現數組的復制和解構賦值(let a = [2,3,4]
;let b = [...a]
)
升級部分
ES6
在Array
原型上新增了find()
方法,用於取代傳統的只能用indexOf
查找包含數組項目的方法,且修復了indexOf
查找不到NaN的bug([NaN].indexOf(NaN) === -1)
.此外還新增了copyWithin()
,includes()
,fill()
,flat()
等方法,可方便的用於字符串的查找,補全,轉換等
6、舉一些ES6對Number數字類型做的常用升級優化
優化部分
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()
同上)
升級部分
ES6
在Math
對象上新增了Math.cbrt()
,trunc()
,hypot()
等等較多的科學計數法運算方法,可以更加全面的進行立方根、求和立方根等等科學計算
7、舉一些ES6對Object類型做的常用升級優化?(重要)
優化部分
對象屬性變量式聲明。
ES6
可以直接以變量形式聲明對象屬性或者方法,。比傳統的鍵值對形式聲明更加簡潔,更加方便,語義更加清晰
let [apple, orange] = ['red appe', 'yellow orange'];
let myFruits = {apple, orange}; // let myFruits = {apple: 'red appe', orange: 'yellow orange'};
12
尤其在對象解構賦值(見優化部分b.)或者模塊輸出變量時,這種寫法的好處體現的最為明顯
let {keys, values, entries} = Object;
let MyOwnMethods = {keys, values, entries}; // let MyOwnMethods = {keys: keys, values: values, entries: entries}
12
可以看到屬性變量式聲明屬性看起來更加簡潔明了。方法也可以采用簡潔寫法
let es5Fun = {
method: function(){}
};
let es6Fun = {
method(){}
}
123456
對象的解構賦值。
ES6
對象也可以像數組解構賦值那樣,進行變量的解構賦值
let {apple, orange} = {apple: 'red appe', orange: 'yellow orange'};
1
對象的擴展運算符(
...
)。 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};
12345
super
關鍵字。ES6
在Class
類里新增了類似this
的關鍵字super
。同this
總是指向當前函數所在的對象不同,super
關鍵字總是指向當前函數所在對象的原型對象
升級部分
ES6`在`Object`原型上新增了`is()`方法,做兩個目標對象的相等比較,用來完善`'==='`方法。`'==='`方法中`NaN === NaN //false`其實是不合理的,`Object.is`修復了這個小`bug`。`(Object.is(NaN, NaN) // true)
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}
12345
注意:
assign
合並的對象target
只能合並source1
、source2
中的自身屬性,並不會合並source1
、source2
中的繼承屬性,也不會合並不可枚舉的屬性,且無法正確復制get和set屬性(會直接執行get/set
函數,取return
的值)
ES6
在Object
原型上新增了getOwnPropertyDescriptors()
方法,此方法增強了ES5
中getOwnPropertyDescriptor()
方法,可以獲取指定對象所有自身屬性的描述對象。結合defineProperties()
方法,可以完美復制對象,包括復制get
和set
屬性ES6
在Object
原型上新增了getPrototypeOf()
和setPrototypeOf()
方法,用來獲取或設置當前對象的prototype
對象。這個方法存在的意義在於,ES5
中獲取設置prototype
對像是通過__proto__
屬性來實現的,然而__proto__
屬性並不是ES規范中的明文規定的屬性,只是瀏覽器各大產商“私自”加上去的屬性,只不過因為適用范圍廣而被默認使用了,再非瀏覽器環境中並不一定就可以使用,所以為了穩妥起見,獲取或設置當前對象的prototype
對象時,都應該采用ES6新增的標准用法ES6
在Object
原型上還新增了Object.keys()
,Object.values()
,Object.entries()
方法,用來獲取對象的所有鍵、所有值和所有鍵值對數組
8、舉一些ES6對Function函數類型做的常用升級優化?
優化部分
箭頭函數(核心)。箭頭函數是ES6核心的升級項之一,箭頭函數里沒有自己的this,這改變了以往JS函數中最讓人難以理解的this運行機制。主要優化點
- 箭頭函數內的this指向的是函數定義時所在的對象,而不是函數執行時所在的對象。ES5函數里的this總是指向函數執行時所在的對象,這使得在很多情況下
this
的指向變得很難理解,尤其是非嚴格模式情況下,this
有時候會指向全局對象,這甚至也可以歸結為語言層面的bug之一。ES6的箭頭函數優化了這一點,它的內部沒有自己的this
,這也就導致了this
總是指向上一層的this
,如果上一層還是箭頭函數,則繼續向上指,直到指向到有自己this
的函數為止,並作為自己的this
- 箭頭函數不能用作構造函數,因為它沒有自己的
this
,無法實例化 - 也是因為箭頭函數沒有自己的this,所以箭頭函數 內也不存在
arguments
對象。(可以用擴展運算符代替) - 函數默認賦值。
ES6
之前,函數的形參是無法給默認值得,只能在函數內部通過變通方法實現。ES6
以更簡潔更明確的方式進行函數默認賦值
function es6Fuc (x, y = 'default') {
console.log(x, y);
}
es6Fuc(4) // 4, default
1234
升級部分
ES6新增了雙冒號運算符,用來取代以往的
bind
,call
,和apply
。(瀏覽器暫不支持,Babel
已經支持轉碼)
foo::bar;
// 等同於
bar.bind(foo);
foo::bar(...arguments);
// 等同於
bar.apply(foo, arguments);
1234567
9、Symbol是什么,有什么作用?
Symbol
是ES6
引入的第七種原始數據類型(說法不准確,應該是第七種數據類型,Object不是原始數據類型之一,已更正),所有Symbol()生成的值都是獨一無二的,可以從根本上解決對象屬性太多導致屬性名沖突覆蓋的問題。對象中Symbol()
屬性不能被for...in
遍歷,但是也不是私有屬性
10、Set是什么,有什么作用?
Set
是ES6
引入的一種類似Array
的新的數據結構,Set
實例的成員類似於數組item
成員,區別是Set
實例的成員都是唯一,不重復的。這個特性可以輕松地實現數組去重
11、Map是什么,有什么作用?
Map
是ES6
引入的一種類似Object
的新的數據結構,Map
可以理解為是Object
的超集,打破了以傳統鍵值對形式定義對象,對象的key
不再局限於字符串,也可以是Object
。可以更加全面的描述對象的屬性
12、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運氣不行,失敗了
})
1234567891011121314151617181920212223242526272829303132333435
13、Reflect是什么,有什么作用?
Reflect`是`ES6`引入的一個新的對象,他的主要作用有兩點,一是將原生的一些零散分布在`Object`、`Function`或者全局函數里的方法(如`apply`、`delete`、`get`、`set`等等),統一整合到`Reflect`上,這樣可以更加方便更加統一的管理一些原生`API`。其次就是因為`Proxy`可以改寫默認的原生API,如果一旦原生`API`別改寫可能就找不到了,所以`Reflect`也可以起到備份原生API的作用,使得即使原生`API`被改寫了之后,也可以在被改寫之后的`API`用上默認的`API
14、Promise是什么,有什么作用?
Promise
是ES6
引入的一個新的對象,他的主要作用是用來解決JS異步機制里,回調機制產生的“回調地獄”。它並不是什么突破性的API
,只是封裝了異步回調形式,使得異步回調可以寫的更加優雅,可讀性更高,而且可以鏈式調用
15、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 };
}
}
};
}
};
12345678910111213141516171819202122
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}
12345678910111213141516171819202122
16、for…in 和for…of有什么區別?
如果看到問題十六,那么就很好回答。問題十六提到了ES6統一了遍歷標准,制定了可遍歷對象,那么用什么方法去遍歷呢?答案就是用
for...of
。ES6規定,有所部署了載了Iterator
接口的對象(可遍歷對象)都可以通過for...of
去遍歷,而for..in
僅僅可以遍歷對象
- 這也就意味着,數組也可以用
for...of
遍歷,這極大地方便了數組的取值,且避免了很多程序用for..in
去遍歷數組的惡習
17、Generator函數是什么,有什么作用?
- 如果說
JavaScript
是ECMAScript
標准的一種具體實現、Iterator
遍歷器是Iterator
的具體實現,那么Generator
函數可以說是Iterator
接口的具體實現方式。 - 執行
Generator
函數會返回一個遍歷器對象,每一次Generator
函數里面的yield
都相當一次遍歷器對象的next()
方法,並且可以通過next(value)
方法傳入自定義的value,來改變Generator
函數的行為。 Generator
函數可以通過配合Thunk
函數更輕松更優雅的實現異步編程和控制流管理。
18、async函數是什么,有什么作用?
async
函數可以理解為內置自動執行器的Generator
函數語法糖,它配合ES6
的Promise
近乎完美的實現了異步編程解決方案
19、Class、extends是什么,有什么作用?
ES6
的class
可以看作只是一個ES5
生成實例對象的構造函數的語法糖。它參考了java
語言,定義了一個類的概念,讓對象原型寫法更加清晰,對象實例化更像是一種面向對象編程。Class
類可以通過extends
實現繼承。它和ES5構造函數的不同點
類的內部定義的所有方法,都是不可枚舉的
///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); //[]
12345678910111213141516171819202122232425
ES6
的class
類必須用new
命令操作,而ES5
的構造函數不用new
也可以執行。ES6
的class
類不存在變量提升,必須先定義class
之后才能實例化,不像ES5
中可以將構造函數寫在實例化之后。ES5
的繼承,實質是先創造子類的實例對象this
,然后再將父類的方法添加到this
上面。ES6
的繼承機制完全不同,實質是先將父類實例對象的屬性和方法,加到this
上面(所以必須先調用super
方法),然后再用子類的構造函數修改this
。
20、module、export、import是什么,有什么作用?
module
、export
、import
是ES6
用來統一前端模塊化方案的設計思路和實現方案。export
、import
的出現統一了前端模塊化的實現方案,整合規范了瀏覽器/服務端的模塊化方法,用來取代傳統的AMD/CMD
、requireJS
、seaJS
、commondJS
等等一系列前端模塊不同的實現方案,使前端模塊化更加統一規范,JS
也能更加能實現大型的應用程序開發。import
引入的模塊是靜態加載(編譯階段加載)而不是動態加載(運行時加載)。import
引入export
導出的接口值是動態綁定關系,即通過該接口,可以取到模塊內部實時的值
21、日常前端代碼開發中,有哪些值得用ES6去改進的編程優化或者規范?
- 常用箭頭函數來取代
var self = this
;的做法。 - 常用
let
取代var
命令。 - 常用數組/對象的結構賦值來命名變量,結構更清晰,語義更明確,可讀性更好。
- 在長字符串多變量組合場合,用模板字符串來取代字符串累加,能取得更好地效果和閱讀體驗。
- 用
Class
類取代傳統的構造函數,來生成實例化對象。 - 在大型應用開發中,要保持
module
模塊化開發思維,分清模塊之間的關系,常用import
、export
方法。
22、ES6的了解
新增模板字符串(為JavaScript提供了簡單的字符串插值功能)、箭頭函數(操作符左邊為輸入的參數,而右邊則是進行的操作以及返回的值Inputs=>outputs。)、for-of(用來遍歷數據—例如數組中的值。)arguments對象可被不定參數和默認參數完美代替。ES6將promise對象納入規范,提供了原生的Promise對象。增加了let和const命令,用來聲明變量。增加了塊級作用域。let命令實際上就增加了塊級作用域。ES6規定,var命令和function命令聲明的全局變量,屬於全局對象的屬性;let命令、const命令、class命令聲明的全局變量,不屬於全局對象的屬性。。還有就是引入module模塊的概念
23、說說你對Promise的理解
- 依照 Promise/A+ 的定義,Promise 有四種狀態:
- pending: 初始狀態, 非 fulfilled 或 rejected.
- fulfilled: 成功的操作.
- rejected: 失敗的操作.
- settled: Promise已被fulfilled或rejected,且不是pending
- 另外, fulfilled 與 rejected 一起合稱 settled
- Promise 對象用來進行延遲(deferred) 和異步(asynchronous ) 計算
24、Promise 的構造函數
- 構造一個 Promise,最基本的用法如下:
var promise = new Promise(function(resolve, reject) {
if (...) { // succeed
resolve(result);
} else { // fails
reject(Error(errMessage));
}
});
12345678910111213
- Promise 實例擁有 then 方法(具有 then 方法的對象,通常被稱為thenable)。它的使用方法如下:
promise.then(onFulfilled, onRejected)
12
- 接收兩個函數作為參數,一個在 fulfilled 的時候被調用,一個在rejected的時候被調用,接收參數就是 future,onFulfilled 對應 resolve, onRejected 對應 reject
什么是 Promise ?
- Promise 就是一個對象,用來表示並傳遞異步操作的最終結果
- Promise 最主要的交互方式:將回調函數傳入 then 方法來獲得最終結果或出錯原因
- Promise 代碼書寫上的表現:以“鏈式調用”代替回調函數層層嵌套(回調地獄)
解構賦值:
只要判定了等號兩邊的模式相同(解構),左邊的變量就會被賦予對應的值(賦值),以前一次給一個變量賦值,解構賦值可以一次性給多個變量進行賦值。
25、談一談你了解ECMAScript6的新特性?
- 塊級作用區域
let a = 1;
- 可定義常量
const PI = 3.141592654;
- 變量解構賦值
var [a, b, c] = [1, 2, 3];
- 字符串的擴展(模板字符串)
var sum =
${a + b};
- 數組的擴展(轉換數組類型)
Array.from($('li'));
- 函數的擴展(擴展運算符)
[1, 2].push(...[3, 4, 5]);
- 對象的擴展(同值相等算法)
Object.is(NaN, NaN);
- 新增數據類型(Symbol)
let uid = Symbol('uid');
- 新增數據結構(Map)
let set = new Set([1, 2, 2, 3]);
- for…of循環
for(let val of arr){};
- Promise對象
var promise = new Promise(func);
- Generator函數
function* foo(x){yield x; return x*x;}
- 引入Class(類)
class Foo {}
- 引入模塊體系
export default func;
- 引入async函數[ES7]
async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value)
}
123456
26、Object.is() 與原來的比較操作符 =、 的區別?
- == 相等運算符,比較時會自動進行數據類型轉換
- === 嚴格相等運算符,比較時不進行隱式類型轉換
- Object.is 同值相等算法,在 === 基礎上對 0 和 NaN 特別處理
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
123456
27、什么是 Babel
- Babel 是一個 JS 編譯器,自帶一組 ES6 語法轉化器,用於轉化 JS 代碼。
這些轉化器讓開發者提前使用最新的 JS語法(ES6/ES7),而不用等瀏覽器全部兼容。 - Babel 默認只轉換新的 JS 句法(syntax),而不轉換新的API。