深刻理解JavaScript中的屬性和特性

深刻理解JavaScript中的屬性和特性

  JavaScript中屬性和特性是徹底不一樣的兩個概念,這裏我將根據本身所學,來深刻理解JavaScript中的屬性和特性。函數

  主要內容以下:this

  • 理解JavaScript中對象的本質、對象與類的關係、對象與引用類型的關係
  • 對象屬性如何進行分類
  • 屬性中特性的理解

第一部分:理解JavaScript中對象的本質、對象與類的關係、對象與引用類型的關係

   對象的本質:ECMA-262把對象定義爲:無序屬性的集合,其屬性能夠包含基本值、對象或者函數。即對象是一組沒有特定順序的值,對象的每一個屬性或方法都有一個名字,而這個名字都映射到一個值。故對象的本質是一個散列表:其中是一組名值對,值能夠是數據或函數。code

   對象和類的關係:在JavaScript中,對象和類沒有任何關係。這是由於ECMAScript中根本就沒有類的概念,它的對象與其餘基於類的語言中的對象是不一樣的。對象

   對象和引用類型的關係:對象和引用類型並非等價的,由於每一個對象都是基於一個引用類型建立的。ip

第二部分:對象屬性如何進行分類

  由構造函數或對象字面量方法建立的對象中具備屬性和方法(只要提到屬性和方法,它們必定是屬於對象的;只要提到對象,它必定是具備屬性和方法的(自定義除外)),其中屬性又可分爲數據屬性和訪問器屬性,他們的區別以下:get

  • 數據屬性通常用於存儲數據數值,訪問器屬性不包含數據值
  • 訪問器屬性多用於get/set操做

第三部分:屬性中特性的理解

  ECMAScript爲了描述對象屬性(property)的各類特徵,定義了特性(attribute)這個概念。也就是說特性不一樣於屬性,特性是爲了描述屬性的。下面,我將分別講解:it

  • 數據屬性及其特性
  • 訪問器屬性及其特性
  • 如何利用Object.defineProperties()方法定義多個特性
  • 如何利用Object.getOwnPropertyDescripter()方法讀取屬性的描述符以讀取屬性的特性

1.數據屬性及其特性io

  剛剛咱們說過,數據屬性是用於存儲數據數值的,所以數據屬性具備一個數據值的位置,在這個位置能夠讀取和寫入值。數據屬性有4個描述其行爲的特性,因爲ECMAScript規定:在JavaScript中不能直接訪問屬性的特性(注意:不是不能訪問),因此咱們把它放在兩組方括號中。以下:console

  • [[Configurable]]:默認值爲true,a、表示可否經過delete刪除屬性從而從新定義屬性 b、可否修改屬性的特性
    c、可以把屬性由數據屬性修改成訪問器屬性
  • [[Enumerable]]:默認值爲true,表示可否經過for-in循環返回該屬性(因此:若是爲false,那麼for-in循環無法枚舉它所在的屬性)
  • [[Writable]]:默認值爲true,表示可否修改屬性的值,這是與[[Configurable]]不一樣之處。
  • [[Value]]:默認值爲undefined,這個值即爲屬性的屬性值,咱們能夠在這個位置上讀取屬性值,也能夠在這個位置上寫入屬性值。
  • 注意:上述的默認是指經過構造函數或對象字面量建立的對象所自身擁有的屬性,而不是下面要介紹的Object.defineProperty()方法

這些特性都具備默認值,可是若是這些默認值不是咱們想要的,該怎麼辦呢?固然就是修改啦!咱們能夠經過Object.defineProperty()方法來修改屬性默認的特性。英文difineProperty即爲定義屬性的意思。這個方法接收三個參數:屬性所在的對象、屬性的名字和一個描述符對象。其中第三個參數描述符對象是對象字面量的方法建立的,裏面的屬性和屬性值實際上保存的是要修改的特性和特性值。table

下面經過幾個例子來深刻理解。

a

var person={};
Object.defineProperty(person,"name",{
    writable:false,
    value:"zhuzhenwei"
});
console.log(person.name);//zhuzhenwei
person.name="heting";
console.log(person.name);//zhuzhenwei

  這裏我用對象字面量的方法建立了一個對象,可是沒有同時建立方法和屬性。而是利用了Object.defineProperty()方法來建立了屬性和修改了默認值。這裏將writable設置爲false,因而後面我試圖修改person.name時,是無效的。

b

var person={};
Object.defineProperty(person,"name",{
    value:"zhuzhenwei"
});
console.log(person.name);//zhuzhenwei
person.name="heting";
console.log(person.name);//zhuzhenwei

  注意看這個例子,這個例子中我刪去了writable:false,爲何仍是不能修改呢?這是由於以前我在介紹特性時,前三個默認爲ture,是在建立對象並建立屬性的狀況下獲得的。對於經過調用Object.defineProperty()方法建立的屬性,其前三個特性的默認值均爲false,這裏須要注意。

c

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

  這裏咱們將新建的屬性name的特性設置爲了configurable:false;所以下面刪除屬性的操做是無效的。根據b,可知configurable,默認就是false,即便去掉也不可修改。

d

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

  在這裏我將默認的configurable的值由默認的false修改成了true,因而變成了可配置的,那麼最後就成功刪除了。

e

var person={};
Object.defineProperty(person,"name",{
    value:"zhuzhenwei",
    configurable:false
});
console.log(person.name);//zhuzhenwei
Object.defineProperty(person,"name",{
    value:"zhuzhenwei",
    configurable:true
});
console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…)

  若是以前已經設置成爲了false,那麼後面再改爲true也是徒勞的,即:一旦把屬性設置成爲不可配置的,就不能再把它變回可配置了。

f

console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…)
var person={};
Object.defineProperty(person,"name",{
    value:"zhuzhenwei",
});
console.log(person.name);//zhuzhenwei
Object.defineProperty(person,"name",{
    value:"zhuzhenwei",
    configurable:true
});
console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…)

  這裏能夠說明,即便前一步咱們無論默認的configurable:false,後面獲得的還是不可配置。因而,能夠得出結論,爲了可配置,必須在第一次調用Object.defineProperty()函數時就將默認的值修改成true

 2.訪問器屬性及其特性  

   以前提到,訪問器屬性不包含數據值,他們包含一對getter函數和setter函數(這兩個函數不是必須的)。在讀取訪問器屬性時,會調用getter函數,這個函數負責返回有效的值;在寫入訪問器屬性是,會調用setter函數並傳入新值,這個函數負責決定如何處理數據。一樣,因爲不能經過JavaScript來直接訪問獲得訪問器屬性的特性,因此下面列出的特性將由[[]]括起來以做區分。

  • [[Configurable]]:默認值爲true,a、表示可否經過delete刪除屬性從而從新定義屬性 b、可否修改屬性的特性c、可以把屬性由訪問器屬性修改成數據屬性
  • [[Enumerable]]:默認值爲true,表示可否經過for-in循環返回該屬性(因此:若是爲false,那麼for-in循環無法枚舉它所在的屬性)
  • [[Get]]:在讀取屬性時調用的函數。默認值爲undefined 關鍵:特性能夠是一個函數
  • [[Set]]: 在寫入屬性時調用的函數。默認值爲undefined 關鍵:特性能夠是一個函數
    因爲getset函數也屬於屬性的特性,那麼他們就有可能(說有多是由於這兩個函數也不是必須的)出如今Object.defineproperty的第三個參數描述符對象的屬性中。

注意:1.相對於數據屬性,咱們發現訪問器屬性中沒有writable特性和value特性。這是由於訪問器屬性不包含數據值,那麼咱們怎麼固然就不可修改屬性的值(用不到writable特性),更不用考慮value了。

   2.訪問器屬性不能直接定義,必須是用Object.defineProperty()來定義。(經過這個規定咱們就能準確地判斷出訪問器屬性和數據屬性了)

經過下面這個例子來深刻理解:

var book={
    _year:2004,
    edition:1
};
Object.defineProperty(book,"year",{
    get:function(){<br>            return this._year;
    },
    set:function(newValue){
        if(newValue>2004){
            this._year=newValue;
            this.edition+=newValue-2004;
        }
    }
});
book.year=2005;
console.log(book.edition);//2

幾個須要深刻理解的地方:

1.訪問器屬性不能直接定義,必須使用Object.defineProperty()來定義,且該屬性具備setget特性,因而能夠判斷,_yearedition是數據屬性,而year是訪問器屬性。

2.咱們看到_year這個數據屬性前面是以_(下劃線)開頭的,這個一種經常使用的記號,用於表示只能經過對象方法訪問的屬性。從上面的例子中能夠看到get至關於描述符對象的一個方法,而_year正是在這個對象方法訪問的屬性。而edition既能夠經過對象方法訪問,也能夠由對象直接訪問。

  1. book.year表示正在讀取訪問器屬性,這時會調用get函數,並返回了2004這個有效的值。
  2. book.year=2005表示寫入訪問器屬性,這時會調用set函數並傳入新值,即將2005傳給newValue,這個函數決定如何處理數據。
  3. 這時使用訪問器屬性的常見方法-即設置一個屬性的值會致使其餘屬性發生變化。

3.如何利用Object.defineProperties()方法定義多個特性

 顯然,一個對象不可能只具備一個屬性,所以,定義多個屬性的可能性很大,因而JavaScript提供了Object.defineProperties()方法解決這個問題。這個方法接收兩個參數,第一個是要定義屬性所在的對象,第二個是一個對象字面量方法建立的對象,對象的屬性名即爲要定義的特姓名,對象的屬性值又是一個對象,這個對象裏的屬性名和屬性值分別是特性名和特性值(這裏不是很好理解,看例子便可)。

var book={};
Object.defineProperties(book,{
    _year:{
        writable:true,
        value:2004
    },
    edition:{
        writable:true,
        value:1
    },
    year:{
        get:function(){
            return this._year;
        },
        set:function(){
            if(newValue>2004){
                this._year=newValue;
                this.edition+=newValue-2004;
            }
        }
    }
});

4.如何利用Object.getOwnPropertyDescripter()方法讀取屬性的描述符以讀取屬性的特性

  咱們可使用Object.getOwnPropertyDescripter()方法來取得給定屬性的描述符。getOwnPropertyDescripter即爲取得自身屬性描述符的意思。這個方法接收兩個參數:屬性所在的對象要要讀取其描述符的屬性名稱。返回一個對象。

  對於訪問器屬性而言,這個對象的屬性有configurableenumerablegetset

  對於數據屬性而言,這個對象的屬性有configurableenumerablewritablevalue

var book={};
Object.defineProperties(book,{
    _year:{
        value:2004
    },
    edition:{
        value:1
    },
    year:{
        get:function(){
            return this._year;
        },
        set:function(){
            if(newValue>2004){
                this._year=newValue;
                this.edition+=newValue-2004;
            }
        }
    }
});
var descriptor=Object.getOwnPropertyDescriptor(book,"_year");
console.log(descriptor.value);//2004
console.log(descriptor.configurable);//false  由於經過Object.defineProperties()方法建立的屬性的特性configurable enumerable都是false
console.log(typeof descriptor.get);//undefined 注意:這是數據屬性,是不具備get特性的
 
var descriptor=Object.getOwnPropertyDescriptor(book,"year");
console.log(descriptor.value);//undefined
console.log(descriptor.enumerable);//false
console.log(typeof descriptor.get);//function get雖然是屬性的一個特性,可是它也是函數。
相關文章
相關標籤/搜索