Babel是一個普遍使用的ES6轉碼器,能夠將ES6代碼轉爲ES5代碼,從而在現有環境執行算法
ES6新增了 let 命令,用來聲明變量。它的用法相似於 var ,可是所聲明的變量,只在 let 命令所在的代碼塊內有效編程
let 不像 var 那樣會發生「變量提高」現象。因此,變量必定要在聲明後使用,不然報錯。windows
只要塊級做用域內存在 let 命令,它所聲明的變量就「綁定」(binding)這個區域,ES6明確規定,若是區塊中存在 let 和 const 命令,這個區塊對這些命令聲明的變量,從一開始就造成了封閉做用域。凡是 在聲明以前就使用這些變量,就會報錯。數組
let不容許在相同做用域內,重複聲明同一個變量瀏覽器
爲何須要塊級做用域?ES5只有全局做用域和函數做用域,沒有塊級做用域,這帶來不少不合理的場景 第一種場景,內層變量可能會覆蓋外層變量 第二種場景,用來計數的循環變量泄露爲全局變量。 let 實際上爲JavaScript新增了塊級做用域緩存
數組解構的元素是按次序排列的,變量的取值由它的位置決定;而對象的屬性沒有次序,變量必須與屬性同名,才能取到正確的值。 對象的解構賦值的內部機制,是先找到同名屬性,而後再賦給對應的變量。真正被賦值的是後者,而不是前者。安全
解構賦值的規則是,只要等號右邊的值不是對象,就先將其轉爲對象,因爲 undefined 和 null 沒法轉爲對象,因此對它們 進行解構賦值,都會報錯。bash
解構賦值對提取JSON對象中的數據,函數參數的默認值,尤爲有用。服務器
模板字符串(template string)是加強版的字符串,用反引號(`)標識。它能夠看成普通字符串使用,也能夠用來定義多行字符 串,或者在字符串中嵌入變量。模板字符串中嵌入變量,須要將變量名寫在 {}若是大括號中的值不是字符串,將按照通常的規則轉爲字符串。好比,大括號中是一個對象,將默認調用對象的 toString 方 法。大括號內部,就是執行JavaScript代碼,所以若是大括號內部是一個字符串,將會原樣輸出數據結構
Number.isFinite()用來檢查一個數值是否爲有限的(finite),即不是Infinity。 Number.isNaN()用來檢查一個值是否爲NaN。 Number.isFinite()對於非數值一概返回false, Number.isNaN()只有對於NaN才返回true,非NaN一概返回false。 ES6 將全局方法parseInt()和parseFloat(),移植到Number對象上面 Number.isInteger()用來判斷一個數值是否爲整數。 Math.trunc方法用於去除一個數的小數部分,返回整數部分。 Math.sign方法用來判斷一個數究竟是正數、負數、仍是零。對於非數值,會先將其轉換爲數值。 它會返回五種值。
ES6 容許爲函數的參數設置默認值,即直接寫在參數定義的後面function Point(x = 0, y = 0) 參數變量是默認聲明的,因此不能用let或const再次聲明。參數默認值是惰性求值的 undefined,結果觸發了默認值,null,就沒有觸發默認值。
這是由於length屬性的含義是,該函數預期傳入的參數個數。某個參數指定默認值之後,預期傳入的參數個數就不包括這個參數了。若是設置了默認值的參數不是尾參數,那麼length屬性也再也不計入後面的參數了。
一旦設置了參數的默認值,函數進行聲明初始化時,參數會造成一個單獨的做用域
ES6 引入 rest
ES6 容許使用「箭頭」(=>)定義函數。箭頭函數和普通函數。前者的this綁定定義時所在的做用域,後者的this指向運行時所在的做用域。this指向的固定化,並非由於箭頭函數內部有綁定this的機制,實際緣由是箭頭函數根本沒有本身的this,致使內部的this就是外層代碼塊的this。正是由於它沒有this,因此也就不能用做構造函數。除了this,如下三個變量在箭頭函數之中也是不存在的,指向外層函數的對應變量:arguments、super、new.target。
ES6 中只要使用尾遞歸,就不會發生棧溢出,相對節省內存。這將大大節省內存。這就是「尾調用優化」的意義。ES6 的尾調用優化只在嚴格模式下開啓,正常模式是無效的。
擴展運算符(spread)是三個點(...)。它比如 rest 參數的逆運算,將一個數組轉爲用逗號分隔的參數序列。容易和rest混淆,兩個不是一個東西。
function f(v, w, x, y, z) { }
const args = [0, 1];
f(-1, ...args, 2, ...[3]);
複製代碼
1.因爲擴展運算符能夠展開數組,因此再也不須要apply方法,將數組轉爲函數的參數了。
2.數組是複合的數據類型,直接複製的話,只是複製了指向底層數據結構的指針,而不是克隆一個全新的數組。擴展運算符提供了複製數組的簡便寫法。
const a1 = [1, 2];
// 寫法一
const a2 = [...a1];
// 寫法二
const [...a2] = a1;
3.合併數組
// ES5
[1, 2].concat(more)
// ES6
[1, 2, ...more]
複製代碼
4.擴展運算符能夠與解構賦值結合起來,用於生成數組。若是將擴展運算符用於數組賦值,只能放在參數的最後一位,不然會報錯。 const [...butLast, last] = [1, 2, 3, 4, 5]; // 報錯
5.擴展運算符還能夠將字符串轉爲真正的數組。 [...'hello'] // [ "h", "e", "l", "l", "o" ]
擴展運算符內部調用的是數據結構的 Iterator 接口,所以只要具備 Iterator 接口的對象,均可以使用擴展運算符,若是對沒有 Iterator 接口的對象,使用擴展運算符,將會報錯。
Array.from方法用於將兩類對象轉爲真正的數組,擴展運算符背後調用的是遍歷器接口(Symbol.iterator),若是一個對象沒有部署這個接口,就沒法轉換。Array.from方法還支持相似數組的對象。所謂相似數組的對象,本質特徵只有一點,即必須有length屬性。所以,任何有length屬性的對象,均可以經過Array.from方法轉爲數組,而此時擴展運算符就沒法轉換。
Array.from還能夠接受第二個參數,做用相似於數組的map方法,用來對每一個元素進行處理,將處理後的值放入返回的數組。第三個參數,用來綁定this。
數組實例的copyWithin方法,在當前數組內部,將指定位置的成員複製到其餘位置(會覆蓋原有成員),而後返回當前數組。也就是說,使用這個方法,會修改當前數組。
[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]
複製代碼
上面代碼表示將從 3 號位直到數組結束的成員(4 和 5),複製到從 0 號位開始的位置,結果覆蓋了原來的 1 和 2。
數組實例的 find() 和 findIndex()
[1, 4, -5, 10].find((n) => n < 0)
// -5
複製代碼
ES6 提供三個新的方法——entries(),keys()和values()——用於遍歷數組。keys()是對鍵名的遍歷、values()是對鍵值的遍歷,entries()是對鍵值對的遍歷。
數組實例的 includes(),沒有該方法以前,咱們一般使用數組的indexOf方法,檢查是否包含某個值。indexOf方法有兩個缺點,一是不夠語義化,它的含義是找到參數值的第一個出現位置,因此要去比較是否不等於-1,表達起來不夠直觀。二是,它內部使用嚴格相等運算符(===)進行判斷,這會致使對NaN的誤判。
ES6 明確將數組的空位轉爲undefined。
ES5 比較兩個值是否相等,只有兩個運算符:相等運算符(==)和嚴格相等運算符(===)。它們都有缺點,前者會自動轉換數據類型,後者的NaN不等於自身,以及+0等於-0 用來解決這個問題。Object.is就是部署這個算法的新方法。它用來比較兩個值是否嚴格相等,與嚴格比較運算符(===)的行爲基本一致。
對象的每一個屬性都有一個描述對象(Descriptor),用來控制該屬性的行爲。Object.getOwnPropertyDescriptor方法能夠獲取該屬性的描述對象。描述對象的enumerable屬性,稱爲」可枚舉性「,若是該屬性爲false,就表示某些操做會忽略當前屬性。
「可枚舉」(enumerable)這個概念的最初目的,就是讓某些屬性能夠規避掉for...in操做,否則全部內部屬性和方法都會被遍歷到。好比,對象原型的toString方法,以及數組的length屬性,就經過「可枚舉性」,從而避免被for...in遍歷到。
Object.getOwnPropertyDescriptor(Object.prototype, 'toString').enumerable
// false
複製代碼
Object.entries 的基本用途是遍歷對象的屬性, Object.entries 方法的一個用處是,將對象轉爲真正的 Map 結構。var map = new Map(Object.entries(obj)); 擴展運算符( ... )用於取出參數對象的全部可遍歷屬性,拷貝到當前對象之中。等同於使用 Object.assign 方法。
let aClone = { ...a };
// 等同於
let aClone = Object.assign({}, a);
複製代碼
ES6引入了一種新的原始數據類型Symbol,表示獨一無二的值。它是JavaScript語言的第七種數據類型,前六種是:Undefined、 Null、布爾值(Boolean)、字符串(String)、數值(Number)、對象(Object)。
Symbol值經過 Symbol
函數生成。這就是說,對象的屬性名如今能夠有兩種類型,一種是原來就有的字符串,另外一種就是新增 的Symbol類型。凡是屬性名屬於Symbol類型,就都是獨一無二的,能夠保證不會與其餘屬性名產生衝突。 在對象的內部,使用Symbol值定義屬性時,Symbol值必須放在方括號之中。
let s = Symbol();
typeof s
// "symbol"
複製代碼
Symbol值能夠顯式轉爲字符串。也能夠轉爲布爾值,可是不能轉爲數值。
Symbol做爲屬性名,該屬性不會出如今 for...in 、 for...of 循環中,也不會 被 Object.keys() 、 Object.getOwnPropertyNames() 返回,它也不是私有屬性,有一 個 Object.getOwnPropertySymbols 方法,能夠獲取指定對象的全部Symbol屬性名。這就形成了一種非私有的內部方法的效果。
Proxy能夠譯爲「代理器」,ES6原生提供Proxy構造函數,用來生成Proxy實例。
ES6提供了新的數據結構Set。它相似於數組,可是成員的值都是惟一的,沒有重複的值。經過 add 方法向Set結構加入成員,結果代表Set結構不會添加劇復的值,一種去除數組重複成員的方法
// 去除數組的重複成員
[...new Set(array)] 或 Array.from(new Set(array));
複製代碼
操做:add(value),delete(value),has(value),clear()
遍歷:keys(),values(),entries(),forEach()
WeakSet結構與Set相似,也是不重複的值的集合。可是,它與Set有兩個區別。
首先,WeakSet的成員只能是對象,而不能是其餘類型的值。
ES6提供了Map數據結構,Object結構提供了「字符串—值」的對應,Map結構提供了「值—值」的對應,是一種更 完善的Hash結構實現,Map的鍵其實是跟內存地址綁定的,只要內存地址不同,就視爲兩個鍵 Map原生提供三個遍歷器生成函數keys(),values(),entries(),forEach()
與其餘數據結構的互相轉換
任何數據結構只要部署,Iterator接口,就能夠完成遍歷操做,引入了 for...of 循環,做爲遍歷全部數據結構的統一的方法。一個數據結構只要部署 了 Symbol.iterator 屬性,就被視爲具備iterator接口,就能夠用 for...of 循環遍歷它的成員。也就是說, for...of 循環 內部調用的是數據結構的 Symbol.iterator 方法。
與其餘遍歷語法的比較:
for循環:寫法比較麻煩;
數組提供內置forEach:沒法中途跳出 forEach 循環,break命令或return命令都不能奏效;
for...of
Promise 是異步編程的一種解決方案,原生提供了Promise對象,有三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗),一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果,Promise也有一些缺點。首先,沒法取消Promise,一旦新建它就會當即執行,沒法中途取消。其次,若是不設置回調函數,Promise內部拋出的錯誤,不會反應到外部。第三,當處於pending狀態時,沒法得知目前進展到哪個階段(剛剛開始仍是即將完成)。 resolve函數的做用是,將Promise對象的狀態從「未完成」變爲「成功」(即從 pending 變爲 resolved) reject函數的做用是,將Promise對象的狀態從「未完成」變爲「失敗」(即從 pending 變爲 rejected)
then方法返回的是一個新的Promise實例(注意,不是原來那個Promise實例)。所以能夠採用鏈式寫法
Promise 對象的錯誤具備「冒泡」性質,會一直向後傳遞,直到被捕獲爲止。也就是說,錯誤老是會被下一個catch語句捕獲。通常來講,不要在then方法裏面定義 Reject 狀態的回調函數(即then的第二個參數),老是使用catch方法。 const p = Promise.all([p1, p2, p3]); Promise.all方法接受一個數組做爲參數,p一、p二、p3都是 Promise 實例。 p的狀態由p一、p二、p3決定,分紅兩種狀況。 (1)只有p一、p二、p3的狀態都變成fulfilled,p的狀態纔會變成fulfilled,此時p一、p二、p3的返回值組成一個數組,傳遞給p的回調函數。 (2)只要p一、p二、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。
Promise.race方法一樣是將多個 Promise 實例,包裝成一個新的 Promise 實例。 const p = Promise.race([p1, p2, p3]);
上面代碼中,只要p一、p二、p3之中有一個實例率先改變狀態,p的狀態就跟着改變。那個率先改變的 Promise 實例的返回值,就傳遞給p的回調函數。
async 函數是什麼?一句話,它就是 Generator 函數的語法糖。async函數就是將 Generator 函數的星號(*)替換成async,將yield替換成await,僅此而已。 async函數對 Generator 函數的改進,體如今如下四點: (1)內置執行器。(2)更好的語義。(3)更廣的適用性。(4)返回值是 Promise。 只有async函數內部的異步操做執行完,纔會執行外面的then方法指定的回調函數
// 函數聲明
async function foo() {}
// 函數表達式
const foo = async function () {};
// 箭頭函數
const foo = async () => {};
複製代碼
若是但願多個請求併發執行,可使用Promise.all方法。 async 函數的實現原理,就是將 Generator 函數和自動執行器,包裝在一個函數裏。
next方法必須是同步的,只要調用就必須馬上返回值。也就是說,一旦執行next方法,就必須同步地獲得value和done這兩個屬性。若是遍歷指針正好指向同步操做,固然沒有問題,但對於異步操做,就不太合適了。目前的解決方法是,Generator 函數裏面的異步操做,返回一個 Thunk 函數或者 Promise 對象,即value屬性是一個 Thunk 函數或者 Promise 對象,等待之後返回真正的值,而done屬性則仍是同步產生的
Generator函數是ES6提供的一種異步編程解決方案,語法行爲與傳統函數徹底不一樣,形式上,Generator函數是一個普通函數,可是有兩個特徵。一是, function 關鍵字與函數名之間有一個星號;二是,函數體 內部使用 yield 語句,定義不一樣的內部狀態. Generator函數的調用方法與普通函數同樣,也是在函數名後面加上一對圓括號。不一樣的是,調用Generator函數後並不執行,返回的也不是函數運行結果,而是一個指向內部狀態的指針對象,必須調用遍歷器對象的next方法,使得指針移向下一個狀態。也就是說,每次調用 next 方法,內部指針就從函數頭部或上一次停下來的地方開始執行,直到遇到下一個 yield 語句(或 return 語句)爲止。
Generator函數是分段執行的, yield 語句是暫停執行的標記,而 next 方法能夠恢復執行。
next方法返回值的value屬性,是Generator函數向外輸出數據;next方法還能夠接受參數,這是向Generator函數體內輸入數據,傳入 Generator 函數,做爲上個階段異步任務的返回結果
Generator函數能夠返回一系列的值,由於能夠有任意多個 yield,yield後面的表達式 123 + 456 ,不會當即求值,只會在 next 方法將指針移到這一句時,纔會求值,Generator函數能夠不用 yield 語句,這時就變成了一個單純的暫緩執行函數。
yield 句自己沒有返回值,或者說老是返回 undefined 。 next 方法能夠帶一個參數,該參數就會被看成上一 個 yield 語句的返回值。
for...of 循環能夠自動遍歷Generator函數,且此時再也不須要調用 next 方法。
yield* 語句,用來在一個Generator函數裏面執行另外一個Generator函數。yield* 不過是 for...of 的一種簡寫形式,yield 命令後面若是不加星號,返回的是整個數組,加了星號就表示返回的是數組的遍歷器對象。
Generator函數不能用NEW(會報錯),又想在函數裏面用THIS ? 首先,生成一個空對象,使用 bind 方法綁定Generator函數內部的 this 。這樣,構造函數調用以 後,這個空對象就是Generator函數的實例對象了。
Generator函數的一個重要實際意義就是用來處理異步操做,改寫回調函數
類的數據類型就是函數,類自己就指向構造函數。
class Point{
// ...
}
複製代碼
typeof Point // "function" constructor 方法是類的默認方法,經過 new 命令生成對象實例時,自動調用該方法。一個類必須有 constructor 方法, 若是沒有顯式定義,一個空的 constructor 方法會被默認添加。
實例的屬性除非顯式定義在其自己(即定義在 this 對象上),不然都是定義在原型上(即定義 在 class 上)。hasOwnProperty方法判斷是自己屬性仍是原型屬性。 Class不存在變量提高(hoist),這一點與ES5徹底不一樣。 Class之間能夠經過extends關鍵字實現繼承,這比ES5的經過修改原型鏈實現繼承,要清晰和方便不少 子類必須在 constructor 方法中調用 super 方法,不然新建實例時會報錯。這是由於子類沒有本身的 this 對象,而是繼 承父類的 this 對象,而後對其進行加工。若是不調用 super 方法,子類就得不到 this 對象。只有調用 super 以後,纔可使用 this 關鍵字 類的繼承是按照下面的模式實現的:Object.setPrototypeOf(B.prototype, A.prototype); Object.getPrototypeOf 方法能夠用來從子類上獲取父類。所以,可使用這個方法判斷,一個類是否繼承了另外一個類。
若是在一個方法前,加上 static 關鍵字,就表示該方法不會被實例繼承,而是直接經過類來調用,這就稱爲「靜態方法」。 父類的靜態方法,能夠被子類繼承
靜態屬性指的是Class自己的屬性,即 Class.propname ,而不是定義在實例對象( this )上的屬性。
修飾器(Decorator)是一個函數,用來修改類的行爲。這是ES7的一個提案,目前Babel轉碼器已經支持。相似和類,方法,屬性添加JAVA註解的功能
在ES6以前,社區制定了一些模塊加載方案,最主要的有CommonJS和AMD兩種。前者用於服務器,後者用於瀏覽器,ES6模塊的設計思想,是儘可能的靜態化,使得編譯時就能肯定模塊的依賴關係,以及輸入和輸出的變量。CommonJS和AMD模塊,都只能在運行時肯定這些東西。 let { stat, exists, readFile } = require('fs') 這種加載稱爲「運行時加載」 import { stat, exists, readFile } from 'fs' 這種加載稱爲「編譯時加載」 ES6的模塊自動採用嚴格模式
模塊功能主要由兩個命令構成: export 和 import,export 命令用於規定模塊的對外接口, import 命令用於輸入其餘 模塊提供的功能,export 和 import 命令若是處於塊級做用域內,就會報錯。由於處於條件代碼塊之中,就無法作靜態優化了,違背了ES6模塊的設計初衷
模塊的總體加載 import * as circle from './circle'; export default 命令,爲模塊指定默認輸出,一個模塊只能有一個默認輸出,因此, import 命令後面纔不用加大括號,由於只可能對應一個方法
ES6模塊是動態引用,而且不會緩存值,模塊裏面的變量綁定其所在的模塊
編碼規範:
(1)let取代var
(2)全局常量和線程安全:在 let 和 const 之間,建議優先使用 const ,尤爲是在全局環境,不該該設置變量,只應設置常量。
..靜態字符串一概使用單引號或反引號,不使用雙引號。動態字符串使用反引號。
..使用數組成員對變量賦值時、函數返回多個值,優先使用解構賦值。
..單行定義的對象,最後一個成員不以逗號結尾。多行定義的對象,最後一個成員以逗號結尾。
..對象儘可能靜態化,一旦定義,就不得隨意添加新的屬性。若是添加屬性不可避免,要使用 Object.assign 方法 ..使用擴展運算符(...)拷貝數組,使用Array.from方法,將相似數組的對象轉爲數組。
..當即執行函數能夠寫成箭頭函數的形式,那些須要使用函數表達式的場合,儘可能用箭頭函數代替。由於這樣更簡潔,並且綁定了this。
..不要在函數體內使用arguments變量,使用rest運算符(...)代替