0%

JS核心-(52)-物件屬性延伸章節:屬性的特徵-屬性特徵是什麼?

前言

這篇主要會解析物件屬性的特徵,解釋原生的原形以及我們自己定義的原形有什麼不一樣的地方,

以及框架是如何利用物件屬性的特徵來開發出特別的功能!

Object.defineProperty

我們先看範例:

var person = {
    a: 1,
    b: 2,
    c: 3
};

console.log(person);

印出來之後可以得到下面的結果

可以看到 person 這個物件裡面的屬性 abc 以及對應的值,還有物件的原形連結到哪裡。

我們都知道,修改物件裡面屬性的值可以是 person.a = 4 或是 person['a'] = 4,這兩種方法都可以。

今天就要教大家另外一種進階的方法,叫做 Object.defineProperty

這個方法除了可以調整屬性的值 value 之外,還可以調整屬性的特徵

而上述的兩種方法都只能調整屬性的其中一個特徵,也就是屬性對應的值 value

屬性到底有那些特徵?

  • value → 對應的值是甚麼
  • 可否寫入 writable → 可否進行屬性質的修改
  • 可否被刪除 configurable → 可否可以用 delete 刪除屬性
  • 可否被列舉 enumerable → 可否利用 for...in 迴圈 將物件屬性列舉出來,可以限制特定的屬性是否被列舉出來

Object.defineProperty 實際運用

先看一下 Object.defineProperty 語法的用法:

Object.defineProperty( 物件的變數,或是物件本人, 要修改的屬性名稱(字串), {

    value: 要修改或是賦予甚麼值,
    writable: 布林值,
    configurable: 布林值,
    enumerable: 布林值
});

Object.defineProperty( person, 'a', {
    value: 4,
    writable: true,
    configurable: true,
    enumerable: true
});

不一定每次每個屬性特徵都要列出來,也可以只修改一種:

Object.defineProperty(person, 'a', {
    configurable: false,
});

那我們實際來操作看看:

var person = {
    a: 1,
    b: 2,
    c: 3
};

console.log(person);

Object.defineProperty(person, 'a', {
    value: 4,
    writable: true,
    configurable: true,
    enumerable: true
});

console.log(person);

我們會發現,一開始 a 的值是 1 ,但是展開後 a 的值是 4,所以他會顯示已經變化過後的值。

我們再來修改其他的屬性看看 ~

var person = {
    a: 1,
    b: 2,
    c: 3
};

console.log(person);

Object.defineProperty(person, 'a', {
    value: 4,
    writable: false,
    configurable: true,
    enumerable: true
});

person.a = 5;

console.log(person);

我們把 writable 改成 false,代表我們沒有辦法再重新修改 a 屬性的 value

所以我們利用 Object.definePropertya 的屬性改成 4 之後,並且把 writable 改成 false ,後面的 person.a = 5;

就沒辦法再進行修改的動作了。

但其實這樣的狀況會是一個靜默的錯誤!

也就是在非嚴格模式下(Sloppy mode),不會顯示錯誤紅字,但是如果切換到嚴格模式的話,就會有錯誤訊息產生。

var person = {
    a: 1,
    b: 2,
    c: 3
};

console.log(person);

Object.defineProperty(person, 'a', {
    writable: false,
    configurable: true,
    value: 4,
    enumerable: true
});

person.a = 5;

(function () {
    'use strict';
    person.a = 5;
})();

console.log(person);

同時也會導致最後面的 console.log 不會執行,所以在嚴格模式的時候要特別注意。


接著我們針對 b 的屬性來試試看不同的屬性操作

var person = {
    a: 1,
    b: 2,
    c: 3
};

console.log(person);

Object.defineProperty(person, 'a', {
    value: 4,
    writable: false,
    configurable: true,
    enumerable: true
});

person.a = 5;

// (function () {
//     'use strict';
//     person.a = 5;
// })();

Object.defineProperty(person, 'b', {
    configurable: false,
});

delete person.a;
delete person.b;

console.log(person);

在這邊我設定 bconfigurable 屬性為 false 後, b 的屬性就無法被 delete 語法給刪除,但 a 可以成功被刪除

最後介紹一個 enumerable 的屬性套用在屬性 c 身上,並且我們先不刪除 a 屬性,透過 for in 的迴圈顯示物件 person

屬性名稱:

var person = {
    a: 1,
    b: 2,
    c: 3
};

console.log(person);

Object.defineProperty(person, 'a', {
    writable: false,
    configurable: true,
    value: 4,
    enumerable: true
});

person.a = 5;

// (function () {
//     'use strict';
//     person.a = 5;
// })();

Object.defineProperty(person, 'b', {
    configurable: false,
});

// delete person.a;
delete person.b;

// Object.defineProperty(person, 'c', {
//     enumerable: false,
// });

for (var key in person) {
    console.log('列舉: ' + key);
}

console.log(person);

最後我們再把註解拿掉

var person = {
    a: 1,
    b: 2,
    c: 3
};

console.log(person);

Object.defineProperty(person, 'a', {
    writable: false,
    configurable: true,
    value: 4,
    enumerable: true
});

person.a = 5;

// (function () {
//     'use strict';
//     person.a = 5;
// })();

Object.defineProperty(person, 'b', {
    configurable: false,
});

delete person.a;
delete person.b;

Object.defineProperty(person, 'c', {
    enumerable: false,
});

for (var key in person) {
    console.log('列舉: ' + key);
}

console.log(person);

可以看到, a 因為被刪掉了無法被列舉,b 可以被列舉但不能寫入,c 還在但不能被列舉,

所以被列舉出來的只有 b 屬性。


Object.defineProperty 除了修改以外,也可以做到新增

var person = {
    a: 1,
    b: 2,
    c: 3
};

console.log(person);

Object.defineProperty(person, 'a', {
    writable: false,
    configurable: true,
    value: 4,
    enumerable: true
});

person.a = 5;

// (function () {
//     'use strict';
//     person.a = 5;
// })();

Object.defineProperty(person, 'b', {
    configurable: false,
});

delete person.a;
delete person.b;

Object.defineProperty(person, 'c', {
    enumerable: false,
});

for (var key in person) {
    console.log('列舉: ' + key);
}

Object.defineProperty(person, 'd', {
    writable: false,
    value: {},
});

person.d = 6;

console.log(person);

在這邊我們新增了屬性 'd'person 這個物件裡面,並且給予不可寫入的特徵,他的值我們給予空物件。

當然 person.d = 6; 就又會是一個靜默錯誤並且不能夠進行修改。但是當我們今天是針對它裡面的值做額外的設定的話,

還是有辦法賦予到內層物件的值喔!

var person = {
    a: 1,
    b: 2,
    c: 3
};

console.log(person);

Object.defineProperty(person, 'a', {
    writable: false,
    configurable: true,
    value: 4,
    enumerable: true
});

person.a = 5;

// (function () {
//     'use strict';
//     person.a = 5;
// })();

Object.defineProperty(person, 'b', {
    configurable: false,
});

delete person.a;
delete person.b;

Object.defineProperty(person, 'c', {
    enumerable: false,
});

for (var key in person) {
    console.log('列舉: ' + key);
}

Object.defineProperty(person, 'd', {
    writable: false,
    value: {},
});

person.d = 6;

person.d.a = 6;

console.log(person);

這也是特別要注意的點,就是以上這4個物件的特徵,都是屬於淺層的控制,深層的控制則需要另外再進行設定喔!


Object.defineProperties

剛剛我們都是一個一個屬性設定,有些人會覺得這樣很麻煩,我設定一個屬性就要寫一長串,這裡就要介紹可以同時設定很多

屬性的方法,那就是 Object.defineProperties

Object.defineProperties(物件變數或物件本人, {
    物件屬性名稱1: {
        特徵1: xx
        特徵2: xx
        特徵3: xx
        特徵4: xx

    },
    物件屬性名稱2: {
        特徵1: xx
        特徵2: xx
        特徵3: xx
        特徵4: xx

    },...
});

Object.defineProperties(person, {
    a: {
        writable: false,
        configurable: true,
        value: 4,
        enumerable: true
    },
    b: {
        configurable: false,
    },
    c: {
        enumerable: false,
    },
    d : {
        writable: false,
        value: {}
    }
});

上方程式碼的就是依照這篇文章剛剛練習的內容一次進行設定的寫法,大家也可以試試看設定不同的屬性。