1. 程式人生 > >說說對 JavaScript 物件的理解

說說對 JavaScript 物件的理解

建立自定義物件的方式有兩種:
* 建立一個 Object 例項,然後再為它新增屬性和方法:

//建立 Object 的例項
var person = new Object();
person.name = "deniro";
person.age = 29;
person.job = "Software Engineer";

person.sayName = function () {
    console.log(this.name);
};
  • 物件字面量方式(已成為首選模式):
//物件字面量語法
var person = {
    name: "deniro",
    age: 29
, job: "Software Engineer", sayName: function () { console.log(this.name); } }

ECMA-262 第 5 版,定義了只有內部才能使用到的屬性,這些屬性是用於 JavaScript 引擎的實現上,因此 JavaScirpt 不能直接訪問到它們,它們一般用兩對方括號表示,比如[[Enumerable]]。

1 資料屬性

資料屬性包含一個數值的位置,在這個位置上可以讀取和寫入值。資料屬性有四個描述其行為的特性:

特性 說明 預設值
[[Configurable]] 能否通過 delete 刪除屬性 true
[[Enumerable]] 能否通過 for-in 迴圈返回屬性 true
[[Writable]] 能否修改屬性的值 true
[[Value]] 包含這個屬性的資料值 undefined
var person = {
    name: "deniro"
}

上面這個例子中的 name 屬性的 [[Value]] 被設定為 deniro,其他值設定為 true。

要修改屬性的預設特性,必須使用 ECMAScript 5 的 Object.defineProperty() 方法。它接受三個引數:屬性所在的物件,屬性名以及描述符物件。其中的描述符物件必須是 configurable、enumerable、writable 和 value:

var person = {};
Object.defineProperty(person, "name", {
    writable: false,
    value: "deniro"
});

console.log(person.name);//deniro
person.name = "Lily";
console.log(person.name);//deniro

一個屬性被設定為不可修改後,如果嘗試為它設定新值,在非嚴格模式下,會被忽略;在嚴格模式下,會丟擲錯誤。

把屬性設定為不可配置:

var person = {};
Object.defineProperty(person, "name", {
    configurable: false,
    value: "deniro"
});
console.log(person.name);//deniro
delete person.name;
console.log(person.name);//deniro

configurable 設定為 false,表示不能從物件中刪除屬性。如果這時對這個屬性呼叫 delete,在非嚴格模式下,會被忽略;在嚴格模式下,會丟擲錯誤。

一旦把屬性定義為不可配置(configurable 設定為 false),就不能再把它定義為可配置的了:

var person = {};
Object.defineProperty(person, "name", {
    configurable: false,
    value: "deniro"
});

//拋錯
Object.defineProperty(person, "name", {
    configurable: true,
    value: "deniro"
});

在呼叫 Object.defineProperty() 方法時,如果不指定,configurable、enumerable、writable 會預設為 false。多數情況下,沒有必要使用 Object.defineProperty() 方法,但理解這些概念有助於對於理解 JavaScript 物件。

注意: IE8 是第一個實現 Object.defineProperty() 的瀏覽器。然而這個版本的實現存在很多限制。因為實現的不徹底,所以建議大家不要在 IE8 中使用 Object.defineProperty()。

2 訪問器屬性

訪問器是一對 getter 和 setter 函式(非必須)。使用 getter 讀取訪問器屬性,使用 setter 寫入訪問器屬性。它有 4 個特性:

特性 說明 預設值
[[Configurable]] 能否通過 delete 刪除屬性 true
[[Enumerable]] 能否通過 for-in 迴圈返回屬性 true
[[Get]] 在讀取屬性時呼叫的函式 undefined
[[Set]] 在寫入屬性時呼叫的函式 undefined

訪問器屬性必須使用 Object.defineProperty() 來定義:

var book = {
    _year: 2016,
    edition: 1
};

Object.defineProperty(book, "year", {
    get: function () {
        return this._year;
    },
    set: function (newValue) {
        if (newValue > 2004) {
            this._year = newValue;
            this.edition += newValue - 2016;
        }
    }
});

book.year = 2017;
console.log(book.edition);//2

_year 前面的下劃線表示只能通過物件方法訪問的屬性。

使用訪問器的常見方式是,設定一個屬性的值會導致其他屬性值發生變化。

只指定 getter 表示屬性為只讀。在嚴格模式下,嘗試寫入會拋錯。只指定 setter 表示屬性為只寫。在非嚴格模式下,返回 undefined;在嚴格模式下,嘗試讀取會拋錯。

在不支援 ECMAScript 5 的瀏覽器中,一般有兩個非標準方法:__defineGetter__()__defineSetter__(),這兩個方法會是由 Firefox 定義的,後來 Safari 3、Chrome 1、Opera 9.5 也給出了相同的實現。

下面用遺留的方法重寫了之前的例子:

var book = {
    _year: 2016,
    edition: 1
};

book.__defineGetter__("year", function () {
    return this._year;
});

book.__defineSetter__("year", function (newValue) {
    if (newValue > 2004) {
        this._year = newValue;
        this.edition += newValue - 2016;
    }
});


book.year = 2017;
console.log(book.edition);//2

注意:在不支援 Object.defineProperty() 方法的瀏覽器中,不能修改 [[Configurable]] 和 [[Enumerable]]。

3 定義多個屬性

ECMAScript 5 定義的 Object.defineProperties(),可以一次定義多個屬性。它接受兩個引數(要新增和修改其屬性的物件、定義的屬性):

var book = {};

Object.defineProperties(book, {
    _year: {
        value: 2016
    },
    edition: {
        value: 1
    },
    year: {
        get: function () {
            return this._year;
        },

        set: function () {
            if (newValue > 2016) {
                this._year = newValue;
                this.edition += newValue - 2016;
            }
        }
    }
});

4 讀取屬性的特性

ECMAScript 5 定義的 Object.getOwnPropertyDescriptor() ,會讀取屬性的特性。它接受兩個引數(屬性所在的物件、要讀取描述符的屬性名稱),返回值是物件,可能是訪問器屬性,也可能是資料屬性:

var book = {};

Object.defineProperties(book, {
    _year: {
        value: 2017
    },

    edition: {
        value: 1
    },

    year: {
        get: function () {
            return this._year;
        },
        set: function (newValue) {
            if (newValue > 2017) {
                this._year = newValue;
                this.edition += newValue - 2017;
            }
        }
    }
});

var descriptor = Object.getOwnPropertyDescriptor(book, "_year");
console.log(descriptor.value);//2017
console.log(descriptor.configurable);//false
console.log(typeof descriptor.get);//undefined

var descriptor = Object.getOwnPropertyDescriptor(book, "year");
console.log(descriptor.value);//undefined
console.log(descriptor.configurable);//false
console.log(typeof descriptor.get);//function

在支援 ECMAScript 5 版本的 JavaScript 中,可以針對任何物件(包括 DOM 和 BOM),使用 Object.getOwnPropertyDescriptor 方法。