官术网_书友最值得收藏!

5.3 屬性的描述

屬性的描述也可以稱為屬性的特性,類似于對象的內部屬性,其主要作用就是描述屬性自己的一些特征。它的表示方法和對象的內部屬性一樣,也使用兩個方括號表示。對象的命名數據屬性和命名訪問器屬性各有4個特性(沒有內部屬性),其中兩個特性是命名數據屬性和命名訪問器屬性所共有的。下面我們來分別學習。

5.3.1 命名數據屬性的4個特性

命名數據屬性的4個特性分別為:[[Value]]、[[Writable]]、[[Enumerable]]和[[Configurable]]。[[Value]]表示屬性的值;[[Writable]]表示屬性值是否可以修改;[[Enumerable]]表示屬性是否可枚舉,如果為false則不會被for-in循環遍歷到;[[Configurable]]表示屬性是否可以被刪除和屬性的特性(除[[Value]]外)是否可修改。

屬性的特性可以使用Object的getOwnPropertyDescriptor方法查詢。如果想修改,那么可以使用Object的defineProperty和defineProperties方法,這兩個方法所操作的屬性如果存在就會對其進行修改,否則就會創建。

我們來看下面這個例子。

    function log(msg){
        console.log(msg);
    }


    var person = {name:"peter"};
    log(Object.getOwnPropertyDescriptor(person, "name"));
    //Object { configurable=true,  enumerable=true,  value="peter",  writable=true}


    Object.defineProperty(person, "name", {writable:false});
    //將person的name屬性設置為不可修改
    person.name = "maker";  //修改無效
    log(person.name);       //peter


    Object.defineProperty(person, "age", {    //添加age屬性
        value:18,
        configurable:true
    });
    log(Object.getOwnPropertyDescriptor(person, "age"));
    //Object { configurable=true,  enumerable=false,  value=18,  writable=false}
    log(Object.getOwnPropertyNames(person)); //["name", "age"]
    for(prop in person){
    //name:peter,因為age的enumerable為false,所以這里不會打印出age
        log(prop+":"+person[prop]);
    }


    Object.defineProperty(person, "age", {writable:true});
    //將person的age屬性改為可修改
    person.age = 21;
    log(person.age);    //21

這個例子中,我們定義了person對象,然后使用花括號定義了name屬性,并使用defineProperty方法定義了age屬性。使用Object.getOwnPropertyDescriptor可以看出,使用花括號定義的屬性默認[[Writable]]、[[Enumerable]]和[[Configurable]]都為true,而使用Object的defineProperty方法定義的屬性,如果沒有明確聲明,那么[[Writable]]、[[Enumerable]]和[[Configurable]]都默認為false。[[Writable]]為false時不能修改屬性的值;[[Enumerable]]為false時,for-in循環遍歷不到此屬性,但是,使用Object. getOwnPropertyNames方法仍然可以獲取;[[Configurable]]屬性為false時不能使用defineProperty方法修改屬性的特性。

當[[Writable]]為false而[[Configurable]]為true時,我們還可以使用defineProperty方法修改屬性的值,但是[[Configurable]]為false的時候就不可以修改了,例如下面的例子。

    var obj = {};
    Object.defineProperty(obj, "name", {value:"喬峰", configurable:true});


    obj.name = "蕭峰";
    console.log(obj.name);                                   //喬峰


    Object.defineProperty(obj, "name", {value:"蕭峰"});
    console.log(obj.name);                                   //蕭峰


    Object.defineProperty(obj, "name", {configurable:false});
    Object.defineProperty(obj, "name", {value:"喬峰"});      //拋出異常

這個例子中,使用defineProperty方法添加的name屬性因為默認[[Writable]]為false,所以不能直接修改它的值,但是因為[[Configurable]]為true,所以可以使用defineProperty方法通過[[Value]] 特性來修改。當我們使用defineProperty方法將[[Configurable]]設置為false的時候,如果再使用defineProperty方法就會拋出異常。另外,當[[Configurable]]為false的時候,屬性也不可以使用delete刪除。

可以使用propertyIsEnumerable方法檢查[[enumerable]]特性。因為這個方法是Object. prototype中的一個,所以一般對象都可以直接調用(create創建的prototype為null的對象除外),例如下面的例子。

    var圣人 = {姓名:"孔子"};
    圣人.代表作 = "論語";


    Object.defineProperty(圣人,"年齡", {value:888, enumerable:true});
    Object.defineProperty(圣人,"國籍", {value:"中國", enumerable:false});
    Object.defineProperty(圣人,"語言", {value:"漢語"});


    console.log(圣人.propertyIsEnumerable("姓名"));        //true
    console.log(圣人.propertyIsEnumerable("代表作"));      //true
    console.log(圣人.propertyIsEnumerable("年齡"));        //true
    console.log(圣人.propertyIsEnumerable("國籍"));        //false
    console.log(圣人.propertyIsEnumerable("語言"));        //false

從這個例子可以看出,使用花括號和點操作符創建的屬性的[[enumerable]]特性默認為true,使用defineProperty方法創建的屬性,如果沒有明確聲明,那么[[enumerable]]默認為false。其他兩個屬性[[Configurable]]和[[Writable]]的默認值也是這樣的。

另外,這個例子中的對象名和屬性名都使用了中文,對于現在的瀏覽器來說一般都是支持的,而且現在很多C++、Java編譯器也支持中文變量名。但是,因為JS是直接將源代碼發送到客戶端的瀏覽器中運行的,而我們并不能保證所有客戶端的瀏覽器都可以支持中文變量名,所以在JS中最好還是使用英文的變量名。如果是C++或者Java等編譯型的語言就無所謂了,因為它們是將編譯后的結果發給用戶使用的。

5.3.2 命名訪問器屬性的4個特性

命名訪問器屬性因為沒有值,所以沒有[[Value]]特性,同時也就沒有[[Writable]]特性,但它比命名數據屬性多了[[Get]]和[[Set]]特性,它們分別代表訪問器屬性的getter和setter方法。因此,命名訪問器屬性也有4個特性:[[Get]]、[[Set]]、[[Enumerable]]和[[Configurable]]。其中,后兩個特性和命名數據屬性的含義是相同的,本節主要介紹它的前兩個特性。下面看個例子。

    function log(msg){
        console.log(msg);
    }


    var person = {_name:"peter"};
    Object.defineProperty(person, "name", {
        get: function () {
            log("getting name");
            return this._name;
        },
        set: function (newName) {
            log("name is changed to " + newName);
            this._name = newName;
        }
    });
    log(Object.getOwnPropertyDescriptor(person, "name"));
    //Object { configurable=false,  enumerable=false,  get=function(),  set=function() }
    person.name = "lucy";   //name is changed to lucy
    log(person.name);       //getting name, lucy

在這個例子中,使用Object的defineProperty方法給person對象添加了name訪問器屬性,其值保存在_name命名數據屬性中,當我們獲取name的值或者給name設置新值的時候就會調用相應的getter、setter方法。我們可以使用Object的getOwnPropertyDescriptor方法來獲取name屬性的所有特性。

另外,我們也可以在function中使用Object的defineProperty方法給其創建的對象實例添加屬性,這時只要將對象寫為this即可,而且這種方式還可以使用function的內部變量。例如,我們將上個例子中的person對象改為由function類型的Person來創建。

    function log(msg){
        console.log(msg);
    }


    function Person(){
        var name="peter";
        Object.defineProperty(this, "name", {
            get: function () {
                log("getting name");
                return name;
            },
            set: function (newName) {
                log("name is changed to " + newName);
                name = newName;
            }
        });
    }
    var person = new Person();
    log(Object.getOwnPropertyDescriptor(person, "name"));
    //Object { configurable=false,  enumerable=false,  get=function(),  set=function() }
    person.name = "lucy";            //name is changed to lucy
    log(person.name);                //getting name, lucy

這個例子就在function中使用defineProperty方法創建了名為name的訪問器屬性,并在其中定義了getter和setter,即[[get]]和[[set]]特性。在這個例子中,我們將它的值保存到Person的局部變量name中,這樣就可以屏蔽通過實例對象直接調用訪問器屬性的值。

主站蜘蛛池模板: 来凤县| 江永县| 泰安市| 碌曲县| 于都县| 西吉县| 滦南县| 桃江县| 嘉黎县| 乌兰浩特市| 崇州市| 丰镇市| 淅川县| 嘉禾县| 通许县| 都江堰市| 舒兰市| 柳河县| 南通市| 原平市| 台湾省| 温州市| 霸州市| 中阳县| 日喀则市| 长武县| 楚雄市| 上蔡县| 天津市| 郴州市| 邢台县| 北海市| 杭州市| 托克逊县| 宜宾市| 馆陶县| 渭南市| 昌平区| 广水市| 环江| 玉环县|