
JavaScript 語言核心(7)ECMAScrpt 5 物件與特性操作
JavaScript 語言核心(6)鍵值聚合體的物件 << 前情 在 JavaScript 中,物件基本上是鍵值的聚合體,你幾乎可以自由地修改物件,然而,如果你有個物件不想要被自由修改的話,則必須透過各種設計來限制相關特性。 ECMAScript 5 中對物件的特性(Properties)擴充或修改等提供了新的 API,特性本身也有了更豐富的描述,你仍然擁有修改物件的自由度,然而,在不需要這種自由度時,你也可以在嚴格模式之下加以限制。 限定物件的擴充在 ECMAScript 5 中,提供了 'use strict' var obj1 = {}; console.log(Object.isExtensible(obj1)); // true obj1.name = 'caterpillar'; var obj2 = Object.preventExtensions(obj1); console.log(obj1 === obj2); // true console.log(Object.isExtensible(obj1)); // false obj1.age = 39; // TypeError 被標示為無法擴充的物件,只是無法再增添特性,不過仍然可以用 'use strict' var obj = {name : 'caterpillar'}; Object.preventExtensions(obj); obj.name = 'Justin'; console.log(obj.name); // Justin delete obj.name; console.log(obj.name); // undefined Object.prototype.name = 'caterpillar'; console.log(obj.name); // caterpillar 在 ECMAScript 5 中,物件一但被 特性描述器想要進一步限定物件的特性可否修改、刪除等,必須透過 ECMAScript 5 新增的其他 API,不過在這之前,你必須認識 ECMAScript 5 中定義的特性描述器(Property descriptor)。 不同於過去物件上的特性,單純只是一對名稱與值,ECMAScript 5 中物件上每個特性,都會有
這幾個屬性合在一起,又稱為資料描述器(Data descriptor),為特性描述器的一部份,可以使用 如果你直接於物件上新增特性,那麼 'use strict' var obj = {name : 'caterpillar'}; console.log(JSON.stringify( Object.getOwnPropertyDescriptor(obj, 'name') )); 上面這個範例執行之後,會顯示 {“value":"caterpillar","writable":true,"enumerable":true,"configurable":true}。 Object.defineProperty、Object.defineProperties你可以使用 'use strict' var obj = {}; Object.defineProperty(obj, 'name', { value : 'caterpillar', writable : false, enumerable : false, configurable : false }); console.log(JSON.stringify( Object.getOwnPropertyDescriptor(obj, 'name') )); 執行以上範例,會顯示 {“value":"caterpillar","writable":false,"enumerable":false,"configurable":false},事實上,如果你使用 'use strict' var obj = {}; Object.defineProperty(obj, 'name', { value : 'caterpillar' }); console.log(JSON.stringify( Object.getOwnPropertyDescriptor(obj, 'name') )); 然而,以下會顯示 {“value":"caterpillar","writable":true,"enumerable":false,"configurable":true},除了 'use strict' var obj = {name : 'caterpillar'}; Object.defineProperty(obj, 'name', { enumerable: false }); console.log(JSON.stringify( Object.getOwnPropertyDescriptor(obj, 'name') )); 如果有多個特性要設定,可以使用 'use strict' var obj = {}; Object.defineProperties(obj, { 'name': { value : 'John', enumerable : true }, 'age': { value : 39, writable : true, enumerable : true }, }); 如果特性的 實際上,你還可以使用 'use strict' var obj = {}; Object.defineProperty(obj, 'name', { get : function(){ return this.__name__; }, set : function(value){ this.__name__ = value.trim(); }, enumerable : true }); Object.defineProperty(obj, '__name__', { writable : true, enumerable : false }); obj.name = ' Justin '; console.log('*' + obj.name + '*'); // *Justin* for(var p in obj) { console.log(p); // 只會顯示 name } 注意,如果你定義了 如果物件被 seal 與 freeze基於 Object.seal = function(obj) { Object.getOwnPropertyNames(obj) .forEach(function(prop) { var desc = Object.getOwnPropertyDescriptor(obj, prop); desc.configurable = false; Object.defineProperty(obj, prop, desc); }); return Object.preventExtensions(obj); }; 被彌封的物件,仍然可以修改現有的特性值,如果連特性值都不能被修改,只想作為一個唯讀物件,那麼可以使用 Object.freeze = function(obj) { Object.getOwnPropertyNames(obj) .forEach(function(prop) { var desc = Object.getOwnPropertyDescriptor(obj, prop); if('value' in desc) { // 排除設定了 get 與 set 的情況 desc.writable = false; } desc.configurable = false; Object.defineProperty(obj, prop, desc); }); return Object.preventExtensions(obj); }; |