js之Object.defineProperty和Object.defineProperties詳解

 1. Object.defineProperty() 

Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性,並返回此對象。

備註:應當直接在 Object 構造器對象上調用此方法,而不是在任意一個 Object 類型的實例上調用。

語法:

Object.defineProperty(obj, prop, descriptor)

obj

要定義屬性的對象。

prop

要定義或修改的屬性的名稱或 Symbol 。

descriptor

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

例子

在我們平常的使用中,給對象添加一個屬性時,直接使用object.param的方式就可以了,或者直接在對象中掛載。

const person = {
    name: 'hj'
}

在ECMAScript5中,對每個屬性都添加了幾個屬性類型,來描述這些屬性的特點。他們分別是

  • configurable:   默認false

configurable 特性表示對象的屬性是否可以被刪除,以及除 value 和 writable 特性外的其他特性是否可以被修改。

當第一次設置爲false後,再改寫是不可以的。屬性值也是不能被刪除的。

var o = {};
Object.defineProperty(o, 'a', {
  get() { return 1; },
  configurable: false
});
// 定義爲false 後  enumerable set get configurable value 都是不能再設置了。 delete o.a 也是刪不了的
Object.defineProperty(o, 'a', {
  configurable: true
}); // throws a TypeError  拋出錯誤
Object.defineProperty(o, 'a', {
  enumerable: true
}); // 拋出錯誤
Object.defineProperty(o, 'a', {
  set() {}
}); // 報錯
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  還存在說明沒刪掉

 

  • enumerable:  默認false

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

for...in 循環和 Object.keys()  定義: 任意順序遍歷一個對象的除Symbol以外的可枚舉屬性。

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 默認爲 false
o.d = 4; // 如果使用直接賦值的方式創建對象的屬性,則 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);
}
// 只會打印a 和 d 

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

打印 對象 o 在谷歌瀏覽器中查看 

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
  • writable:  默認false

當 writable 屬性設置爲 false 時,該屬性被稱爲“不可寫的”。它不能被重新賦值。

var o = {}; // 創建一個新對象

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

console.log(o.a); //  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); // 還是37 

// 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
}());
  •  value :  默認undefined

如果對象中不存在指定的屬性,Object.defineProperty() 會創建這個屬性。當描述符中省略某些字段時,這些字段將使用它們的默認值。

var o = {}; // 創建一個新對象

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

// 對象 o 擁有了屬性 a,值爲 37

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

o.b; // 38
// 對象 o 擁有了屬性 b,值爲 38
// 現在,除非重新定義 o.b,o.b 的值總是與 bValue 相同

// 數據描述符和存取描述符不能混合使用
Object.defineProperty(o, "conflict", {
  value: 0x9f91102,
  get() { return 0xdeadbeef; } 
});
// 拋出錯誤 TypeError: value appears only in data descriptors, get appears only in accessor descriptors
  • get: 當我們通過person.name訪問name的值時,get將被調用。該方法可以自定義返回的具體值是多少。get默認值爲undefined
  • set: 當我們通過person.name = 'Jake'設置name的值時,set方法將被調用。該方法可以自定義設置值的具體方式。set默認值爲undefined

考慮特性被賦予的默認特性值非常重要,通常,使用點運算符和 Object.defineProperty() 爲對象的屬性賦值時,數據描述符中的屬性默認值是不同的

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
});

需要注意的是,不能同時設置value、writable 與 get、set的值。

 

var person = {}

// 通過get與set自定義訪問與設置name屬性的方式
Object.defineProperty(person, 'name', {
    get: function() {
        // 一直返回TOM
        return 'TOM'
    },
    set: function(value) {
        // 設置name屬性時,返回該字符串,value爲新值
        console.log(value + ' in set');
    }
})

// 第一次訪問name,調用get
console.log(person.name)   // TOM

// 嘗試修改name值,此時set方法被調用
person.name = 'alex'   // alex in set

// 第二次訪問name,還是調用get
console.log(person.name) // TOM

請儘量同時設置get、set。如果僅僅只設置了get,那麼我們將無法設置該屬性值。如果僅僅只設置了set,我們也無法讀取該屬性的值。

2.Object.defineProperties

當我們想要同時設置多個屬性的特性時,需要使用Object.defineProperties

語法:  Object.defineProperties(obj, props)

參數說明

obj

在其上定義或修改屬性的對象。

props

要定義其可枚舉屬性或修改的屬性描述符的對象。對象中存在的屬性描述符主要有兩種:數據描述符和訪問器描述符(更多詳情,請參閱Object.defineProperty())。描述符具有以下鍵:

configurable

true 當且僅當該屬性描述符的類型可以被改變並且該屬性可以從對應對象中刪除。
默認爲 false

enumerable

true 當且僅當在枚舉相應對象上的屬性時該屬性顯現。
默認爲 false

value

與屬性關聯的值。可以是任何有效的JavaScript值(數字,對象,函數等)。
默認爲 undefined.

writable

true當且僅當與該屬性相關聯的值可以用assignment operator改變時。
默認爲 false

get

作爲該屬性的 getter 函數,如果沒有 getter 則爲undefined。函數返回值將被用作屬性的值。
默認爲 undefined

set

作爲屬性的 setter 函數,如果沒有 setter 則爲undefined。函數將僅接受參數賦值給該屬性的新值。
默認爲 undefined

用法除了格式基本與Object.defineProperty相同

var person = {}

Object.defineProperties(person, {
    name: {
        value: 'Jake',
        configurable: true
    },
    age: {
        get: function() {
            return this.value || 22
        },
        set: function(value) {
            this.value = value
        }
    }
})

person.name   // Jake
person.age    // 22

讀取屬性的特性值

我們可以使用Object.getOwnPropertyDescriptor方法讀取某一個屬性的特性值。

 

var person = {}

Object.defineProperty(person, 'name', {
    value: 'alex',
    writable: false,
    configurable: false
})

var descripter = Object.getOwnPropertyDescriptor(person, 'name');

console.log(descripter);  // 返回結果如下

descripter = {
    configurable: false,
    enumerable: false,
    value: 'alex',
    writable: false
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章