重讀 ES6 — 數(shù)組、對(duì)象的擴(kuò)展

上一篇《讀 ES6 — 字符串赞庶、數(shù)值你辣、正則的擴(kuò)展》 將新增 API 做了一些梳理,隔離了一些復(fù)雜而低頻的知識(shí)點(diǎn)尘执,從有限的篇幅分析舍哄,可以窺探到 JavaScript 內(nèi)部代碼組織更趨于合理、而本身 API 則變得更加易用和強(qiáng)大誊锭。

本篇繼續(xù)沿著上篇的分析方向表悬,來整理下 ES6 基本類型中的數(shù)組對(duì)象 新增特性丧靡。(函數(shù)稍特別蟆沫,將單獨(dú)來梳理)

因?yàn)樗鼈冊(cè)?JavaScript 語(yǔ)言中的地位無比重要,無論是 ES5 還是 ES6 中都應(yīng)該優(yōu)先掌握它們温治。本篇著重梳理 ES6 相對(duì)于 ES5 的增量知識(shí)饭庞,但是有些增量使用起來仍然很費(fèi)勁,用到一個(gè)知識(shí)點(diǎn)恐怕先得弄清一大片才行——好比對(duì)朋友說了一個(gè)謊(大家應(yīng)該都有這樣干過吧......)熬荆,就得用好幾個(gè)謊才能圓回來舟山。

比如當(dāng)你開始用起 Array.from() 你可能會(huì)遇到 數(shù)組的空位的概念,然后猛然一驚還有這事,那豈不是我曾經(jīng)有段程序用錯(cuò)了埋了 bug? 比如函數(shù)的尾調(diào)用優(yōu)化累盗,是什么鬼呀寒矿?還比如對(duì)象 Object 搞了一大堆 getOwnPropertyDescriptor()setPrototypeOf()若债、getPrototypeOf() 這些看著 prototype 就頭疼的方法......

確實(shí)符相,這些東西有點(diǎn)繁瑣,不過沒關(guān)系蠢琳,如之前的理念我們暫且先把它們隔離起來啊终,關(guān)在鐵籠子里,等咱先打完小怪傲须,在來收拾它們蓝牲。(怎么感覺和一款叫 “軒轅劍...” 的游戲劇情有點(diǎn)相似)。另外躏碳,就是很多 API 可能初期有點(diǎn)抗拒搞旭,只要用起來了就覺得太 TM 順手絲滑了。

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

一如既往菇绵,啰嗦完畢開始進(jìn)入正題肄渗。數(shù)組新增的特性有:

  • 新增方法(包括在命名空間 Array 上和數(shù)組實(shí)例上)
  • spread 運(yùn)算符
  • 數(shù)組空位的概念

新增方法

數(shù)組實(shí)例的新增方法:

在數(shù)組內(nèi)自我覆蓋: copyWithin()
見好就收: find() 和 findIndex()
是否包含了某項(xiàng): includes()
自我填充: fill()
花式遍歷: entries()、keys() 和 values()

以及命名空間 Array 上的方法:

數(shù)組產(chǎn)自近親: Array.from()
化零為整: Array.of()

新增方法都很簡(jiǎn)單咬最,和上一篇總結(jié)的特征規(guī)律是相似的翎嫡。比如封裝高頻的寫法:

// ES5 數(shù)組是否存在某項(xiàng)
var isExist = [1, NaN, 3, 6, 8].indexOf(6) !== -1;
// => 這樣寫總感覺不夠直觀
// => 經(jīng)常還要查 `indexOf` 的 `O` 是大寫還是小寫有木有!
// => 因?yàn)?`typeof` 的是小寫永乌!

// 莫名其妙 `NaN` 的存在性判斷不出來
[1, NaN, 3, 6, 8].indexOf(NaN);
// => -1

// ES6 數(shù)組是否存在某項(xiàng)就沒有上述的問題
let isExist = [1, NaN, 3, 6, 8].includes(NaN)

另外比較重要的特征就是:

  • 幾乎都是純函數(shù)
  • 更偏向聲明式編程
  • API 的行為統(tǒng)一明確

首先惑申,為什么說幾乎都是 “純函數(shù)”(相關(guān)知識(shí)若有需要,可以閱讀本人的另一篇專題文章《走向 JavaScript 函數(shù)式編程》)翅雏? 新增的方法都滿足相同輸入恒有相同輸出圈驼,但是還有一點(diǎn)點(diǎn) “副作用”,就是改變了外部變量即當(dāng)前數(shù)組本身望几。但總的來說绩脆,比起 ES5,已向 “確定性” API 邁出了一大步橄抹。

于是靴迫,將稍有 “副作用” 的方法改成 “純函數(shù)”,是非常容易的:

// 帶副作用的 fill 方法會(huì)改變數(shù)組 arr
arr.fill(val, start, end);

// 很容易封裝成純函數(shù)楼誓,并且實(shí)現(xiàn)一定的柯里化
function fill(arr) {
    let inner_arr = arr.slice(0);
    return function (val, start, end) {
        return inner_arr.fill(val, start, end);
    }
}

其次玉锌,偏向于聲明式編程。ES6 看起來是要圍剿 for循環(huán)疟羹,讓它們少出頭露面的辦法就是提供新的 API來隱藏它們主守。

比如禀倔,在一次 “尋找 NaN” 的活動(dòng)中,這樣的調(diào)用是不是更 “聲明式”丸逸,更絲滑順暢蹋艺?

//在數(shù)組中尋找 NaN
let index = [1, 2, 3, NaN, 6, 8].findIndex(Number.isNaN);
// => 3

let item = [1, 2, 3, NaN, 6, 8].find(Number.isNaN);
// => NaN

至于統(tǒng)一而確定的行為剃袍,在上例中 includes 以及 Array.from() 都有體現(xiàn)黄刚。對(duì)于一個(gè)重量級(jí)的語(yǔ)言,尤其是欲意 “征服全宇宙” 的 JavaScript 來說民效,定當(dāng)以極為挑剔的眼光審視它憔维,而 ES6 已經(jīng)做了很多。

spread 運(yùn)算符

spread 運(yùn)算符(擴(kuò)展運(yùn)算符)是三個(gè)點(diǎn)(...)畏邢。這個(gè)新增項(xiàng)非常能讓人接受业扒,而且它已經(jīng)蔓延到了除數(shù)字本身外的 函數(shù)參數(shù)(類似數(shù)組)、對(duì)象舒萎、字符串等等類型上程储。

以一個(gè)簡(jiǎn)單的例子,看看用 ... 書寫帶來的良好的閱讀體驗(yàn):

// 合并數(shù)組臂寝,通過 concat() 連接起來
var compose = first_arr.concat(second_arr).concat(third_arr);

//毫無雜質(zhì)的 ...
let compose = [...first_arr, ...second_arr, ...third_arr];

數(shù)組空位

最后數(shù)組的空位概念章鲤,本文不打算去說明。知識(shí)點(diǎn)本身比較簡(jiǎn)單咆贬,但是牽扯到太多的驗(yàn)證,比較難梳理,感覺像是揮不走的蒼蠅认罩。所以衫冻,最好的辦法是在編程實(shí)踐中再去 “拍” 它。

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

其實(shí)對(duì)象的擴(kuò)展并不復(fù)雜眷蜈,歸結(jié)起來差不多以下內(nèi)容:

  • 為了更好的賦值運(yùn)算
    • 屬性的簡(jiǎn)寫
    • 擴(kuò)展運(yùn)算符
  • Object 命名空間的新增方法
    • 常用方法
    • 對(duì)象的 prototype 方法
    • 屬性的描述方法

為了更好的賦值運(yùn)算

屬性的簡(jiǎn)寫沪哺、擴(kuò)展運(yùn)算符以及與此緊密相關(guān)的解構(gòu)賦值,可以說為舊的 JavaScript 開創(chuàng)了一批新的賦值運(yùn)算方式酌儒,讓 賦值運(yùn)算 擺脫了一板一眼的 =號(hào)運(yùn)算的方式辜妓。

var lang = {name: 'ECMA2015', shortName: 'ES6'};

//ES5 寫法比較冗余
var name = lang.name;
var shortName = lang.shortName;

//ES6 寫法更加簡(jiǎn)明
let {name, shortName} = lang;

對(duì)一個(gè) ES6 模塊的導(dǎo)入 (import) 和導(dǎo)出 (export) ,也能充分凸顯這種 賦值運(yùn)算 的便利性今豆。

// a.js
const version = '1.0.0';
let fn1 = () => {};
let fn2 = () => {};
export { version, fn1, fn2 };  //注:為了節(jié)約空間嫌拣,就寫成一行了

//b.js
import { version, fn1 } from './a';

// 如果不能解構(gòu)賦值
import a from './a';
let version = a.verion;
let fn1 = a.fn1;

屬性的簡(jiǎn)寫

上述 賦值運(yùn)算,和一個(gè)符號(hào)有關(guān)呆躲,那就是 ES6 的大括號(hào){}异逐,與 ES5 不同的是,ES6{}不僅能開辟一塊作用域插掂,又能進(jìn)行 模式匹配運(yùn)算灰瞻,有如自帶魔法一般腥例。

先來看看一個(gè)有意思的例子:將任意一個(gè)變量變成對(duì)象。

// ES5 需要獲取形參名
function var2obj(x) {
    var obj = {};
    var str_fn = var2obk.toString();
    var key = str_fn.split(')')[0].split('(')[1];
    obj[key] = x;
    return obj;
}
var x = 'unkown';
var myObj = var2obj(x);
// => {x: 'unkown'};

更為完整的情形酝润,請(qǐng)參考 這里 燎竖。但在 ES6{} 眼里,完全是另一番景象要销。甚至可以通過這個(gè)方式构回,輕易的獲取到所有形參名。(此處不去延伸)

function var2obj (x) {
    return {x};
}
var x = 'unkown';
var myObj = var2obj(x);
// => {x: 'unkown'};

let y = 'yes';
var anotherObj = var2obj(y);
// => {y: 'yes'};

大括號(hào) {} 自帶運(yùn)算魔法疏咐。解析時(shí)纤掸,能將 {x} 一分為二,自動(dòng)展開為 {x: x}浑塞。反之借跪,將對(duì)象的鍵值合成一個(gè)變量項(xiàng),寫在 {} 中酌壕,就是屬性的簡(jiǎn)寫掏愁。

// 簡(jiǎn)寫形式
let {name, age} = {name: 'jeremy', age: 18};

// 展開形式
let {name: name, age: age} = {name: 'jeremy', age: 18};

簡(jiǎn)寫對(duì)象的解構(gòu)賦值,可以看做是先轉(zhuǎn)化成上述的 “展開形式” 卵牍,然后再開始匹配賦值的果港。

因此,解構(gòu)賦值 = 號(hào)左邊的任何鍵名辽慕,都必須來自 = 號(hào)右邊對(duì)象中的某個(gè)元素京腥,否則將無法識(shí)別(undefined)。換句話說溅蛉,= 號(hào)右邊對(duì)象能夠訪問到的屬性公浪,都是可以被解構(gòu)和賦值給 = 號(hào)左邊的,包括它原型鏈上的屬性船侧。

function Corder (name) {
    this.name = name;
}
Corder.prototype.age = 18;
let {name, age} = new Corder('jeremy');
// => age 18

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

對(duì)象也有擴(kuò)展運(yùn)算符 (...)欠气,和數(shù)組的是類似的,簡(jiǎn)單理解就是剝離了一層大括號(hào) {}镜撩,將對(duì)象鍵值對(duì)直接暴露出來预柒。

擴(kuò)展運(yùn)算并不難,但是有一個(gè)和解構(gòu)賦值不同的特征袁梗,是它不會(huì)將原型鏈上的屬性宜鸯、方法暴露出來。準(zhǔn)確的說遮怜,擴(kuò)展運(yùn)算符是取出對(duì)象自身(不包括其原型鏈上)的所有可遍歷屬性淋袖。

let {...aCoder} = new Corder('jeremy');
console.log(aCoder);
// => {name: ''jeremy'}

let aCoder = Object.assign({}, new Corder('jeremy'));
// => {name: ''jeremy'}

可見,...Object.assign 都沒有將原型鏈上的 age 取出來锯梁。

對(duì)了即碗,這里提到了 取出對(duì)象自身(不包括其原型鏈上)的所有可遍歷屬性焰情,不禁腦袋中又蹦出一個(gè)問題:到底哪些運(yùn)算或方法,只獲取到對(duì)象自身的可遍歷的屬性剥懒?又有哪些是可以獲取到對(duì)象原型鏈上的屬性呢内舟?

面對(duì) ES6 不勝枚舉的新增特性,信息量的暴增初橘,往往讓人煩躁不堪验游。情緒性的鄙夷油然而生:看吧,就為了解決些小問題壁却,卻弄出這么一大堆東西來批狱,有意思嗎裸准!

Object 的新增方法

信息量暴增展东,確有其事。不過本文意在梳理炒俱,解決的問題就是從這些繁雜的信息中盐肃,提取容易的、有利的為自己所用权悟,其他晦澀麻煩的暫且一并鎖在鐵籠子里砸王。

從總體上看,ES6 的新增的特性并非沒有瑕疵(筆者實(shí)際編程中也曾遇到過峦阁,以后的篇幅有機(jī)會(huì)再提)谦铃,而且很多粒度很小,辨識(shí)起來的確很麻煩榔昔。但單單從剛才的提問來說驹闰,遍歷自身屬性還是原型鏈屬性,ES6 并沒有給我們制造麻煩撒会。

第一類在 Object 命名空間上新增方法:

  • Object.assign(target[, source1, source2, ...])
  • Object.keys(obj)
  • Object.values(obj)
  • Object.entries(obj)
  • Object.is(a, b)
  • Object.getOwnPropertyDescriptors(obj)

除了 Object.is(a, b) 之外嘹朗,它們都是處理屬性自身的可遍歷的屬性的方法。Own 是專屬自有屬性的特定命名诵肛,所以帶 OwnAPI 只遍歷到自有屬性就很好識(shí)別了屹培,包括 ES5obj.hasOwnProperty() 就是這個(gè)規(guī)則。此外怔檩,前文的 ... 擴(kuò)展運(yùn)算符是也歸類于此褪秀。

第二類在 Object 命名空間上新增方法:

  • Object.setPrototypeOf(obj, proto)
  • Object.getPrototypeOf(obj)

這兩個(gè)方法是直接對(duì)原型鏈對(duì)象的 setget,雖不用于遍歷薛训,但屬于和原型鏈直接打交道的方法媒吗。真正能遍歷到原型鏈的還是 ES5 已有的 for in 循環(huán)。

到此许蓖,問題就非常明確了蝴猪,仍然只有極少數(shù)的 API 可以遍歷或者直接操作原型鏈调衰,其他絕大多數(shù)新增 API,都只明確的限定在對(duì)象自有屬性的遍歷上自阱。

屬性的描述方法

對(duì)象的每個(gè)屬性都有一個(gè)描述對(duì)象(Descriptor)嚎莉。屬性的描述對(duì)象在 ES5就具備了,但當(dāng)初很少接觸到這個(gè)概念沛豌。具體上趋箩,它包含以下項(xiàng)目:

{
    value: 'attr', //屬性值
    writable: true, // 可寫
    enumerable: true, //可枚舉可遍歷
    configurable: true //可配置
}

描述對(duì)象之于屬性,好比于原子細(xì)分成用若干個(gè)質(zhì)子加派、中子來描述叫确。描述對(duì)象用來對(duì)外界開放處置屬性的權(quán)限,這在設(shè)計(jì)健壯的 API 是非常有用的芍锦。

為此而新增的方法有:

  • Object.getOwnPropertyDescriptors(obj)
  • Object.getOwnPropertyDescriptor(obj, key)

上文曾提過竹勉,該方法只會(huì)遍歷到對(duì)象自身屬性。至于其他方面娄琉,延伸暫無必要次乓。

ES6 之于遍歷

很多時(shí)候,提到獲取數(shù)組孽水、對(duì)象中的元素時(shí)候票腰,總會(huì)說來個(gè) for 循環(huán),來遍歷一下女气。數(shù)組杏慰、對(duì)象大部分的消費(fèi)方式就是通過遍歷。類似的炼鞠,字符串也有遍歷缘滥,甚至 Generator + yield 也是對(duì)狀態(tài)的遍歷〈亟粒看過或用過這么多 ES6API完域,不知您是否注意到,作為如此共性的 遍歷 這事兒瘩将,ES6 其實(shí)開放了一個(gè)底層概念:Iterator 遍歷器吟税。

讀過 underscore.js 源碼的同學(xué),不難發(fā)現(xiàn)它在 遍歷 問題上也是采用了統(tǒng)一的 口徑 ——即用數(shù)組的 for i++ 方式姿现,對(duì)象的遍歷 for in 最終也是落腳在數(shù)組遍歷上肠仪。

這兩個(gè)問題擺在一起,能為我們?cè)O(shè)計(jì) API 提供怎樣的啟示呢备典?信息量略大异旧,容我想好再說,哈哈~

最后提佣, Iterator 遍歷器這個(gè)偏底層的概念吮蛹,本文還是老套路荤崇,把它先關(guān)在籠子里,以后再來拜會(huì)潮针。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末术荤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子每篷,更是在濱河造成了極大的恐慌瓣戚,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,348評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焦读,死亡現(xiàn)場(chǎng)離奇詭異子库,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)矗晃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門仑嗅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人喧兄,你說我怎么就攤上這事无畔。” “怎么了吠冤?”我有些...
    開封第一講書人閱讀 156,936評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)恭理。 經(jīng)常有香客問我拯辙,道長(zhǎng),這世上最難降的妖魔是什么颜价? 我笑而不...
    開封第一講書人閱讀 56,427評(píng)論 1 283
  • 正文 為了忘掉前任涯保,我火速辦了婚禮,結(jié)果婚禮上周伦,老公的妹妹穿的比我還像新娘夕春。我一直安慰自己,他們只是感情好专挪,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評(píng)論 6 385
  • 文/花漫 我一把揭開白布及志。 她就那樣靜靜地躺著,像睡著了一般寨腔。 火紅的嫁衣襯著肌膚如雪速侈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評(píng)論 1 290
  • 那天迫卢,我揣著相機(jī)與錄音倚搬,去河邊找鬼。 笑死乾蛤,一個(gè)胖子當(dāng)著我的面吹牛每界,可吹牛的內(nèi)容都是我干的捅僵。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼眨层,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼命咐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起谐岁,我...
    開封第一講書人閱讀 37,696評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤醋奠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后伊佃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體窜司,經(jīng)...
    沈念sama閱讀 44,141評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評(píng)論 2 327
  • 正文 我和宋清朗相戀三年航揉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了塞祈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,625評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡帅涂,死狀恐怖议薪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情媳友,我是刑警寧澤斯议,帶...
    沈念sama閱讀 34,291評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站醇锚,受9級(jí)特大地震影響哼御,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜焊唬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評(píng)論 3 312
  • 文/蒙蒙 一恋昼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赶促,春花似錦液肌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至爵赵,卻和暖如春吝秕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背空幻。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工烁峭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓约郁,卻偏偏與公主長(zhǎng)得像缩挑,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鬓梅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 1.屬性的簡(jiǎn)潔表示法 允許直接寫入變量和函數(shù) 上面代碼表明供置,ES6 允許在對(duì)象之中,直接寫變量绽快。這時(shí)芥丧,屬性名為變量...
    雨飛飛雨閱讀 1,130評(píng)論 0 3
  • 三,字符串?dāng)U展 3.1 Unicode表示法 ES6 做出了改進(jìn)坊罢,只要將碼點(diǎn)放入大括號(hào)续担,就能正確解讀該字符。有了這...
    eastbaby閱讀 1,518評(píng)論 0 8
  • 冬季活孩,北方的城市到處都有霧霾物遇,出行帶來了麻煩,但穿過厚厚的霧霾憾儒,能有一束光亮打來询兴,就是一個(gè)溫暖的信號(hào),讓前行更加有...
    葉知秋哥閱讀 99評(píng)論 0 0
  • 好久沒有這么心痛了起趾,這心痛的原因七年來始終沒有改變诗舰。 當(dāng)今天再次看到你因電腦屏幕上的東西而來不及收拾的悲傷心情時(shí),...
    你是此生最美的風(fēng)景l(fā)ove閱讀 179評(píng)論 0 0