僅將本身的理解作整理、歸類並結合實際遇到的問題作記錄,更推薦閱讀 ECMAScript 6 入門。git
「ES6 中容許按照必定模式,從數組和對象中提取值,對變量進行賦值,這被稱爲解構(Destructuring)」es6
做用:能夠快速取得數組或對象當中的元素或屬性,而無需使用arr[x]或者obj[key]等傳統方式進行賦值github
let [a,b,c] = [1,2,3];
複製代碼
「擴展運算符是三個點,它如同rest參數的逆運算,將一個數組轉爲用逗號分隔的參數序列」編程
做用:把數組或類數組對象展開成一系列用逗號隔開的值數組
console.log(...[1,2,3])
//1 2 3
console.log([1,2,3].join())
//1,2,3
console.log(typeof(...[1,2,3]))
//Uncaught SyntaxError: Unexpected token ...
console.log(typeof([1,2,3].join()))
//string
* 爲何typeof(...[1,2,3])報錯Uncaught SyntaxError: Unexpected token ...???
複製代碼
經常使用promise
let [head,...tail] = [1,2,3,4]; //head:1,tail:[2,3,4]
複製代碼
let colors = ["red","green","blue"];
let [...cloneColors] = colors;
console.log(cloneColors) //["red", "green", "blue"]
複製代碼
1.將數組轉爲函數的參數,替代 apply 方法bash
Math.max.apply(null,[14,3,77])//ES5
Math.max(...[14,3,77])//ES6
複製代碼
2.合併數組數據結構
var arr1=['a','b']
var arr2=['c','d']
console.log(arr1.concat(arr2))//ES5
console.log([...arr1,...arr2])//ES6
複製代碼
「Array.from方法用於將兩類對象轉成真正的數組」app
「Array.of方法用於將一組值轉換爲數組」異步
經常使用
console.log(Array.of(3,11,8))//[3, 11, 8]
複製代碼
複製替換數據
Array.prototype.copyWithin(target,start=0,end=this.length)
複製代碼
target:必選,從該位置開始替換 start:替換內容的起點 end:到該位置前中止讀取
console.log([1,2,3,4,5].copyWithin(0,3,4))//[4, 2, 3, 4, 5]
console.log(['a','b','c'].copyWithin(0,1,3))//["b", "c", "c"]
複製代碼
1.for...in
2.Object.keys()
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']
複製代碼
對象的屬性沒有次序,變量必須與屬性同名,才能取到正確的值
let { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
複製代碼
console.log('foo'.split(''))//["f", "o", "o"]//ES5
複製代碼
for(let codePoint of 'foo'){
console.log(codePoint)//'f',"o","o"//ES6
}
複製代碼
console.log('lallallal'.indexOf('a'))//1 Number//ES5
複製代碼
console.log('lallallal'.charAt('a'))//1 String//ES6
複製代碼
function log(x,y='world'){
console.log(x,y)
}
log('moximoxi')//moximoxi world
複製代碼
function test(...values){
console.log(values)
for(var val of values){
console.log(val)
}//for...of能夠不只能夠遍歷字符串,還能夠遍歷數組 ES6
}
test(1,2,3)//[1, 2, 3]
複製代碼
name屬性:function foo(){} foo.name//"foo"
複製代碼
函數名=參=>返回值
某個函數的最後一步是調用另外一個函數
from MDN:
var promise1 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 300);
});
promise1.then(function(value) {
console.log(value);
// expected output: "foo"
});
console.log(promise1);
// expected output: [object Promise]
複製代碼
from CSDN:
function* helloGenerator() {
console.log("this is generator");
}
var h = helloGenerator();
h.next();
複製代碼
function* helloGenerator() {
yield "hello";
yield "generator";
return;
}
var h = helloGenerator();
console.log(h.next());//{ value: 'hello', done: false }
console.log(h.next());//{ value: 'generator', done: false }
console.log(h.next());//{ value: 'undefined', done: true }
複製代碼
yield實際就是暫緩執行的標示,每執行一次next(),至關於指針移動到下一個yield位置。
總結一下,Generator函數是ES6提供的一種異步編程解決方案。經過yield標識位和next()方法調用,實現函數的分段執行。
迭代器=>Generator=>async
1.迭代器:不暴露對象的內部表示的狀況下,可以遍歷整個元素
遍歷 ~ Traverse 訪問一個集合(廣義)的每一個元素
迭代 ~ Iterate 反覆調用同一個過程最終達成目的(迭代是循環的一種方式),這個過程若是是一個函數,那就是遞歸,若是是一個循環體,那就是狹義上的迭代。遞歸和迭代之間的關係、轉換、優化等等又是另外一個故事了。
function makeIterator (arr) {
let nextIndex = 0;
//返回一個迭代器方法
return {
next: () => {
if(nextIndex < arr.length){
return {value: arr[nextIndex++], done: false}
}else{
return {done: true}
}
}
}
}
const it = makeIterator([1,2,3]);
console.log('1:', it.next());
console.log('2:', it.next());
console.log('3:', it.next());
console.log('end:', it.next());
複製代碼
2.Generator函數執行後會返回一個迭代器
3.async函數是Generator的語法糖
async 函數返回一個Promise對象,可使用 then 方法添加回調函數
(function(){
let pro=new Promise((resolve,reject)=>{
resolve(true)
})
console.log(pro)//promise對象
})()
(async function(){
let pro=await new Promise((resolve,reject)=>{
resolve(true)
})
console.log(pro)//true
//(async函數的返回值是一個promise對象;waite命令後面是一個Promise對象,若是不是,會被轉成一個當即resolve的Promise對象)
})()
複製代碼
Set 是成員值惟一的數據結構,相似於數組 Array
因此,有一種重要且好用的去重方法:
var set1 = Array.from(new Set([1,1,2,2,33,'aa','aa','bb'
]))
console.log(set1)//[1, 2, 33, "aa", "bb"]
或:
var set1 = [...new Set([1,1,2,2,33,'aa','aa','bb'
])]
console.log(set1)//[1, 2, 33, "aa", "bb"]
複製代碼
經常使用
Set 實例的方法分爲兩大類:操做方法(用於操做數據)、遍歷方法(用於遍歷成員)
分類 | 方法 |
---|---|
操做方法 | Set.prototype.add(value):添加某個值,返回 Set 結構自己 Set.prototype.delete(value):刪除某個值,返回一個布爾值,表示刪除是否成功。 Set.prototype.has(value):返回一個布爾值,表示該值是否爲Set的成員。 Set.prototype.clear():清除全部成員,沒有返回值。 |
遍歷方法 | Set.prototype.keys():返回鍵名的遍歷器 Set.prototype.values():返回鍵值的遍歷器 Set.prototype.entries():返回鍵值對的遍歷器 Set.prototype.forEach():使用回調函數遍歷每一個成員 |
Map 是能夠用非字符串看成鍵的鍵值對數據結構,相似於對象 Object
Map 結構提供了「值—值」的對應,是一種更完善的 Hash 結構實現。
經常使用
Map 實例的方法分爲兩大類:操做方法(用於操做數據)、遍歷方法(用於遍歷成員)
分類 | 方法 |
---|---|
操做方法 | Map.prototype.set(key, value):設置 Map.prototype.get(key):獲取 Map.prototype.has(key):判斷存在 Map.prototype.delete(key):刪除 Map.prototype.clear():清除 |
遍歷方法 | Map.prototype.keys():返回鍵名的遍歷器。 Map.prototype.values():返回鍵值的遍歷器。 Map.prototype.entries():返回全部成員的遍歷器。 Map.prototype.forEach():遍歷 Map 的全部成員。 |
Proxy 用於修改某些操做的默認行爲,等同於在語言層面作出修改,因此屬於一種「元編程」(meta programming),即對編程語言進行編程。
Proxy 能夠理解成,在目標對象以前架設一層「攔截」,外界對該對象的訪問,都必須先經過這層攔截,所以提供了一種機制,能夠對外界的訪問進行過濾和改寫。Proxy 這個詞的原意是代理,用在這裏表示由它來「代理」某些操做,能夠譯爲「代理器」。
Proxy就是對象的攔截器
典例:set()方法:用validator攔截person.age的建立;
let validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
// 對於知足條件的 age 屬性以及其餘屬性,直接保存
obj[prop] = value;
}
};
let person = new Proxy({}, validator);
person.age = 100;
person.age // 100
person.age = 'young' // The age is not an integer
person.age = 300 // The age seems invalid
複製代碼
將Object對象的一些明顯屬於語言內部的方法(好比Object.defineProperty),放到Reflect對象上。
爲了修改某些Object方法的返回結果,讓其變得更合理。
例:用defineProperty,不拋出錯誤,返回false:
// 老寫法
try {
Object.defineProperty(target, property, attributes);
// success
} catch (e) {
// failure
}
// 新寫法
if (Reflect.defineProperty(target, property, attributes)) {
// success
} else {
// failure
}
複製代碼
重點食用:使用 Proxy 實現觀察者模式——結合Vue的雙向綁定原理
觀察者模式(Observer mode)指的是函數自動觀察數據對象,一旦對象有變化,函數就會自動執行。
const person = observable({
name: '張三',
age: 20
});
function print() {
console.log(`${person.name}, ${person.age}`)
}
observe(print);
person.name = '李四';
// 輸出
// 李四, 20
複製代碼
上面代碼中,數據對象person是觀察目標,函數print是觀察者。一旦數據對象發生變化,print就會自動執行。
下面,使用 Proxy 寫一個觀察者模式的最簡單實現,即實現observable和observe這兩個函數。 思路是observable函數返回一個原始對象的 Proxy 代理,攔截賦值操做,觸發充當觀察者的各個函數。
const queuedObservers = new Set();
const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, {set});
function set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
queuedObservers.forEach(observer => observer());
return result;
}
複製代碼
上面代碼中,先定義了一個Set集合,全部觀察者函數都放進這個集合。 而後,observable函數返回原始對象的代理,攔截賦值操做。攔截函數set之中,會自動執行全部觀察者。
Iterator 的做用有三個:一是爲各類數據結構,提供一個統一的、簡便的訪問接口;二是使得數據結構的成員可以按某種次序排列;三是 ES6 創造了一種新的遍歷命令for...of循環,Iterator 接口主要供for...of消費。
lterator有點難理解了 傳送門 結合迭代器、生成器理解
默認調用 Iterator 接口(即Symbol.iterator方法)的場合:
(1)解構賦值
對數組和 Set 結構進行解構賦值時,會默認調用Symbol.iterator方法。
let set = new Set().add('a').add('b').add('c');
let [x,y] = set;
// x='a'; y='b'
let [first, ...rest] = set;
// first='a'; rest=['b','c'];
複製代碼
(2)擴展運算符
擴展運算符(...)也會調用默認的 Iterator 接口。
// 例一
var str = 'hello';
[...str] // ['h','e','l','l','o']
// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']
複製代碼
上面代碼的擴展運算符內部就調用 Iterator 接口。
實際上,這提供了一種簡便機制,能夠將任何部署了 Iterator 接口的數據結構,轉爲數組。也就是說,只要某個數據結構部署了 Iterator 接口,就能夠對它使用擴展運算符,將其轉爲數組。
let arr = [...iterable];
複製代碼
(3)yield*
yield*後面跟的是一個可遍歷的結構,它會調用該結構的遍歷器接口。
let generator = function* () {
yield 1;
yield* [2,3,4];
yield 5;
};
var iterator = generator();
iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: 4, done: false }
iterator.next() // { value: 5, done: false }
iterator.next() // { value: undefined, done: true }
複製代碼
(4)其餘場合
因爲數組的遍歷會調用遍歷器接口,因此任何接受數組做爲參數的場合,其實都調用了遍歷器接口。下面是一些例子。
for...of
Array.from()
Map(), Set(), WeakMap(), WeakSet()(好比new Map([['a',1],['b',2]]))
Promise.all()
Promise.race()
複製代碼
ES6 的class能夠看做只是一個語法糖,它的絕大部分功能,ES5 均可以作到,新的class寫法只是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已。
∴ JS實現繼承仍是經過原型鏈的方式!
ES5:
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 Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
複製代碼
ES6 模塊的設計思想是儘可能的靜態化,使得編譯時就能肯定模塊的依賴關係,以及輸入和輸出的變量。CommonJS 和 AMD 模塊,都只能在運行時肯定這些東西。好比,CommonJS 模塊就是對象,輸入時必須查找對象屬性。 ES6 模塊不是對象,而是經過export命令顯式指定輸出的代碼,再經過import命令輸入。
// circle.js
export function area(radius) {
return Math.PI * radius * radius;
}
export function circumference(radius) {
return 2 * Math.PI * radius;
}
複製代碼
// main.js
import { area, circumference } from './circle';
console.log('圓面積:' + area(4));
console.log('圓周長:' + circumference(14));
複製代碼
(1) let 取代 var
(2) 在let和const之間,建議優先使用const,尤爲是在全局環境,不該該設置變量,只應設置常量。
(3) 靜態字符串一概使用單引號或反引號,不使用雙引號。動態字符串使用反引號。
// bad
const a = "foobar";
const b = 'foo' + a + 'bar';
// acceptable
const c = `foobar`;
// good
const a = 'foobar';
const b = `foo${a}bar`;
複製代碼
(4) 善用解構賦值⭐
(5) 對象儘可能靜態化,一旦定義,就不得隨意添加新的屬性。若是添加屬性不可避免,要使用Object.assign方法。
// bad
const a = {};
a.x = 3;
// if reshape unavoidable
const a = {};
Object.assign(a, { x: 3 });
// good
const a = { x: null };
a.x = 3;
複製代碼
(6) 使用擴展運算符(...)拷貝數組。⭐
// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i++) {
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];
複製代碼
(7)當即執行函數能夠寫成箭頭函數的形式
(() => {
console.log('Welcome to the Internet.');
})();
複製代碼
(8)善用Map⭐
(9)Module 語法是 JavaScript 模塊的標準寫法,堅持使用這種寫法。使用import取代require。
(10)ESLint