天天看點

js中屬性類型:資料屬性與通路器屬性

js中屬性類型分為兩種:資料屬性和通路器屬性

在js中,對象都是由名值對構成的,名:就是我們所說的屬性名,值就是屬性對應的值(基本值、對象、方法)。

ECMA-262第5版定義了隻有内部才用的特性,描述了屬性的各種特征,比如,這個屬性能否被删除、能否被枚舉、能否被修改、以及讀取屬性的值。

這些特性是為了實作JavaScript引擎用的,是以在JavaScript中不能直接通路他們。

1、先來說一下資料屬性

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

Configurable:表示能否通過delete删除屬性,進而重新定義屬性,能否修改屬性的特性,或則能否把屬性修改為通路器屬性。我們一般在對象上自定義的屬性,它們的這個特性一般為true。

Enumberable:表示能否通過for-in循環傳回屬性(枚舉),自定義的對象屬性,預設值為true。

Writable:表示能否修改屬性的值,自定義對象的屬性,這個特性值預設為true。

Value:包含這個屬性的資料值,讀取屬性值的時候從這個讀,寫入屬性值的時候,把新值儲存在這個位置。這個特性的預設值為undefined。

ECMAScript5定義了一個修改屬性的這些預設特性的方法:Object.defineProperty();

這個方法接收三個參數:屬性所在的對象、屬性的名字(字元串形式)、一個描述符對象。其中描述符對象的屬性必須是:configurable、enumberable、writable、value,設定其中的一個或多個值,可以修改對應的特性值。例如:

var person={};
    Object.defineProperty(person,"name",{
        writable:false,
        value:"Nicholas"
    });
    console.log(Object.getOwnPropertyDescriptor(person,"name"));//讀取屬性的特性的方法,後面會介紹
    console.log(person.name);//Nicholas
    person.name="Greg";
    console.log(person.name);//Nicholas      

上面,對name屬性的兩個特性writable、value進行了設定,可見,我們更改name屬性的值是無效的,在嚴格模式下會報錯;

當用Object.defineProperty()建立一個新屬性時,若不指定configurable、enumberable、writable這些特性時,都是false,若果在原來設定過的基礎上設定其中的一個或多個,則無此限制。

另外,當設定configurable為false後,也就是把該屬性變成不可設定的,除delete不能删除該屬性外,再次用Object.defineProperty()方法設定除writable特性外的其它特性時,在非嚴格模式時什麼都不會發生,在嚴格模式時會報錯。(同時configurable特性也設定不了true了!!!);

2、通路器屬性:

通路器屬性不包含資料值,他們包含一對兒getter和setter函數(不過,這兩個函數都不是必須的)。在讀取通路器屬性時,會調用getter函數,這個函數負責傳回有效的值;在寫入通路器屬性時,會調用setter函數并傳入新值,這個函數負責決定如何處理資料。通路器屬性有如下4個特性。

configurable:表示能否通過delete删除屬性進而重新定義屬性,能否修改屬性的特性,或則能否把屬性修改為資料屬性。對于直接在對象上定義的屬性,這個特性的預設值為true。

enumberable:表示能否通過for-in來循環傳回屬性。對于直接在對象上定義的屬性,這個特性的預設值為true。

get:讀取屬性時調用的函數。預設值為undefined

set:在寫入屬性時調用的函數。預設值為undefined

通路器屬性不能直接定義,必須使用Object.defineProperty()來定義。請看下面的例子:

var book={
        _year:2004,
        edition:1
    };
    //下邊給book對象設定一個year(通路器屬性)
    Object.defineProperty(book,"year",{
        get:function(){
            return this._year;
        },
        set:function(newValue){
            if(newValue>2004){
                this._year=newValue;
                this.edition=newValue - 2004;
            }
        }
    });
    book.year =2005;
    console.log(book.edition);//1
    console.log(book._year);//2005
    console.log(book.year);//2005
    console.log(book);//{_year: 2005, edition: 1}      

可見,建立的通路器屬性,用console是列印不出來的,我們常見的使用通路器屬性的方式就是上述這種方式,修改通路器屬性,會導緻其他屬性變化!!!

var book={};
    Object.defineProperties(book,{
        _year:{//定義一個資料屬性,其它為指定的特性值為false
            writable:true,
            value:2004
        },
        edition:{//同樣是一個資料屬性
            writable:true,
            value:1
        },
        year:{
            get:function(){
                return this._year;
            },
            set:function(newValue){
                if(newValue>2004){
                    this._year=newValue;
                    this.edition+=newValue-2004;
                }
            }
        }
    });
    book.year=2003;
    console.log(book);//{_year: 2004, edition: 1}
    book.year=2005;
    console.log(book);//{_year: 2005, edition: 1}
    // Object.getOwnPropertyDescriptor();用來讀取給定屬性的描述符,這個方法有兩個參數,屬性所在的對象和要讀取其描述符的屬性名,傳回一個對象。
    var descriptor=Object.getOwnPropertyDescriptor(book,"_year");//資料屬性
    var descriptor2=Object.getOwnPropertyDescriptor(book,"year");//通路器屬性
    console.log(descriptor);//{value: 2005, writable: true, enumerable: false, configurable: false}
    console.log(descriptor2);//{get: ƒ, set: ƒ, enumerable: false, configurable: false}      
Object.getOwnPropertyDescriptor();用來讀取給定屬性的描述符,這個方法有兩個參數,屬性所在的對象和要讀取其描述符的屬性名,傳回一個對象。這個方法可以在任何浏覽器使用。