Object.defineProperty

Object.defineProperty

Object.defineProperty()方法會(huì)直接在一個(gè)對(duì)象上定義一個(gè)新屬性胎撇,或者修改一個(gè)對(duì)象的現(xiàn)有屬性半哟,并返回此對(duì)象绘证。

備注:應(yīng)當(dāng)直接在 Object 構(gòu)造器對(duì)象上調(diào)用此方法撵割,而不是在任意一個(gè) Object 類型的實(shí)例上調(diào)用。

語法

Object.defineProperty(obj, prop, descriptor)
參數(shù)
  • obj

    要定義屬性的對(duì)象跑杭。

  • prop

    要定義或修改的屬性的名稱或symbol铆帽。

  • descriptor

    要定義或修改的屬性描述符。

返回值

被傳遞給函數(shù)的對(duì)象德谅。

在ES6中爹橱,由于 Symbol類型的特殊性,用Symbol類型的值來做對(duì)象的key與常規(guī)的定義或修改不同窄做,而Object.defineProperty 是定義key為Symbol的屬性的方法之一愧驱。

描述

該方法允許精確地添加或修改對(duì)象的屬性。通過賦值操作添加的普通屬性是可枚舉的浸策,在枚舉對(duì)象屬性時(shí)會(huì)被枚舉到(for...inObject.keys方法)冯键,可以改變這些屬性的值,也可以刪除這些屬性庸汗。這個(gè)方法允許修改默認(rèn)的額外選項(xiàng)(或配置)惫确。默認(rèn)情況下,使用 Object.defineProperty() 添加的屬性值是不可修改(immutable)的蚯舱。

對(duì)象里目前存在的屬性描述符有兩種主要形式:數(shù)據(jù)描述符存取描述符改化。數(shù)據(jù)描述符是一個(gè)具有值的屬性,該值可以是可寫的枉昏,也可以是不可寫的陈肛。存取描述符是由 getter 函數(shù)和 setter 函數(shù)所描述的屬性。一個(gè)描述符只能是這兩者其中之一兄裂;不能同時(shí)是兩者句旱。

這兩種描述符都是對(duì)象。它們共享以下可選鍵值(默認(rèn)值是指在使用 Object.defineProperty() 定義屬性時(shí)的默認(rèn)值):

  • configurable

    當(dāng)且僅當(dāng)該屬性的 configurable 鍵值為 true 時(shí)晰奖,該屬性的描述符才能夠被改變谈撒,同時(shí)該屬性也能從對(duì)應(yīng)的對(duì)象上被刪除。 默認(rèn)為 false匾南。

  • enumerable

    當(dāng)且僅當(dāng)該屬性的 enumerable 鍵值為 true 時(shí)啃匿,該屬性才會(huì)出現(xiàn)在對(duì)象的枚舉屬性中。 默認(rèn)為 false蛆楞。

數(shù)據(jù)描述符還具有以下可選鍵值:

  • value

    該屬性對(duì)應(yīng)的值溯乒。可以是任何有效的 JavaScript 值(數(shù)值豹爹,對(duì)象裆悄,函數(shù)等)。 默認(rèn)為 undefined臂聋。

  • writable

    當(dāng)且僅當(dāng)該屬性的 writable 鍵值為 true 時(shí)光稼,屬性的值崖技,也就是上面的 value,才能被賦值運(yùn)算符改變钟哥。 默認(rèn)為 false迎献。

存取描述符還具有以下可選鍵值:

  • get

    屬性的 getter 函數(shù),如果沒有 getter腻贰,則為 undefined吁恍。當(dāng)訪問該屬性時(shí),會(huì)調(diào)用此函數(shù)播演。執(zhí)行時(shí)不傳入任何參數(shù)冀瓦,但是會(huì)傳入 this 對(duì)象(由于繼承關(guān)系,這里的this并不一定是定義該屬性的對(duì)象)写烤。該函數(shù)的返回值會(huì)被用作屬性的值翼闽。 默認(rèn)為 undefined

  • set

    屬性的 setter 函數(shù)洲炊,如果沒有 setter感局,則為 undefined。當(dāng)屬性值被修改時(shí)暂衡,會(huì)調(diào)用此函數(shù)询微。該方法接受一個(gè)參數(shù)(也就是被賦予的新值),會(huì)傳入賦值時(shí)的 this 對(duì)象狂巢。 默認(rèn)為 undefined撑毛。

描述符默認(rèn)值匯總

  • 擁有布爾值的鍵 configurableenumerablewritable 的默認(rèn)值都是 false唧领。
  • 屬性值和函數(shù)的鍵 value藻雌、getset 字段的默認(rèn)值為 undefined

描述符可擁有的鍵值

configurable enumerable value writable get set
數(shù)據(jù)描述符 可以 可以 可以 可以 不可以 不可以
存取描述符 可以 可以 不可以 不可以 可以 可以

如果一個(gè)描述符不具有 value斩个、writable胯杭、getset 中的任意一個(gè)鍵,那么它將被認(rèn)為是一個(gè)數(shù)據(jù)描述符萨驶。如果一個(gè)描述符同時(shí)擁有 valuewritablegetset 鍵歉摧,則會(huì)產(chǎn)生一個(gè)異常艇肴。

示例

創(chuàng)建屬性

如果對(duì)象中不存在指定的屬性腔呜,Object.defineProperty() 會(huì)創(chuàng)建這個(gè)屬性。當(dāng)描述符中省略某些字段時(shí)再悼,這些字段將使用它們的默認(rèn)值核畴。

var o = {}; // 創(chuàng)建一個(gè)新對(duì)象

// 在對(duì)象中添加一個(gè)屬性與數(shù)據(jù)描述符的示例
Object.defineProperty(o, "a", {
  value : 37,
  writable : true,
  enumerable : true,
  configurable : true
});

// 對(duì)象 o 擁有了屬性 a,值為 37

// 在對(duì)象中添加一個(gè)設(shè)置了存取描述符屬性的示例
var bValue = 38;
Object.defineProperty(o, "b", {
  // 使用了方法名稱縮寫(ES2015 特性)
  // 下面兩個(gè)縮寫等價(jià)于:
  // get : function() { return bValue; },
  // set : function(newValue) { bValue = newValue; },
  get() { return bValue; },
  set(newValue) { bValue = newValue; },
  enumerable : true,
  configurable : true
});

o.b; // 38
// 對(duì)象 o 擁有了屬性 b冲九,值為 38
// 現(xiàn)在谤草,除非重新定義 o.b跟束,o.b 的值總是與 bValue 相同

// 數(shù)據(jù)描述符和存取描述符不能混合使用
Object.defineProperty(o, "conflict", {
  value: 0x9f91102,
  get() { return 0xdeadbeef; }
});
// 拋出錯(cuò)誤 TypeError: value appears only in data descriptors, get appears only in accessor descriptors

修改屬性

如果屬性已經(jīng)存在,Object.defineProperty()將嘗試根據(jù)描述符中的值以及對(duì)象當(dāng)前的配置來修改這個(gè)屬性丑孩。如果舊描述符將其configurable 屬性設(shè)置為false冀宴,則該屬性被認(rèn)為是“不可配置的”,并且沒有屬性可以被改變(除了單向改變 writable 為 false)温学。當(dāng)屬性不可配置時(shí)略贮,不能在數(shù)據(jù)和訪問器屬性類型之間切換。

當(dāng)試圖改變不可配置屬性(除了 valuewritable 屬性之外)的值時(shí)仗岖,會(huì)拋出TypeError逃延,除非當(dāng)前值和新值相同。

Writable 屬性

當(dāng) writable 屬性設(shè)置為 false 時(shí)轧拄,該屬性被稱為“不可寫的”揽祥。它不能被重新賦值。

var o = {}; // 創(chuàng)建一個(gè)新對(duì)象

Object.defineProperty(o, 'a', {
  value: 37,
  writable: false
});

console.log(o.a); // logs 37
o.a = 25; // No error thrown
// (it would throw in strict mode,
// even if the value had been the same)
console.log(o.a); // logs 37. The assignment didn't work.

// strict mode
(function() {
  'use strict';
  var o = {};
  Object.defineProperty(o, 'b', {
    value: 2,
    writable: false
  });
  o.b = 3; // throws TypeError: "b" is read-only
  return o.b; // returns 2 without the line above
}());
Enumerable 屬性

enumerable 定義了對(duì)象的屬性是否可以在 for...in循環(huán)和 Object.keys()中被枚舉檩电。

var o = {};
Object.defineProperty(o, "a", { value : 1, enumerable: true });
Object.defineProperty(o, "b", { value : 2, enumerable: false });
Object.defineProperty(o, "c", { value : 3 }); // enumerable 默認(rèn)為 false
o.d = 4; // 如果使用直接賦值的方式創(chuàng)建對(duì)象的屬性拄丰,則 enumerable 為 true
Object.defineProperty(o, Symbol.for('e'), {
  value: 5,
  enumerable: true
});
Object.defineProperty(o, Symbol.for('f'), {
  value: 6,
  enumerable: false
});

for (var i in o) {
  console.log(i);
}
// logs 'a' and 'd' (in undefined order)

Object.keys(o); // ['a', 'd']

o.propertyIsEnumerable('a'); // true
o.propertyIsEnumerable('b'); // false
o.propertyIsEnumerable('c'); // false
o.propertyIsEnumerable('d'); // true
o.propertyIsEnumerable(Symbol.for('e')); // true
o.propertyIsEnumerable(Symbol.for('f')); // false

var p = { ...o }
p.a // 1
p.b // undefined
p.c // undefined
p.d // 4
p[Symbol.for('e')] // 5
p[Symbol.for('f')] // undefined
Configurable 屬性

configurable 特性表示對(duì)象的屬性是否可以被刪除,以及除 valuewritable 特性外的其他特性是否可以被修改俐末。

var o = {};
Object.defineProperty(o, 'a', {
  get() { return 1; },
  configurable: false
});

Object.defineProperty(o, 'a', {
  configurable: true
}); // throws a TypeError
Object.defineProperty(o, 'a', {
  enumerable: true
}); // throws a TypeError
Object.defineProperty(o, 'a', {
  set() {}
}); // throws a TypeError (set was undefined previously)
Object.defineProperty(o, 'a', {
  get() { return 1; }
}); // throws a TypeError
// (even though the new get does exactly the same thing)
Object.defineProperty(o, 'a', {
  value: 12
}); // throws a TypeError // ('value' can be changed when 'configurable' is false but not in this case due to 'get' accessor)

console.log(o.a); // logs 1
delete o.a; // Nothing happens
console.log(o.a); // logs 1

如果 o.aconfigurable 屬性為 true愈案,則不會(huì)拋出任何錯(cuò)誤,并且鹅搪,最后站绪,該屬性會(huì)被刪除。

添加多個(gè)屬性和默認(rèn)值

考慮特性被賦予的默認(rèn)特性值非常重要丽柿,通常恢准,使用點(diǎn)運(yùn)算符和 Object.defineProperty() 為對(duì)象的屬性賦值時(shí),數(shù)據(jù)描述符中的屬性默認(rèn)值是不同的甫题,如下例所示馁筐。

var o = {};

o.a = 1;
// 等同于:
Object.defineProperty(o, "a", {
  value: 1,
  writable: true,
  configurable: true,
  enumerable: true
});


// 另一方面,
Object.defineProperty(o, "a", { value : 1 });
// 等同于:
Object.defineProperty(o, "a", {
  value: 1,
  writable: false,
  configurable: false,
  enumerable: false
});
自定義 Setters 和 Getters

下面的例子展示了如何實(shí)現(xiàn)一個(gè)自存檔對(duì)象坠非。當(dāng)設(shè)置temperature 屬性時(shí)敏沉,archive 數(shù)組會(huì)收到日志條目。

function Archiver() {
  var temperature = null;
  var archive = [];

  Object.defineProperty(this, 'temperature', {
    get: function() {
      console.log('get!');
      return temperature;
    },
    set: function(value) {
      temperature = value;
      archive.push({ val: temperature });
    }
  });

  this.getArchive = function() { return archive; };
}

var arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]
繼承屬性

如果訪問者的屬性是被繼承的炎码,它的 getset 方法會(huì)在子對(duì)象的屬性被訪問或者修改時(shí)被調(diào)用盟迟。如果這些方法用一個(gè)變量存值,該值會(huì)被所有對(duì)象共享潦闲。

function myclass() {
}

var value;
Object.defineProperty(myclass.prototype, "x", {
  get() {
    return value;
  },
  set(x) {
    value = x;
  }
});

var a = new myclass();
var b = new myclass();
a.x = 1;
console.log(b.x); // 1
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末攒菠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子歉闰,更是在濱河造成了極大的恐慌辖众,老刑警劉巖卓起,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異凹炸,居然都是意外死亡戏阅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門啤它,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饲握,“玉大人,你說我怎么就攤上這事蚕键【扰罚” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵锣光,是天一觀的道長笆怠。 經(jīng)常有香客問我,道長誊爹,這世上最難降的妖魔是什么蹬刷? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮频丘,結(jié)果婚禮上办成,老公的妹妹穿的比我還像新娘。我一直安慰自己搂漠,他們只是感情好迂卢,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著桐汤,像睡著了一般而克。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上怔毛,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天员萍,我揣著相機(jī)與錄音,去河邊找鬼拣度。 笑死碎绎,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的抗果。 我是一名探鬼主播筋帖,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼窖张!你這毒婦竟也來了幕随?” 一聲冷哼從身側(cè)響起蚁滋,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤宿接,失蹤者是張志新(化名)和其女友劉穎赘淮,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體睦霎,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡梢卸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了副女。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛤高。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖碑幅,靈堂內(nèi)的尸體忽然破棺而出戴陡,到底是詐尸還是另有隱情,我是刑警寧澤沟涨,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布恤批,位于F島的核電站,受9級(jí)特大地震影響裹赴,放射性物質(zhì)發(fā)生泄漏喜庞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一棋返、第九天 我趴在偏房一處隱蔽的房頂上張望延都。 院中可真熱鬧,春花似錦睛竣、人聲如沸晰房。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嫉你。三九已至,卻和暖如春躏惋,著一層夾襖步出監(jiān)牢的瞬間幽污,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來泰國打工簿姨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留距误,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓扁位,卻偏偏與公主長得像准潭,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子域仇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

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