談?wù)凟S6語法(匯總中篇)

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

匯總上篇文章請(qǐng)戳這里--談?wù)凟S6語法(匯總上篇)

好了,我們直奔中篇的內(nèi)容~

數(shù)組擴(kuò)展

數(shù)組擴(kuò)展運(yùn)算符

數(shù)組擴(kuò)展運(yùn)算符(spread)是三個(gè)點(diǎn)(...)。它好比rest參數(shù)的逆運(yùn)算,將一個(gè)數(shù)組轉(zhuǎn)為用空格分隔的參數(shù)序列。

console.log(...[1, 2, 3]); // 1 2 3
console.log(1, ...[2, 3, 4], 5); // 1 2 3 4 5

??rest參數(shù)是運(yùn)用在函數(shù)參數(shù)上的,將函數(shù)參數(shù)轉(zhuǎn)換為數(shù)組的形式,如下:

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

下面我們結(jié)合數(shù)組擴(kuò)展運(yùn)算符和rest參數(shù)來實(shí)現(xiàn)一個(gè)類似call的方法call2操作:

Function.prototype.call2 = function(context, ...args){ // 這里使用到rest參數(shù)
    context = context || window; // 因?yàn)閭鬟f過來的context有可能是null
    context.fn = this; // 讓fn的上下文為context
    const result = context.fn(...args); // 這里使用了數(shù)組擴(kuò)展運(yùn)算符
    delete context.fn;
    return result; // 因?yàn)橛锌赡躷his函數(shù)會(huì)有返回值return
}
var job = 'outter teacher';
var obj = {
    job: 'inner teacher'
};
function showJob() {
    console.log(this.job);
}
showJob(); // outter teacher
showJob.call2(obj); // inner teacher

復(fù)習(xí)一下,我們把var job = 'outter teacher'改為let job = 'outter teacher'后,showJob()會(huì)輸出什么?

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

Array.from()

Array.from方法用于將兩類對(duì)象轉(zhuǎn)為真正的數(shù)組:類似數(shù)組的對(duì)象(array-like object)和可遍歷(iterable)的對(duì)象(對(duì)象包括ES6新增的數(shù)據(jù)結(jié)構(gòu)Set和Map)。

// 類數(shù)組轉(zhuǎn)化成數(shù)組
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()方法用于將一組值,轉(zhuǎn)換為數(shù)組。

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

Array.of基本上可以彌補(bǔ)Array()或new Array()帶來的因?yàn)閰?shù)個(gè)數(shù)導(dǎo)致的不同行為。Array.of基本上可以替代它們兩個(gè)了。

Array.of(); // []
Array.of('reng'); // ['reng']
Array.of(2, 'reng'); // [2, 'reng']

數(shù)組中還有其它有用的方法:

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

有這么一個(gè)需求:將數(shù)組[[2, 8], [2], [[4, 6], 7, 6]]轉(zhuǎn)成一維且元素不重復(fù)的數(shù)組。

我們的實(shí)現(xiàn)方案如下:

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

對(duì)象擴(kuò)展

屬性名表達(dá)式

ES6允許字面量定義對(duì)象時(shí),把表達(dá)式放在方括號(hào)內(nèi):

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'] // '!'

對(duì)象的擴(kuò)展運(yùn)算符

上面整理數(shù)組擴(kuò)展內(nèi)容的時(shí)候,提到了數(shù)組的擴(kuò)展運(yùn)算符。ES2018將這個(gè)運(yùn)算符引入了對(duì)象~

let z = { a: 3, b: 4 };
let n = { ...z }; // 關(guān)鍵點(diǎn)
n // { a: 3, b: 4 }

對(duì)象中某些新增的方法

  • Object.is(arg1, arg2): 比較兩個(gè)值是否嚴(yán)格相等,與===行為基本一致
  • Object.assign(target, source1, ...): 用于對(duì)象的合并,將源對(duì)象(source)的所有可枚舉屬性,復(fù)制到目標(biāo)對(duì)象(target)。屬于淺拷貝
  • Object.keys(obj): 返回一個(gè)數(shù)組,成員是參數(shù)對(duì)象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵名
  • Object.values(obj): 方法返回一個(gè)數(shù)組,成員是參數(shù)對(duì)象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值。
  • Object.entries(obj): 方法返回一個(gè)數(shù)組,成員是參數(shù)對(duì)象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值對(duì)數(shù)組。
const obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]

Set和Map數(shù)據(jù)結(jié)構(gòu)

Set

Set翻譯出來就是集合,有元素唯一性的特點(diǎn)。

在數(shù)組去重的場景上很有用處:

// 去除數(shù)組的重復(fù)成員
[...new Set(array)]
// 如
console.log([...new Set([2, 2, 3, 2])]); // [2, 3]

需要留意的Set屬性和方法有以下:

  • size: 返回實(shí)例成員的總數(shù)
  • add(value): 添加某個(gè)值,返回Set結(jié)構(gòu)本身
  • delete(value): 刪除某個(gè)值,返回一個(gè)布爾值,表示刪除是否成功。
  • has(value): 返回一個(gè)布爾值,表示該值是否為Set的成員
  • clear(): 清除所有成員,沒有返回值。
  • key():返回鍵名的遍歷器。
  • values(): 返回鍵值的遍歷器。
  • entries(): 返回鍵值對(duì)的遍歷器。
  • forEach(): 使用回調(diào)函數(shù)遍歷每個(gè)成員

WeakSet

WeakSet結(jié)構(gòu)與Set類似,也是有不重復(fù)元素的集合。但是它和Set有兩個(gè)區(qū)別:

  1. WeakSet對(duì)象中只能存放對(duì)象引用, 不能存放值, 而Set對(duì)象都可以.

  2. WeakSet中對(duì)象中存儲(chǔ)的對(duì)象值都是被弱引用的, 如果沒有其他的變量或?qū)傩砸眠@個(gè)對(duì)象值, 則這個(gè)對(duì)象值會(huì)被當(dāng)成垃圾回收掉. 正因?yàn)檫@樣, WeakSet 對(duì)象是無法被枚舉的, 沒有辦法拿到它包含的所有元素。

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

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

ws.has(window); // true
ws.has(foo);    // false, 對(duì)象 foo 并沒有被添加進(jìn) ws 中 

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

ws.clear(); // 清空整個(gè) WeakSet 對(duì)象

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

Map

Map對(duì)象保持鍵值對(duì)。任何值(對(duì)象或者原始值)都可以作為一個(gè)鍵或一個(gè)值。

Object和Map的比較:

  • 一個(gè)Object的鍵只能是字符串或者Symbols,但一個(gè)Map的鍵可以是任意值,包括函數(shù)、對(duì)象、基本類型。
  • Map中的鍵值是有序的,而添加到對(duì)象中的鍵則不是。因此,當(dāng)對(duì)它進(jìn)行遍歷時(shí),Map對(duì)象是按插入的順序返回鍵值。
  • Map在涉及頻繁增刪鍵值對(duì)的場景下會(huì)有些性能優(yōu)勢`。
  • ...

如果你需要“鍵值對(duì)”的數(shù)據(jù)結(jié)構(gòu),MapObject更合適。

const set = new Set([ // 數(shù)組轉(zhuǎn)換為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方法設(shè)置鍵名key對(duì)應(yīng)的鍵值為value,然后返回整個(gè) Map 結(jié)構(gòu)。如果key已經(jīng)有值,則鍵值會(huì)被更新,否則就新生成該鍵。
  • get(key):get方法讀取key對(duì)應(yīng)的鍵值,如果找不到key,返回undefined

WeakMap

WeakMap結(jié)構(gòu)與Map結(jié)構(gòu)類似,也是用于生成鍵值對(duì)的集合。但是有兩點(diǎn)區(qū)別:

  • WeakMap只接受對(duì)象作為鍵名(null除外),不接受其他類型的值作為鍵名。
  • WeakMap的鍵名所指向的對(duì)象,不計(jì)入垃圾回收機(jī)制。和WeakSet相似啦。

屬性方法啥的跟Map差不多,就是沒有了sizeforEach,因?yàn)槠涫遣豢擅杜e的。

Promise對(duì)象

Promise是異步編程的一種解決方案,比傳統(tǒng)的解決方案“回調(diào)函數(shù)和事件”更合理和更強(qiáng)大。

Promise對(duì)象有以下兩個(gè)特點(diǎn):

  1. 對(duì)象的狀態(tài)不受外界影響。Promise對(duì)象代表一個(gè)異步操作,有三種狀態(tài):pending(進(jìn)行中)、fulfilled(已成功)和rejected(已失?。?。

  2. 一旦狀態(tài)改變,就不會(huì)再變,任何時(shí)候都可以得到這個(gè)結(jié)果。Promise對(duì)象的狀態(tài)改變,只有兩種情況:從pending變成fulfilled(fulfilled也稱resolved)和從pending變成rejected。

用法

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

參數(shù)resolvereject是兩個(gè)函數(shù),由JavaScript引擎提供,不用自己部署。

Promise實(shí)例生成之后,可以使用then方法分別指定resolved狀態(tài)和rejected狀態(tài)的回調(diào)函數(shù)。

promise.then(function(value) {
    // success
}, function(error) {
    // failure
});

我們來粘貼個(gè)簡單例子:

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

timeout(100).then((value) => {
    console.log(value); // 100ms后輸出'done'
});

嗯~我們順道來復(fù)習(xí)下setTimeout的第三個(gè)參數(shù)。哦??,不,是第三個(gè),第四個(gè)...

var timeoutID = scope.setTimeout(function[, delay, param1, param2, ...]);
  • function 是你想要在到期時(shí)間(delay毫秒)之后執(zhí)行的函數(shù)。
  • delay 是可選語法,表示延遲的毫秒數(shù)。
  • param1, ..., paramN 是可選的附加參數(shù),一旦定時(shí)器到期,它們會(huì)作為參數(shù)傳遞給function

那么,到這里你理解了上面的例子為什么在100ms后輸出done了嘛??

詳細(xì)的setTimeout信息,請(qǐng)戳MDN的setTimeout

簡單的例子看完了,看下我們?cè)诠ぷ髦惺褂玫帽容^多的請(qǐng)求接口的例子:

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作為參數(shù)傳給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)的別名,用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)。

p.then((val) => console.log('fulfilled:', val))
    .catch((err) => console.log('rejected', err)); // promise中任何一個(gè)拋出錯(cuò)誤,都會(huì)被最后一個(gè)catch捕獲
    
// 等同于
p.then((val) => console.log('fulfilled:', val))
    .then(null, (err) => console.log('rejected:', err));

finally方法

Promise.prototype.finally()方法(其不接受任何參數(shù))用于指定不管Promise對(duì)象最后狀態(tài)如何,都會(huì)執(zhí)行的操作。該方法是 ES2018 引入標(biāo)準(zhǔn)的。

語法:

promise
    .then(result => {···})
    .catch(error => {···})
    .finally(() => {···});

Promise.all

構(gòu)造函數(shù)方法Promise.all方法用于將多個(gè)Promise實(shí)例,包裝成一個(gè)新的Promise實(shí)例。

const p = Promise.all([p1, p2, p3]);

上面代碼中,Promise.all方法接受一個(gè)數(shù)組作為參數(shù),p1, p2, p3都是Promise實(shí)例。如果不是會(huì)調(diào)用Promise.resolve方法,具體看文檔。

// 生成一個(gè)Promise對(duì)象的數(shù)組
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 個(gè) Promise 實(shí)例的數(shù)組,只有這6個(gè)實(shí)例的狀態(tài)都變成fulfilled,或者其中有一個(gè)變?yōu)?code>rejected,才會(huì)調(diào)用Promise.all方法后面的回調(diào)函數(shù)。

??注意,如果作為參數(shù)的Promise實(shí)例,自己定義了catch方法,那么它一旦被rejected,并不會(huì)觸發(fā)Promise.all()catch方法。所以使用Promise.all()別手癢在每個(gè)實(shí)例promise內(nèi)添加錯(cuò)誤捕獲。

一道練手題

需求:使用promise改寫下面的代碼,使得輸出的期望結(jié)果是每隔一秒輸出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對(duì)象
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語法的匯總總共分為上、中、下三篇,本篇文章為中篇。

文章首發(fā)在github上--談?wù)凟S6語法(匯總中篇)。更多的內(nèi)容,請(qǐng)戳我的博客進(jìn)行了解,能留個(gè)star就更好了??

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容