- 看透JavaScript:原理、方法與實踐
- 韓路彪
- 1810字
- 2020-11-28 15:50:46
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中,這樣就可以屏蔽通過實例對象直接調用訪問器屬性的值。
- 從零開始:數字圖像處理的編程基礎與應用
- INSTANT Sencha Touch
- MATLAB實用教程
- Python漫游數學王國:高等數學、線性代數、數理統計及運籌學
- Android Native Development Kit Cookbook
- The DevOps 2.5 Toolkit
- ElasticSearch Cookbook(Second Edition)
- Python項目實戰從入門到精通
- RubyMotion iOS Develoment Essentials
- 代碼閱讀
- 自學Python:編程基礎、科學計算及數據分析(第2版)
- 大學計算機基礎實驗指導
- 高性能PHP 7
- R語言數據分析從入門到實戰
- jBPM6 Developer Guide