我是經過阮一峯老師的ES6教程入門的,基本上是把ES6的幾個核心特性過了一遍,可是面試官一問深我就???了,仍是實際運用的太少。javascript
本篇文章也偏總結類,結合我親身經歷的高頻面試題,建議你們必需要對箭頭函數、Promise、Generator、async等內容深刻理解。前端
工具:Babel是一個 ES6 轉碼器,能夠將 ES6 代碼轉爲 ES5 代碼,以便兼容那些還沒支持ES6的平臺。java
新增了字符串模板,在拼接大段字符串時,用反斜槓取代以往的字符串相加的形式,能保留全部空格和換行,使得字符串拼接看起來更加直觀,更加優雅。es6
新增了includes()方法,用於取代傳統的只能用indexOf查找包含字符的方法, 此外還新增了startsWith(), endsWith(), padStart(),padEnd(),repeat()等方法,可方便的用於查找,補全字符串。面試
數組解構賦值: 如ES6能夠直接以let [a,b,c] = [1,2,3]
形式進行變量賦值,映射關係更清晰。編程
擴展運算符:json
console.log(...[1, 2, 3]) // 1 2 3
複製代碼
能夠實現數組的複製和解構賦值 (let a = [2,3,4]; let b = [...a])
數組
能夠取代arguments對象和apply方法,輕鬆獲取未知參數個數狀況下的參數集合。promise
使用擴展運算符替代函數的apply方法:bash
// ES5 的寫法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f.apply(null, args);
// ES6 寫法
let args = [0, 1, 2];
f(...args);
複製代碼
JS中遍歷數組的方法:
myArray.forEach(function(value){
console.log(value);
})
複製代碼
沒法中途跳出forEach循環,break命令或return命令都不能奏效。
for... in
for…in主要是爲遍歷對象而設計的,不適用於遍歷數組。
遍歷數組的缺點:
for...of
(ES6)for(let value of myArray){
console.log(value);
}
複製代碼
for of 和 for in 的總結:
ES6能夠直接以變量形式聲明對象屬性或者方法。比傳統的鍵值對形式聲明更加簡潔,更加方便,語義更加清晰。
let [apple, orange] = ['red appe', 'yellow orange'];
let myFruits = {apple, orange};
// let myFruits = {apple: 'red appe', orange: 'yellow orange'};
複製代碼
可將一個數組轉爲用逗號分隔的參數序列,主要用於函數調用。 console.log(...[1, 2, 3]) // 1 2 3
ES6在Class類裏新增了相似this的關鍵字super。同this老是指向當前函數所在的對象不一樣,super關鍵字老是指向當前函數所在對象的原型對象。
基本使用:
若是 return 值就只有一行表達式,能夠省去 return,默認表示該行是返回值,不然須要加一個大括號和 return。若是參數只有一個,也能夠省去括號,兩個則須要加上括號。
var f = v => v*2;
// 等價於
var f = function(v){
return v*2;
}
// 判斷偶數
var isEven = n => n % 2 == 0;
// 須要加 return
var = (a, b) => {
if(a >= b)
return a;
return b;
}
複製代碼
ES6 引入 rest 參數(形式爲...變量名),用於獲取函數的多餘參數,這樣就不須要使用arguments對象了。
rest 參數搭配的變量是一個數組,該變量將多餘的參數放入數組中。
//利用 rest 參數,能夠向該函數傳入任意數目的參數。
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
複製代碼
面試問題:箭頭函數和普通函數的區別
函數中的 this 始終是指向函數執行時所在的對象。好比全局函數執行時,this 指向 window,對象的方法執行時,this 指向該對象,這就是函數 this 的可變。
而箭頭函數中的 this 是固定的,箭頭函數繼承本身做用域的上一層的this,就是上一級外部函數的 this 的指向。任何方法都改變不了其指向,如call(), bind(), apply()。
一個例子:
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 }); // id: 42
複製代碼
執行的結果是 42 而不是全局的 21,表示 setTimeout 函數執行的時候,this 指向的不是 window。所以箭頭函數很好地解決了匿名函數和setTimeout
和setInterval
的this指向問題,不用再去給其用that變量存儲this。
對箭頭函數中關於 this 的總結:在對象的方法中直接使用箭頭函數,會指向 window,其餘箭頭函數 this 會指向上一層的 this,箭頭函數並無存儲 this。
var obj = {
id: 1,
foo: ()=>{
return this.id;
}
}
var id = 2;
obj.foo(); // 2
複製代碼
arguments
, 取而代之用rest參數(形式爲...變量名)。也沒有super
、new.target
。yield
命令,箭頭函數不可用做Generator
函數。ES6引入的一種相似Array的新的數據結構,Set實例的成員相似於數組item成員,區別是Set實例的成員都是惟一,不重複的。這個特性能夠輕鬆地實現數組去重。
Set自己是一個構造函數,用來生成 Set 數據結構。
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4
複製代碼
JavaScript 的對象(Object),本質上是鍵值對的集合,可是傳統上只能用字符串看成鍵。這給它的使用帶來了很大的限制。
Map
是ES6引入的一種相似Object的新的數據結構,也是鍵值對的集合,可是「鍵」的範圍不限於字符串,各類類型的值(包括對象)均可以看成鍵。
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
複製代碼
沒有塊級做用域回來帶不少難以理解的問題,好比for循環var變量泄露,變量覆蓋等問題。
let
聲明的變量擁有本身的塊級做用域,形如for (let x...)的循環在每次迭代時都爲x建立新的綁定。且修復了var聲明變量帶來的變量提高問題。(必須聲明 'use strict' 後才能使用let聲明變量,不然瀏覽並不能顯示結果)
「變量提高」現象:即變量能夠在聲明以前使用,值爲undefined。爲了糾正這種現象,let命令改變了語法行爲,它所聲明的變量必定要在聲明後使用,不然報錯。
ES5 只有全局做用域和函數做用域,沒有塊級做用域。
塊級做用域的出現,實際上使得得到普遍應用的當即執行函數表達式(IIFE)再也不必要了。
// IIFE 寫法
(function () {
var tmp = ...;
...
}());
// 塊級做用域寫法
{
let tmp = ...;
...
}
複製代碼
const
聲明一個只讀的常量。一旦聲明,常量的值就不能改變。 const 聲明的變量不得改變值,這意味着,const一旦聲明變量,就必須當即初始化。
const的做用域與let命令相同:只在聲明所在的塊級做用域內有效。
總結: 使用var聲明的變量,其做用域爲該語句所在的函數內,且存在變量提高現象; 使用let聲明的變量,其做用域爲該語句所在的代碼塊內,不存在變量提高; 使用const聲明的是常量,在後面出現的代碼中不能再修改該常量的值。
主要做用是用來解決JS回調機制產生的「回調地獄」。 回調地獄帶來的負面做用有如下幾點:
Promise它不是新的語法功能,而是一種新的寫法,將回調函數的嵌套,改爲鏈式調用。
new Promise(請求1)
.then(請求2(請求結果1))
.then(請求3(請求結果2))
.catch(處理異常(異常信息))
複製代碼
Promise 使用總結:
而後調用上一步返回的 promise 對象的 then 方法,註冊回調函數。 then 中的回調函數能夠有一個參數,也能夠不帶參數。若是 then 中的回調函數依賴上一步的返回結果,那麼要帶上參數。
最後註冊 catch 異常處理函數,處理前面回調中可能拋出的異常。
簡單例子:
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done');
});
}
timeout(100).then((value) => {
console.log(value);
});
複製代碼
timeout方法返回一個Promise實例,表示一段時間之後纔會發生的結果。過了指定的時間(ms參數)之後,Promise實例的狀態變爲resolved,就會觸發then方法綁定的回調函數。
一些經常使用API:
Promise.race
類方法,多個 Promise 任務同時執行,返回最早執行結束的 Promise 任務的結果,無論這個 Promise 結果是成功仍是失敗。
Promise.all
類方法,多個 Promise 任務同時執行。 若是所有成功執行,則以數組的方式返回全部 Promise 任務的執行結果。 若是有一個 Promise 任務 rejected,則只返回 rejected 任務的結果。
若是後續任務是異步任務的話,必須return 一個 新的 promise 對象。若是後續任務是同步任務,只需 return 一個結果便可。
new Promise(買菜)
//用買好的菜作飯
.then((買好的菜)=>{
return new Promise(作飯);
})
複製代碼
一個 Promise 對象有三個狀態,而且狀態一旦改變,便不能再被更改成其餘狀態:
generator 以及 async/await 語法使異步處理更加接近同步代碼寫法,可讀性更好,同時異常捕獲和同步代碼的書寫趨於一致。
(async ()=>{
let 蔬菜 = await 買菜();
let 飯菜 = await 作飯(蔬菜);
let 送飯結果 = await 送飯(飯菜);
let 通知結果 = await 通知我(送飯結果);
})();
複製代碼
Generator 函數會返回一個遍歷器對象,能夠依次遍歷 Generator 函數內部的每個狀態。
形式上,Generator 函數是一個普通函數,可是有兩個特徵。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }
複製代碼
Generator 函數的調用方法與普通函數同樣,也是在函數名後面加上一對圓括號。不一樣的是,調用 Generator 函數後,該函數並不執行,返回的也不是函數運行結果,而是一個指向內部狀態的指針對象,也就是上一章介紹的遍歷器對象。
下一步,必須調用遍歷器對象的next方法,使得指針移向下一個狀態。也就是說,每次調用next方法,內部指針就從函數頭部或上一次停下來的地方開始執行,直到遇到下一個yield表達式(或return語句)爲止。換言之,Generator 函數是分段執行的,yield表達式是暫停執行的標記,而next方法能夠恢復執行。
ES2017 標準引入了 async 函數,使得異步操做變得更加方便。
async 函數就是 Generator 函數的語法糖。
async函數就是將 Generator 函數的星號(*)替換成async,將yield替換成await。而且返回一個
Promise
,可使用then方法添加回調函數。 當函數執行的時候,一旦遇到await就會先返回,等到異步操做完成,再接着執行函數體內後面的語句。
async函數對 Generator 函數的改進:
例子:getJSON函數返回一個promise,這個promise成功resolve時會返回一個json對象。咱們只是調用這個函數,打印返回的JSON對象,而後返回」done」。
// promise
const makeRequest = () =>
getJSON()
.then(data => {
console.log(data)
return "done"
})
makeRequest()
複製代碼
//使用Async/Await
const makeRequest = async () => {
console.log(await getJSON())
return "done"
}
makeRequest()
//async函數會隱式地返回一個promise,該promise的reosolve值就是函數return的值。(示例中reosolve值就是字符串」done」)
複製代碼
Async的優缺點:
優點: 處理 then 的調用鏈可以更清晰準確的寫出代碼。
缺點: 濫用 await 可能會致使性能問題,由於 await 會阻塞代碼,也許以後的異步代碼並不依賴於前者,但仍然須要等待前者完成,致使代碼失去了併發性。
是ES6中一個很重要概念,它並非對象,也不是任何一種數據類型。爲Set、Map、Array、Object新增一個統一的遍歷API。部署了Iterator接口的對象(可遍歷對象)均可以經過for...of
去遍歷。
ES6 的class能夠看做只是一個語法糖,它的絕大部分功能,ES5 均可以作到,新的class寫法只是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已,能夠看作是構造函數的另外一種寫法。
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
//ES6的class改寫
class Point {
//構造方法
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
複製代碼
上面代碼定義了一個「類」,能夠看到裏面有一個constructor方法,這就是構造方法,而this關鍵字則表明實例對象。也就是說,ES5 的構造函數Point,對應 ES6 的Point類的構造方法。
Point類除了構造方法,還定義了一個toString方法。注意,定義「類」的方法的時候,前面不須要加上function這個關鍵字,直接把函數定義放進去了就能夠了。另外,方法之間不須要逗號分隔,加了會報錯。
class實現繼承: Class 能夠經過extends關鍵字實現繼承,這比 ES5 的經過修改原型鏈實現繼承,要清晰和方便不少。
平常前端代碼開發中,有哪些值得用ES6去改進的編程優化或者規範:
var self = this;
的作法。