javascript 面向對象版塊之對象屬性

這是 javascript 面向對象版塊的第二篇文章,主要講解的是對象的屬性,首先建立一個對象:javascript

var person = {
  name: "Nicholas",
  age: 29,
  job: "Software Engineer",
  sayName: function () {
    console.log(this.name);
  }
};

上面的例子建立了一個名爲 person 的對象,併爲它添加了三個屬性( name 、 age 和 job )和一個方法( sayName() )。其中, sayName() 方法用於顯示 this.name (將被解析爲 person.name )的值。
ECMAScript 中有兩種屬性:數據屬性和訪問器屬性。java

數據屬性

數據屬性是可獲取和設置值得屬性,數據屬性將 value 和 writable 屬性包含在其描述符中。數據屬性有4個描述其行爲的特性:chrome

  • [[Configurable]]:表示可否經過 delete 刪除屬性從而從新定義屬性,可否修改屬性的特性,或者可否把屬性修改成訪問器屬性。像前面例子中那樣直接在對象上定義的屬性,它們的這個特性默認值爲 true 。
  • [[Enumerable]]:表示可否經過 for-in 循環返回屬性。像前面例子中那樣直接在對象上定義的屬性,它們的這個特性默認值爲 true 。
  • [[Writable]]: 表示可否修改屬性的值。像前面例子中那樣直接在對象上定義的屬性,它們的這個特性默認值爲 true 。
  • [[Value]]: 包含這個屬性的數據值。讀取屬性值的時候,從這個位置讀;寫入屬性值的時候,把新值保存在這個位置。這個特性的默認值爲 undefined 。

對於像前面例子中那樣直接在對象上定義的屬性,它們的 [[Configurable]] 、 [[Enumerable]]和 [[Writable]] 特性都被設置爲 true ,而 [[Value]] 特性被設置爲指定的值。例如:segmentfault

var person = {
  name: "Nicholas",
};

這裏建立了一個名爲 name 的屬性,爲它指定的值是 "Nicholas" 。也就是說, [[Value]] 特性將被設置爲 "Nicholas" ,而對這個值的任何修改都將反映在這個位置。
此時有這樣一個想法,若是我不容許修改 name 屬性的值,怎麼辦?或者說我要修改屬性的默認特性,怎樣才能夠實現呢?要實現這些功能就要用到 Object.defineProperty()方法,這個方法接收三個參數:屬性所在的對象、屬性的名字和一個描述符對象。其中,描述符(descriptor)對象的屬性必須是: configurable 、 enumerable 、 writable 和 value 。設置其中的一或多個值,能夠修改對應的特性值。例如:函數

var person = {};
Object.defineProperty(person, 'name', {
  writable: false,
  value: 'Nicholas'
})
console.log(person.name); // Nicholas
person.name = 'Greg';
console.log(person.name); // Nicholas

這個例子建立了一個 name 屬性,它的值 "Nicholas" 是隻讀的。這個屬性不可修改,若是嘗試修改這個值的話在非嚴格模式下會被忽略,可是若是在嚴格模式下,會拋出錯誤:
Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>'
相似的規則也適用於不可配置的屬性:測試

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

把 configurable 設置爲 false ,表示不能從對象中刪除屬性。若是對這個屬性調用 delete ,則在非嚴格模式下什麼也不會發生,而在嚴格模式下會致使錯誤。並且一旦把屬性定義爲不可配置,就不能再把它變回可配置了。此時,再調用 Object.defineProperty() 方法修改特性,都會致使錯誤:this

var person = {};
Object.defineProperty(person, 'name', {
  configurable: false,
  value: 'Nicholas'
})
console.log(person.name); // Nicholas
/* delete person.name;
console.log(person.name); // Nicholas */
Object.defineProperty(person, 'name', {
  writable: true
})

也就是說 能夠屢次調用 Object.defineProperty() 方法修改同一屬性,可是當 configurable 設置爲 false 後就不能夠了。設計

在《JavaScript 高級程序設計(第三版)》中寫到:
「一旦把屬性定義爲不可配置的,就不能再把它變回可配置了。此時,再調用 Object.defineProperty() 方法修改除 writable 以外的特性,都會致使錯誤。」
可是我試了一些,即便修改的是 writable 屬性,仍是會報錯。若是我描述的有錯,還望各位大佬指出,以便交流。

在調用 Object.defineProperty() 來定義屬性時。若是不指定, configurable 、 enumerable 和writable 特性的默認值都是 false。其實在平常的開發中用到這種高級方法來定義屬性的機會仍是比較少,不過理解這部分對理解對象仍是有很大的好處。code

訪問器屬性

訪問器屬性不包含數據值,但包含一對兒 getter 和 setter 函數(不過,這兩個函數都不是必需的)。在讀取訪問器屬性的時候,會調用 getter 方法,這個函數複製返回有效的值;在寫入訪問器屬性的時候,會調用 setter 函數,這個函數複製若是修改數據。訪問器屬性有以下 4 個特性:對象

  • [[Configurable]] :表示可否經過 delete 刪除屬性從而從新定義屬性,可否修改屬性的特性,或者可否把屬性修改成數據屬性。對於直接在對象上定義的屬性,這個特性的默認值爲true 。
  • [[Enumerable]] :表示可否經過 for-in 循環返回屬性。對於直接在對象上定義的屬性,這個特性的默認值爲 true 。
  • [[Get]] :在讀取屬性時調用的函數。默認值爲 undefined 。
  • [[Set]] :在寫入屬性時調用的函數。默認值爲 undefined 。訪問器屬性不能直接定義,必須使用 Object.defineProperty() 來定義。請看下面的例子。
var book = {
  _year: 2004,
  edition: 1
}

Object.defineProperty(book, 'year', {
  get: function () {
    return this._year;
  },
  set: function (newVal) {
    if (newVal > this._year) {
      this._year = newVal;
      return this.edition += newVal - 2004;
    }
  }
})
book.year = 2005;
console.log(book.edition); // 2

以上代碼代碼建立了一個 book 對象,定義了兩個屬性,_year 和 edition。而訪問器屬性 year 包含了一個 geter 函數和 setter 函數。getter 函數返回 _year 的值,而 setter 函數返正確的版本。當把 year 屬性修改爲 2005 時,而 edition 變爲 2,這是使用訪問器屬性的常見方式,即設置一個屬性的值會致使其餘屬性發生變化。
不必定非要同時指定 getter 和 setter,若是隻指定 getter ,代表該屬性不能寫,只能讀取,嘗試寫入屬性會被忽略,但在嚴格模式下會報錯。若是隻指定 setter ,代表該屬性不能讀取,若是嘗試讀取,在嚴格模式和非嚴格模式下都會返回 undefined。

在《JavaScript 高級程序設計(第三版)》中寫到:
「只指定 setter 函數的屬性也不能讀,不然在非嚴格模式下會返回 undefined ,而在嚴格模式下會拋出錯誤。」
但在 chrome 中測試了一下,在嚴格模式下不會拋出錯誤,返回的也是 undefined 。若是我描述的有錯,還望各位大佬指出,以便交流。

小結

本片博客主要介紹了兩種屬性:數據屬性訪問器屬性,介紹了這兩種屬性的定義以及這兩種屬性的特性,主要使用方法 Object.defineProperty()。其實這篇文章主要是增強對對象的理解。

上一篇:javascript 面向對象版塊之理解對象

下一篇:javascript 面向對象版塊之定義多個對象屬性以及讀取屬性特性

相關文章
相關標籤/搜索