談談ES6語法(彙總中篇)

本次的ES6語法的彙總總共分爲上、中、下三篇,本篇文章爲中篇。javascript

彙總上篇文章請戳這裏--談談ES6語法(彙總上篇)java

好了,咱們直奔中篇的內容~git

數組擴展

數組擴展運算符

數組擴展運算符(spread)是三個點(...)。它比如rest參數的逆運算,將一個數組轉爲用空格分隔的參數序列。es6

console.log(...[1, 2, 3]); // 1 2 3
console.log(1, ...[2, 3, 4], 5); // 1 2 3 4 5
複製代碼

⚠️rest參數是運用在函數參數上的,將函數參數轉換爲數組的形式,以下:github

function fn(...values) {
  console.log(values); // ['jia', 'ming']
}
fn('jia', 'ming');
複製代碼

下面咱們結合數組擴展運算符和rest參數來實現一個相似call的方法call2操做:編程

Function.prototype.call2 = function(context, ...args){ // 這裏使用到rest參數
	context = context || window; // 由於傳遞過來的context有多是null
	context.fn = this; // 讓fn的上下文爲context
	const result = context.fn(...args); // 這裏使用了數組擴展運算符
	delete context.fn;
	return result; // 由於有可能this函數會有返回值return
}
var job = 'outter teacher';
var obj = {
	job: 'inner teacher'
};
function showJob() {
	console.log(this.job);
}
showJob(); // outter teacher
showJob.call2(obj); // inner teacher
複製代碼

複習一下,咱們把var job = 'outter teacher'改成let job = 'outter teacher'後,showJob()會輸出什麼?json

答案是undefined。在前一篇中也提到過,ES6語法聲明的變量是不會掛載在全局對象上的~數組

Array.from()

Array.from方法用於將兩類對象轉爲真正的數組:相似數組的對象(array-like object)和可遍歷(iterable)的對象(對象包括ES6新增的數據結構Set和Map)。promise

// 類數組轉化成數組
let arrayLike = {
	'0': 'a',
	'1': 'b',
	'2': 'c',
	length: 3
}

// ES5的寫法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的寫法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
複製代碼

Array.of()

Array.of()方法用於將一組值,轉換爲數組。數據結構

let arr = Array.of(2, 3, 'reng');
console.log(arr); // [2, 3, 'reng']
console.log(arr.pop()); // reng
複製代碼

Array.of基本上能夠彌補Array()或new Array()帶來的由於參數個數致使的不一樣行爲。Array.of基本上能夠替代它們兩個了。

Array.of(); // []
Array.of('reng'); // ['reng']
Array.of(2, 'reng'); // [2, 'reng']
複製代碼

數組中還有其它有用的方法:

  • copyWithin(target, start = 0, end = this.length): 拷貝指定數組的範圍值
  • find(fn): 用於查找第一個符合條件的數組成員,沒有返回undefined
  • findIndex(fn): 用於查找第一個符合條件的數組成員的位置,沒有返回-1
  • entries(): 對鍵值對的遍歷
  • keys(): 對鍵的遍歷
  • values(): 對值的遍歷
  • includes(el): 返回一個布爾值,表示某個數組是否包含給定的值,與字符串的include(el)方法類似
  • flat(num): 將嵌套的數組拉平,num是遍歷的深度
[1, [2, [3]]].flat(Infinity);
// [1, 2, 3]
複製代碼

有這麼一個需求:將數組[[2, 8], [2], [[4, 6], 7, 6]]轉成一維且元素不重複的數組。

咱們的實現方案以下:

let arr = [[2, 8], [2], [[4, 6], 7, 6]];
console.log([...new Set(arr.flat(Infinity))]); // [2, 8, 4, 6, 7]
複製代碼

對象擴展

屬性名錶達式

ES6容許字面量定義對象時,把表達式放在方括號內:

let lastWord = 'last word';

const a = {
  'first word': 'hello',
  [lastWord]: 'world',
  ['end'+'symbol']: '!' 
};

a['first word'] // 'hello'
a[lastWord] // 'world'
a['last word'] // 'world'
a['endsymbol'] // '!'
複製代碼

對象的擴展運算符

上面整理數組擴展內容的時候,提到了數組的擴展運算符。ES2018將這個運算符引入了對象~

let z = { a: 3, b: 4 };
let n = { ...z }; // 關鍵點
n // { a: 3, b: 4 }
複製代碼

對象中某些新增的方法

  • Object.is(arg1, arg2): 比較兩個值是否嚴格相等,與===行爲基本一致
  • Object.assign(target, source1, ...): 用於對象的合併,將源對象(source)的全部可枚舉屬性,複製到目標對象(target)。屬於淺拷貝
  • Object.keys(obj): 返回一個數組,成員是參數對象自身的(不含繼承的)全部可遍歷(enumerable)屬性的鍵名
  • Object.values(obj): 方法返回一個數組,成員是參數對象自身的(不含繼承的)全部可遍歷(enumerable)屬性的鍵值。
  • Object.entries(obj): 方法返回一個數組,成員是參數對象自身的(不含繼承的)全部可遍歷(enumerable)屬性的鍵值對數組。
const obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]
複製代碼

Set和Map數據結構

Set

Set翻譯出來就是集合,有元素惟一性的特色。

在數組去重的場景上頗有用處:

// 去除數組的重複成員
[...new Set(array)]
// 如
console.log([...new Set([2, 2, 3, 2])]); // [2, 3]
複製代碼

須要留意的Set屬性和方法有如下:

  • size: 返回實例成員的總數
  • add(value): 添加某個值,返回Set結構自己
  • delete(value): 刪除某個值,返回一個布爾值,表示刪除是否成功。
  • has(value): 返回一個布爾值,表示該值是否爲Set的成員
  • clear(): 清除全部成員,沒有返回值。
  • key():返回鍵名的遍歷器。
  • values(): 返回鍵值的遍歷器。
  • entries(): 返回鍵值對的遍歷器。
  • forEach(): 使用回調函數遍歷每一個成員

WeakSet

WeakSet結構與Set相似,也是有不重複元素的集合。可是它和Set有兩個區別:

  1. WeakSet對象中只能存放對象引用, 不能存放值, 而Set對象均可以.

  2. WeakSet中對象中存儲的對象值都是被弱引用的, 若是沒有其餘的變量或屬性引用這個對象值, 則這個對象值會被當成垃圾回收掉. 正由於這樣, WeakSet 對象是沒法被枚舉的, 沒有辦法拿到它包含的全部元素。

var ws = new WeakSet();
var obj = {};
var foo = {};

ws.add(window);
ws.add(obj);

ws.has(window); // true
ws.has(foo);    // false, 對象 foo 並無被添加進 ws 中 

ws.delete(window); // 從集合中刪除 window 對象
ws.has(window);    // false, window 對象已經被刪除了

ws.clear(); // 清空整個 WeakSet 對象
複製代碼

WeakSet 沒有size屬性,沒有辦法遍歷它的成員

Map

Map對象保持鍵值對。任何值(對象或者原始值)均可以做爲一個鍵或一個值。

Object和Map的比較:

  • 一個Object的鍵只能是字符串或者Symbols,但一個Map的鍵能夠是任意值,包括函數、對象、基本類型。
  • Map中的鍵值是有序的,而添加到對象中的鍵則不是。所以,當對它進行遍歷時,Map對象是按插入的順序返回鍵值。
  • Map在涉及頻繁增刪鍵值對的場景下會有些性能優點`。
  • ...

若是你須要「鍵值對」的數據結構,MapObject更合適。

const set = new Set([ // 數組轉換爲map
  ['foo', 1],
  ['bar', 2]
]);
const m1 = new Map(set);
m1.get('foo') // 1

const m2 = new Map([['baz', 3]]);
const m3 = new Map(m2);
m3.get('baz') // 3
複製代碼

Map擁有的屬性和方法和Set類似,多出了些:

  • set(key, value):set方法設置鍵名key對應的鍵值爲value,而後返回整個 Map 結構。若是key已經有值,則鍵值會被更新,不然就新生成該鍵。
  • get(key):get方法讀取key對應的鍵值,若是找不到key,返回undefined

WeakMap

WeakMap結構與Map結構相似,也是用於生成鍵值對的集合。可是有兩點區別:

  • WeakMap只接受對象做爲鍵名(null除外),不接受其餘類型的值做爲鍵名。
  • WeakMap的鍵名所指向的對象,不計入垃圾回收機制。和WeakSet類似啦。

屬性方法啥的跟Map差很少,就是沒有了sizeforEach,由於其是不可枚舉的。

Promise對象

Promise是異步編程的一種解決方案,比傳統的解決方案「回調函數和事件」更合理和更強大。

Promise對象有如下兩個特色:

  1. 對象的狀態不受外界影響。Promise對象表明一個異步操做,有三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗)。

  2. 一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。Promise對象的狀態改變,只有兩種狀況:從pending變成fulfilled(fulfilled也稱resolved)和從pending變成rejected

用法

const promise = new Promise(function(resolve, reject) {
	// ...some code
	
	if(/* 異步操做成功 */) {
		resolve(value);
	} else {
		reject(error);
	}
})
複製代碼

參數resolvereject是兩個函數,由JavaScript引擎提供,不用本身部署。

Promise實例生成以後,可使用then方法分別指定resolved狀態和rejected狀態的回調函數。

promise.then(function(value) {
	// success
}, function(error) {
	// failure
});
複製代碼

咱們來粘貼個簡單例子:

function timeout(ms) {
	return new Promise((resolve, reject) => {
		setTimeout(resolve, ms, 'done');
	});
}

timeout(100).then((value) => {
	console.log(value); // 100ms後輸出'done'
});
複製代碼

嗯~咱們順道來複習下setTimeout的第三個參數。哦😯,不,是第三個,第四個...

var timeoutID = scope.setTimeout(function[, delay, param1, param2, ...]);
複製代碼
  • function 是你想要在到期時間(delay毫秒)以後執行的函數。
  • delay 是可選語法,表示延遲的毫秒數。
  • param1, ..., paramN 是可選的附加參數,一旦定時器到期,它們會做爲參數傳遞給function

那麼,到這裏你理解了上面的例子爲何在100ms後輸出done了嘛💨

詳細的setTimeout信息,請戳MDN的setTimeout

簡單的例子看完了,看下咱們在工做中使用得比較多的請求接口的例子:

const getJSON = function(url) {
	const promise = new Promise(function(resolve, reject){
		const handler = function() {
			if(this.readyState !== 4) {
				return;
			}
			if(this.status === 200) {
				resolve(this.response); // this.response做爲參數傳給then中的json
			} else {
				reject(new Error(this.statusText));
			}
		};
		const client = new XMLHttpRequest();
		client.open('GET', url);
		client.onreadystatechange = handler;
		client.responseType = 'json';
		client.setRequestHeader('Accept', 'application.json');
		client.send();
	});
	return promise;
};
getJSON('/post.json').then(function(json) {
	console.log('Contents: '+ json);
}, function(error) {
	console.log('error happen ', error);
});
複製代碼

catch方法

Promise.prototype.catch方法是.then(null, rejection).then(undefined, rejection)的別名,用於指定發生錯誤時的回調函數。

p.then((val) => console.log('fulfilled:', val))
	.catch((err) => console.log('rejected', err)); // promise中任何一個拋出錯誤,都會被最後一個catch捕獲
	
// 等同於
p.then((val) => console.log('fulfilled:', val))
	.then(null, (err) => console.log('rejected:', err));
複製代碼

finally方法

Promise.prototype.finally()方法(其不接受任何參數)用於指定無論Promise對象最後狀態如何,都會執行的操做。該方法是 ES2018 引入標準的。

語法:

promise
	.then(result => {···})
	.catch(error => {···})
	.finally(() => {···});
複製代碼

Promise.all

構造函數方法Promise.all方法用於將多個Promise實例,包裝成一個新的Promise實例。

const p = Promise.all([p1, p2, p3]);
複製代碼

上面代碼中,Promise.all方法接受一個數組做爲參數,p1, p2, p3都是Promise實例。若是不是會調用Promise.resolve方法,具體看文檔

// 生成一個Promise對象的數組
const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
	return getJSON('/post/' + id + ".json");
});

Promise.all(promises).then(function (posts) {
	// ...
}).catch(function(reason){
	// ...
});
複製代碼

上面代碼中,promises是包含 6 個 Promise 實例的數組,只有這6個實例的狀態都變成fulfilled,或者其中有一個變爲rejected,纔會調用Promise.all方法後面的回調函數。

⚠️注意,若是做爲參數的Promise實例,本身定義了catch方法,那麼它一旦被rejected,並不會觸發Promise.all()catch方法。因此使用Promise.all()別手癢在每一個實例promise內添加錯誤捕獲。

一道練手題

需求:使用promise改寫下面的代碼,使得輸出的指望結果是每隔一秒輸出0, 1, 2, 3, 4, 5,其中i < 5條件不能變

for(var i = 0 ; i < 5; i++){
    setTimeout(function(){
        console.log(i);
    },1000)
}
console.log(i);
複製代碼

咱們直接上使用promise改寫的代碼吧~

const tasks = []; // 存放promise對象
for(let i = 0; i < 5; i++){
	tasks.push(new Promise((resolve) => {
		setTimeout(() => {
			console.log(i);
			resolve();
		}, 1000 * i);
	}));
}
Promise.all(tasks).then(() => {
	setTimeout(() => {
		console.log(tasks.length);
	}, 1000);
});
// 每隔一秒輸出 0, 1, 2, 3, 4, 5
複製代碼

參考和後話

本次的ES6語法的彙總總共分爲上、中、下三篇,本篇文章爲中篇。

文章首發在github上--談談ES6語法(彙總中篇)。更多的內容,請戳個人博客進行了解,能留個star就更好了💨

相關文章
相關標籤/搜索