1. 程式人生 > 其它 >Object函式的內建方法 Object.defineProperty

Object函式的內建方法 Object.defineProperty

一、定義

Object.defineProperty()方法用於在物件上定義或修改一個自有屬性,並返回這個物件。

二、語法

Object.defineProperty(obj, prop, descriptor)

引數:

obj:要定義或修改屬性的物件

prop:屬性字串名

descriptor:屬性描述符物件

返回值:

被傳遞給函式的物件

三、屬性描述符

物件的屬性的屬性描述符主要有兩種形式,分別是資料描述符和存取描述符。

資料描述符是一個具有值的屬性。

存取描述符是由getter和setter函式描述的屬性。

 

資料描述符具有的鍵值:

configurable:可配置性。當該特性為true時,屬性描述符可以被修改,屬性可以被刪除。預設為false。

enumerable:可列舉性。當該特性為true時,屬性可以出現在物件的列舉屬性中。預設為false。

writable:可寫性。當該特性為true時,屬性的值可以被賦值運算子改變。預設為false。

value:屬性對應的值。可以是任意有效的JavaScript值。預設為undefined。

存取描述符具有的鍵值:

configurable:可配置性。當該特性為true時,屬性描述符可以被修改,屬性可以被刪除。預設為false。

enumerable:可列舉性。當該特性為true時,屬性可以出現在物件的列舉屬性中。預設為false。

get:一個給屬性提供getter的方法。如果沒有getter則預設為undefined,屬性將不可讀。

set:一個給屬性提供setter的方法。如果沒有setter則預設為undefined,屬性將不可以修改。

如果一個屬性描述符不具有value、writable、set和get的任意一個關鍵字,則認為是一個數據描述符。

四、示例

示例1:定義一個數據屬性

var obj = Object.defineProperty({}, 'a', {
    value: 1
});

Object.getOwnPropertyDescriptor(obj, 'a');
// {value: 1, writable: false, enumerable: false, configurable: false}

obj.b = 2;
Object.getOwnPropertyDescriptor(obj, 'b');
// {value: 2, writable: true, enumerable: true, configurable: true}

通過Object.defineProperty()方法定義的資料屬性,如果沒有指定writable、enumerable、configurable特性,則這三個特性預設為false。

通過表示式直接建立的屬性(b),預設是資料屬性,且writable、enumerable、configurable特性,則這三個特性預設為true。

示例2:定義一個存取屬性

var obj = { x: 1 };
Object.defineProperty(obj, 'a', {
    get: function () { return this.x;},
    set: function (val) { this.x = val;}
});
Object.getOwnPropertyDescriptor(obj, 'a');
// {get: ƒ, set: ƒ, enumerable: false, configurable: false}

通過Object.defineProperty()方法定義的存取屬性,如果沒有指定enumerable、configurable特性,則這兩個特性預設為false。

示例3:資料屬性的特性功能示例

// 建立一個空物件
var obj = {};

// 定義一個可寫的、不可列舉的、可配置的屬性x
Object.defineProperty(obj, 'x', {
    value: 1,
    writable: true,
    enumerable: false,
    configurable: true
});

// 訪問物件的屬性
obj.x; // 1,說明屬性是存在的

// 列舉物件的屬性,獲取不到屬性x
Object.keys(obj); // []

// 修改屬性的可寫性,使其變為只讀
Object.defineProperty(obj, 'x', { writable: false });

// 嘗試修改屬性的值
obj.x = 2;
obj.x; // 1, 靜默修改失敗,在嚴格模式下引發TypeError異常。

// 目前屬性是可配置的,故可以通過defineProperty來修改屬性的值
Object.defineProperty(obj, 'x', { value: 2 });
obj.x;  // 2 屬性值修改成功

// 將屬性可寫性改回可寫
Object.defineProperty(obj, 'x', { writable: true });

// 將屬性可配置性改成不可配置
Object.defineProperty(obj, 'x', { configurable: false });

// 此時,可以修改物件屬性的值,但不能修改物件屬性的可列舉性和可配置性,強行修改會引發TypeError
// 但此時,依然可以把物件屬性的可寫性改回不可寫
Object.defineProperty(obj, 'x', { writable: false });

// 自從之後,物件的屬性x,不可配置、不可修改、不可列舉、不可刪除。

如果configurable為false,不可修改enumerable和configurable,但可以把writable從true改為false,無法把writable從false改為true。

如果configurable為true,writable為false,只能通過Object.defineProperty()方法來修改屬性的值。

示例4:存取屬性的特性功能演示

var obj = {};
obj.a = 1;
obj.b = 2;
Object.defineProperty(obj, 'c', {
    get: function () {
        return Math.sqrt(obj.a * obj.a + obj.b * obj.b);
    },
    set: function (value) {
        obj.a = value;
    }
});

Object.getOwnPropertyDescriptor(obj, 'c');
// {get: ƒ, set: ƒ, enumerable: false, configurable: false}
// 由於在定義屬性c的時候沒有顯式指定enumerable和configurable的值,故這2個特性預設為false。
// 此時不能修改其getter和setter方法。

// 嘗試修改為資料屬性
Object.defineProperty(obj, 'c', { value: 3 });
// 上述操作會引發TypeError:Cannot redefine property: c

如果存取屬性是不可配置的,則不能修改其getter和setter方法,也不能將其轉換為資料屬性。

示例5:資料屬性和存取屬性的轉換

var obj = {};
// 定義一個可寫、可配置、可列舉的資料屬性
Object.defineProperty(obj, 'a', {
    value: 1,
    writable: true,
    enumerable: true,
    configurable: true
});

Object.getOwnPropertyDescriptor(obj, 'a');
// {value: 1, writable: true, enumerable: true, configurable: true}

obj.a; // 1

// 轉換為存取屬性
Object.defineProperty(obj, 'a', {
    get: function () { 
        console.log('get a');
        return 2; 
    }
});

Object.getOwnPropertyDescriptor(obj, 'a');
// {get: ƒ, set: undefined, enumerable: true, configurable: true}

obj.a; // 'get a', 2

// 再轉回資料屬性
Object.defineProperty(obj, 'a', {
    value: 3
});

obj.a; // 3

如果屬性是可配置的,則可以從資料屬性轉為存取屬性,也可以從存取屬性轉為資料屬性。