對象的擴展

  1. 屬性的簡潔表示法
  2. 屬性名表達式
  3. 方法的 name 屬性
  4. Object.is()
  5. Object.assign()
  6. 屬性的可枚舉性和遍歷
  7. Object.getOwnPropertyDescriptors()
  8. proto屬性谭贪,Object.setPrototypeOf()涎拉,Object.getPrototypeOf()
  9. super 關(guān)鍵字
  10. Object.keys(),Object.values()伙单,Object.entries()
  11. 對象的擴展運算符

1屬性的簡潔表示法 § ?

ES6 允許直接寫入變量和函數(shù)瓣履,作為對象的屬性和方法檐薯。這樣的書寫更加簡潔。

const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}

// 等同于
const baz = {foo: foo};

上面代碼表明袍冷,ES6 允許在對象之中磷醋,直接寫變量。這時胡诗,屬性名為變量名, 屬性值為變量的值邓线。下面是另一個例子淌友。

function f(x, y) {
  return {x, y};
}

// 等同于

function f(x, y) {
  return {x: x, y: y};
}

f(1, 2) // Object {x: 1, y: 2}
let birth = '2000/01/01';

const Person = {

  name: '張三',

  //等同于birth: birth
  birth,

  // 等同于hello: function ()...
  hello() { console.log('我的名字是', this.name); }

};

2屬性名表達式 § ?

JavaScript 定義對象的屬性,有兩種方法褂痰。

// 方法一
obj.foo = true;

// 方法二
obj['a' + 'bc'] = 123;

上面代碼的方法一是直接用標識符作為屬性名亩进,方法二是用表達式作為屬性名,這時要將表達式放在方括號之內(nèi)缩歪。

let lastWord = 'last word';

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

a['first word'] // "hello"
a[lastWord] // "world"
a['last word'] // "world"

表達式還可以用于定義方法名归薛。

let obj = {
  ['h' + 'ello']() {
    return 'hi';
  }
};

obj.hello() // hi

3方法的 name 屬性 § ?

函數(shù)的name屬性,返回函數(shù)名匪蝙。對象方法也是函數(shù)主籍,因此也有name屬性。

const person = {
  sayName() {
    console.log('hello!');
  },
};

person.sayName.name   // "sayName"

如果對象的方法使用了取值函數(shù)(getter)和存值函數(shù)(setter)逛球,則name屬性不是在該方法上面千元,而是該方法的屬性的描述對象的get和set屬性上面,返回值是方法名前加上get和set颤绕。

有兩種特殊情況:bind方法創(chuàng)造的函數(shù)幸海,name屬性返回bound加上原函數(shù)的名字;Function構(gòu)造函數(shù)創(chuàng)造的函數(shù)奥务,name屬性返回anonymous物独。

4 Object.is() § ?

ES5 比較兩個值是否相等,只有兩個運算符:相等運算符(==)和嚴格相等運算符(===)氯葬。它們都有缺點挡篓,前者會自動轉(zhuǎn)換數(shù)據(jù)類型,后者的NaN不等于自身帚称,以及+0等于-0官研。JavaScript 缺乏一種運算,在所有環(huán)境中闯睹,只要兩個值是一樣的戏羽,它們就應(yīng)該相等。

ES6 提出“Same-value equality”(同值相等)算法楼吃,用來解決這個問題始花。Object.is就是部署這個算法的新方法。它用來比較兩個值是否嚴格相等所刀,與嚴格比較運算符(===)的行為基本一致。

不同之處只有兩個:一是+0不等于-0捞挥,二是NaN等于自身浮创。

5Object.assign() § ?

Object.assign方法用于對象的合并,將源對象(source)的所有可枚舉屬性砌函,復(fù)制到目標對象(target)斩披。

const target = { a: 1 };

const source1 = { b: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

Object.assign方法的第一個參數(shù)是目標對象溜族,后面的參數(shù)都是源對象。

注意垦沉,如果目標對象與源對象有同名屬性煌抒,或多個源對象有同名屬性,則后面的屬性會覆蓋前面的屬性厕倍。

const target = { a: 1, b: 1 };

const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

注意點 § ?

(1)淺拷貝

Object.assign方法實行的是淺拷貝寡壮,而不是深拷貝。也就是說讹弯,如果源對象某個屬性的值是對象况既,那么目標對象拷貝得到的是這個對象的引用。

const obj1 = {a: {b: 1}};
const obj2 = Object.assign({}, obj1);

obj1.a.b = 2;
obj2.a.b // 2

上面代碼中组民,源對象obj1的a屬性的值是一個對象棒仍,Object.assign拷貝得到的是這個對象的引用。這個對象的任何變化臭胜,都會反映到目標對象上面莫其。

(2)同名屬性的替換

對于這種嵌套的對象,一旦遇到同名屬性耸三,Object.assign的處理方法是替換乱陡,而不是添加。

const target = { a: { b: 'c', d: 'e' } }
const source = { a: { b: 'hello' } }
Object.assign(target, source)
// { a: { b: 'hello' } }

上面代碼中吕晌,target對象的a屬性被source對象的a屬性整個替換掉了蛋褥,而不會得到{ a: { b: 'hello', d: 'e' } }的結(jié)果。這通常不是開發(fā)者想要的睛驳,需要特別小心烙心。

一些函數(shù)庫提供Object.assign的定制版本(比如 Lodash 的_.defaultsDeep方法),可以得到深拷貝的合并乏沸。

(3)數(shù)組的處理

Object.assign可以用來處理數(shù)組淫茵,但是會把數(shù)組視為對象。

Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]

上面代碼中蹬跃,Object.assign把數(shù)組視為屬性名為 0匙瘪、1、2 的對象蝶缀,因此源數(shù)組的 0 號屬性4覆蓋了目標數(shù)組的 0 號屬性1丹喻。

(4)取值函數(shù)的處理

Object.assign只能進行值的復(fù)制,如果要復(fù)制的值是一個取值函數(shù)翁都,那么將求值后再復(fù)制碍论。

const source = {
  get foo() { return 1 }
};
const target = {};

Object.assign(target, source)
// { foo: 1 }

上面代碼中,source對象的foo屬性是一個取值函數(shù)柄慰,Object.assign不會復(fù)制這個取值函數(shù)鳍悠,只會拿到值以后税娜,將這個值復(fù)制過去。

常見用途 § ?

Object.assign方法有很多用處藏研。

(1)為對象添加屬性

class Point {
  constructor(x, y) {
    Object.assign(this, {x, y});
  }
}

上面方法通過Object.assign方法敬矩,將x屬性和y屬性添加到Point類的對象實例。

(2)為對象添加方法

Object.assign(SomeClass.prototype, {
  someMethod(arg1, arg2) {
    ···
  },
  anotherMethod() {
    ···
  }
});

// 等同于下面的寫法
SomeClass.prototype.someMethod = function (arg1, arg2) {
  ···
};
SomeClass.prototype.anotherMethod = function () {
  ···
};

上面代碼使用了對象屬性的簡潔表示法蠢挡,直接將兩個函數(shù)放在大括號中弧岳,再使用assign方法添加到SomeClass.prototype之中。

(3)克隆對象

function clone(origin) {
  return Object.assign({}, origin);
}

上面代碼將原始對象拷貝到一個空對象袒哥,就得到了原始對象的克隆缩筛。

克隆。

不過堡称,采用這種方法克隆瞎抛,只能克隆原始對象自身的值,不能克隆它繼承的值却紧。如果想要保持繼承鏈桐臊,可以采用下面的代碼。

function clone(origin) {
  let originProto = Object.getPrototypeOf(origin);
  return Object.assign(Object.create(originProto), origin);
}

(4)合并多個對象

將多個對象合并到某個對象晓殊。

const merge =
  (target, ...sources) => Object.assign(target, ...sources);

如果希望合并后返回一個新對象断凶,可以改寫上面函數(shù),對一個空對象合并巫俺。

const merge =
  (...sources) => Object.assign({}, ...sources);

(5)為屬性指定默認值

const DEFAULTS = {
  logLevel: 0,
  outputFormat: 'html'
};

function processContent(options) {
  options = Object.assign({}, DEFAULTS, options);
  console.log(options);
  // ...
}

6屬性的可枚舉性和遍歷 § ?

可枚舉性 § ?

let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
//  {
//    value: 123,
//    writable: true,
//    enumerable: true,
//    configurable: true
//  }

描述對象的enumerable屬性认烁,稱為”可枚舉性“,如果該屬性為false介汹,就表示某些操作會忽略當前屬性却嗡。

目前,有四個操作會忽略enumerable為false的屬性嘹承。

1 .for...in循環(huán):只遍歷對象自身的和繼承的可枚舉的屬性窗价。

  1. Object.keys():返回對象自身的所有可枚舉的屬性的鍵名。
  2. JSON.stringify():只串行化對象自身的可枚舉的屬性叹卷。
  3. Object.assign(): 忽略enumerable為false的屬性撼港,只拷貝對象自身的可枚舉的屬性。

這四個操作之中骤竹,前三個是 ES5 就有的帝牡,最后一個Object.assign()是 ES6 新增的。其中蒙揣,只有for...in會返回繼承的屬性靶溜,其他三個方法都會忽略繼承的屬性,只處理對象自身的屬性鸣奔。實際上墨技,引入“可枚舉”(enumerable)這個概念的最初目的,就是讓某些屬性可以規(guī)避掉for...in操作挎狸,不然所有內(nèi)部屬性和方法都會被遍歷到扣汪。比如,對象原型的toString方法锨匆,以及數(shù)組的length屬性崭别,就通過“可枚舉性”,從而避免被for...in遍歷到恐锣。

另外茅主,ES6 規(guī)定,所有 Class 的原型的方法都是不可枚舉的土榴。

Object.getOwnPropertyDescriptor(class {foo() {}}.prototype, 'foo').enumerable
// false

總的來說诀姚,操作中引入繼承的屬性會讓問題復(fù)雜化,大多數(shù)時候玷禽,我們只關(guān)心對象自身的屬性赫段。所以,盡量不要用for...in循環(huán)矢赁,而用Object.keys()代替糯笙。

屬性的遍歷 § ?

ES6 一共有 5 種方法可以遍歷對象的屬性。

(1)for...in

for...in循環(huán)遍歷對象自身的和繼承的可枚舉屬性(不含 Symbol 屬性)撩银。

(2)Object.keys(obj)

Object.keys返回一個數(shù)組给涕,包括對象自身的(不含繼承的)所有可枚舉屬性(不含 Symbol 屬性)的鍵名。

(3)Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames返回一個數(shù)組额获,包含對象自身的所有屬性(不含 Symbol 屬性够庙,但是包括不可枚舉屬性)的鍵名。

(4)Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols返回一個數(shù)組咪啡,包含對象自身的所有 Symbol 屬性的鍵名首启。

(5)Reflect.ownKeys(obj)

Reflect.ownKeys返回一個數(shù)組,包含對象自身的所有鍵名撤摸,不管鍵名是 Symbol 或字符串毅桃,也不管是否可枚舉。

以上的 5 種方法遍歷對象的鍵名准夷,都遵守同樣的屬性遍歷的次序規(guī)則钥飞。

首先遍歷所有數(shù)值鍵,按照數(shù)值升序排列衫嵌。
其次遍歷所有字符串鍵读宙,按照加入時間升序排列。
最后遍歷所有 Symbol 鍵楔绞,按照加入時間升序排列结闸。

7Object.getOwnPropertyDescriptors() § ?

前面說過唇兑,Object.getOwnPropertyDescriptor方法會返回某個對象屬性的描述對象(descriptor)。ES2017 引入了Object.getOwnPropertyDescriptors方法桦锄,返回指定對象所有自身屬性(非繼承屬性)的描述對象扎附。

該方法的引入目的,主要是為了解決Object.assign()無法正確拷貝get屬性和set屬性的問題结耀。

8 proto屬性留夜,Object.setPrototypeOf(),Object.getPrototypeOf() § ?

proto屬性(前后各兩個下劃線)图甜,用來讀取或設(shè)置當前對象的prototype對象碍粥。目前,所有瀏覽器(包括 IE11)都部署了這個屬性黑毅。

Object.setPrototypeOf() § ?

Object.setPrototypeOf方法的作用與proto相同嚼摩,用來設(shè)置一個對象的prototype對象,返回參數(shù)對象本身矿瘦。它是 ES6 正式推薦的設(shè)置原型對象的方法低斋。

Object.getPrototypeOf() § ?

9 super 關(guān)鍵字 § ?

我們知道,this關(guān)鍵字總是指向函數(shù)所在的當前對象匪凡,ES6 又新增了另一個類似的關(guān)鍵字super膊畴,指向當前對象的原型對象。

const proto = {
  foo: 'hello'
};

const obj = {
  foo: 'world',
  find() {
    return super.foo;
  }
};

Object.setPrototypeOf(obj, proto);
obj.find() // "hello"

上面代碼中病游,對象obj的find方法之中唇跨,通過super.foo引用了原型對象proto的foo屬性。

注意衬衬,super關(guān)鍵字表示原型對象時买猖,只能用在對象的方法之中,用在其他地方都會報錯滋尉。

10 Object.keys()玉控,Object.values(),Object.entries() § ?

Object.keys() § ?

ES5 引入了Object.keys方法狮惜,返回一個數(shù)組高诺,成員是參數(shù)對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵名。

var obj = { foo: 'bar', baz: 42 };
Object.keys(obj)
// ["foo", "baz"]

ES2017 引入了跟Object.keys配套的Object.valuesObject.entries碾篡,作為遍歷一個對象的補充手段虱而,供for...of循環(huán)使用。

let {keys, values, entries} = Object;
let obj = { a: 1, b: 2, c: 3 };

for (let key of keys(obj)) {
  console.log(key); // 'a', 'b', 'c'
}

for (let value of values(obj)) {
  console.log(value); // 1, 2, 3
}

for (let [key, value] of entries(obj)) {
  console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
}

Object.values() § ?

Object.values方法返回一個數(shù)組开泽,成員是參數(shù)對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值牡拇。

const obj = { foo: 'bar', baz: 42 };
Object.values(obj)
// ["bar", 42]

返回數(shù)組的成員順序,與本章的《屬性的遍歷》部分介紹的排列規(guī)則一致。

Object.values只返回對象自身的可遍歷屬性惠呼。

Object.entries § ?

Object.entries方法返回一個數(shù)組导俘,成員是參數(shù)對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值對數(shù)組。

const obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]

除了返回值不一樣剔蹋,該方法的行為與Object.values基本一致趟畏。

如果原對象的屬性名是一個 Symbol 值,該屬性會被忽略滩租。

Object.entries({ [Symbol()]: 123, foo: 'abc' });
// [ [ 'foo', 'abc' ] ]

Object.entries方法的另一個用處是,將對象轉(zhuǎn)為真正的Map結(jié)構(gòu)利朵。

對象的擴展運算符 § ?

《數(shù)組的擴展》一章中律想,已經(jīng)介紹過擴展運算符(...)。

解構(gòu)賦值 § ?

對象的解構(gòu)賦值用于從一個對象取值绍弟,相當于將目標對象自身的所有可遍歷的(enumerable)技即、但尚未被讀取的屬性,分配到指定的對象上面樟遣。所有的鍵和它們的值而叼,都會拷貝到新對象上面。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末豹悬,一起剝皮案震驚了整個濱河市葵陵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瞻佛,老刑警劉巖脱篙,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異伤柄,居然都是意外死亡绊困,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門适刀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來秤朗,“玉大人,你說我怎么就攤上這事笔喉∪∈樱” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵常挚,是天一觀的道長贫途。 經(jīng)常有香客問我,道長待侵,這世上最難降的妖魔是什么丢早? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上怨酝,老公的妹妹穿的比我還像新娘傀缩。我一直安慰自己,他們只是感情好农猬,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布赡艰。 她就那樣靜靜地躺著,像睡著了一般斤葱。 火紅的嫁衣襯著肌膚如雪慷垮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機與錄音悄但,去河邊找鬼先紫。 笑死,一個胖子當著我的面吹牛芹血,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播楞慈,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼幔烛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了囊蓝?” 一聲冷哼從身側(cè)響起饿悬,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎聚霜,沒想到半個月后乡恕,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡俯萎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年傲宜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夫啊。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡函卒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出撇眯,到底是詐尸還是另有隱情报嵌,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布熊榛,位于F島的核電站锚国,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏玄坦。R本人自食惡果不足惜血筑,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一绘沉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧豺总,春花似錦车伞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至表伦,卻和暖如春谦去,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蹦哼。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工鳄哭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人翔怎。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像杨耙,于是被迫代替她去往敵國和親赤套。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

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

  • 1. 屬性的間接表示法 ES6 允許直接寫入變量和函數(shù) 除屬性外 方法也可以簡寫 2. 屬性名表達式 ES6 允許...
    MrZhou_b216閱讀 218評論 0 0
  • 1.屬性的簡潔表示法 ES6 允許直接寫入變量和函數(shù)珊膜,作為對象的屬性和方法容握。這樣的書寫更加簡潔。 const fo...
    Masami_9e88閱讀 344評論 0 0
  • 屬性的簡潔表示法 ES6 允許直接寫入變量和函數(shù)车柠,作為對象的屬性和方法剔氏。這樣的書寫更加簡潔。 Object.is(...
    小卡丁閱讀 266評論 0 0
  • 一竹祷、屬性的簡介表示法 (1)ES6 允許直接寫入變量和函數(shù)谈跛,作為對象的屬性和方法。這樣的書寫更加簡潔 上面代碼表明...
    不可不信緣_b32e閱讀 339評論 0 0
  • 我推開門塑陵,屋里烏煙瘴氣感憾,一群油膩的中年人正醉酒熏天,以為自己走錯了門令花,正準備退出阻桅,一哥們招呼我說,怎么才回來兼都,接著...
    風(fēng)熊花吉閱讀 95評論 0 0