###### tags: `JaveScript` `核心篇` {%hackmd BJrTq20hE %} # 屬性的特徵與Getter/Setter ## 1-1屬性特徵是什麼? 有四點 1.value,也就是物件屬性的值。 2.writable,屬性的值能不能被寫入。 3.configurable,屬性能不能被刪除。 4.enumerable,屬性能不能被列舉。 屬性的特徵預設都是ture,value沒有特別設定的話就是最後所賦值。 ## 1-2如何更改屬性的特徵? 使用Object.defineProperty() Object.definePorperty(物件,'屬性',參數) 使用的範例如下 ```javascript= const obj = { a:'a', b:'b', c:'c', b:'b' } // 修改屬性特徵 //Object.definePorperty(物件,'屬性',參數) //參數{value:,writable:boolean,configable:boolean,enumerable:boolean} // 參數可以只寫要修改就好,下面是因為展示與練習需要才寫全 // 修改值 Object.defineProperty(obj, 'a',{ value:1, writable:true, configurable:true, enumerable:true, }) console.log('obj屬性a的值被修改為',obj.a) // 1 // writable:false 屬性的值是否能被修改 Object.defineProperty(obj, 'b',{ writable:false }) obj.b = 2 console.log('obj屬性b的值為',obj.b) // "b" // configurable:false 屬性的值是否能被刪除 Object.defineProperty(obj, 'c', { configurable:false }) delete obj.c console.log(obj.c) // "c" // enumerable 屬性是否可被列舉 Object.defineProperty(obj, 'd', { enumerable:false }) const objProperty = Object.keys(obj) const objValue = Object.values(obj) console.log(objProperty) // ["a", "b", "c"] 注意沒有出現d這個屬性 console.log(objValue) // ["1", "b", "c"] 注意沒有出現d的的值 ``` ## 1-3如何一次更改多個屬性特徵? 使用Object.definePropertys(物件,{"屬性":{參數}}) ```javascript= const obj2 = { A:'A', B:'B', C:'C' } Object.defineProperties(obj2, {'A':{value:'123', configurable: false}, 'B':{writable: false, enumerable: false}, 'C':{value:'abc', writable: false, configurable:false, enumerable: false}}) delete obj2.A console.log('obj2屬性a的值:',obj2.A) // "123" 且無法被刪除 obj2.B = 123 console.log(obj2.B) // obj2屬性B的值無法被寫入 obj2.C = 'STRING' delete obj2.C console.log(obj2.C) // "abc" console.log(Object.keys(obj2)) // ["A"] B 與 c無法被列舉 ``` ## 1-4巢狀的物件屬性不會被影響 直接看範例 ```javascript= const obj3 = { a:{ A:'123' } } Object.defineProperty(obj3, 'a', {writable:false}) obj3.a = 123 console.log(obj3.a) //{A:'123'} obj3.a.A = 'STRING' console.log(obj3.a.A) // 'STRING' 不受writable:false的影響可以寫入 ``` [屬性的特性修改範例](https://codepen.io/efzdamnp-the-lessful/pen/ZExppzE?editors=0011) ### 2-1物件擴充的修改與調整 這裡可以看做是物件的特徵,與屬性相同可以透過與法設定物件的特性,讓物件能不能寫入或刪除新的屬性。 ### 2-2preventExtensions防止擴充 就是不讓物件新增新的屬性。 物件的屬性值可以修改。 物件的屬性可以刪除。 物件屬性特徵可以調整。 巢狀屬性的值可修改。 範例如下 ```javascript= // Object.preventExtensions(物件) const obj = { a:'a', b:'b', c:{} } Object.preventExtensions(obj) console.log('obj是否可擴充', Object.isExtensible(obj)) console.log('obj a的屬性特膯', Object.getOwnPropertyDescriptor(obj, 'a')) //{ // value:"a", // writable:true, // configurable:true, // enumerable:true, //} obj.d = 'd' console.log(obj.d) // undefind Object.defineProperty(obj, 'a', {value:1,writable:false}) obj.a = 123 console.log(obj.a) // 1 obj.b = 2 console.log(obj.b) / 2 obj.c.a = 789 console.log(obj.c.a) // 789 ``` ### 2-3 seal 就是不讓物件新增新的屬性。 物件的屬性值可以修改。 物件的屬性不可刪除。 物件屬性特徵不可以調整。 巢狀屬性的值可修改。 範例如下 ```javascript= const obj2 = { a:'a', b:'b', c:{} } Object.seal(obj2) console.log('obj2是否可擴充', Object.isExtensible(obj2)) console.log('obj2是否封裝', Object.isSealed(obj2)) console.log('obj2 a的屬性特徵', Object.getOwnPropertyDescriptor(obj2, 'a')) //{ // value:"a", // writable:true, // configurable:false, // enumerable:true, //} obj2.d = 'd' console.log(obj2.d) // undefind // Object.defineProperty(obj2, 'a', {value:1,writable:false}) 會報錯不可執行 obj2.a = 123 console.log(obj2.a) // 1 obj2.b = 2 console.log(obj2.b) / 2 obj2.c.a = 789 console.log(obj2.c.a) // 789 delete obj2.b console.log(obj2.b) // 2 ``` ### 2-4 frezzez 就是不讓物件新增新的屬性。 物件的屬性值不可修改。 物件的屬性不可刪除。 物件屬性特徵不可以調整。 巢狀屬性的值可修改。 範例如下 ```javascript= const obj3 = { a:'a', b:'b', c:{} } Object.freeze(obj3) console.log('obj3是否可擴充', Object.isExtensible(obj3)) console.log('obj3是否封裝', Object.isSealed(obj3)) console.log('obj3是否凍結', Object.isFrozen(obj3)) console.log('obj3 a的屬性特徵', Object.getOwnPropertyDescriptor(obj3, 'a')) //{ // value:"a", // writable:false, // configurable:false, // enumerable:true, //} obj3.d = 'd' console.log(obj2.d) // undefind // Object.defineProperty(obj3, 'a', {value:1,writable:true}) 會報錯不可執行 obj3.a = 123 console.log(obj3.a) // "a" obj3.b = 2 console.log(obj3.b) // "b" obj3.c.a = 789 console.log(obj3.c.a) // 789 delete obj3.b console.log(obj3.b) // "b" ``` ### 2-5 對照表 ||preventExtensions|seal|frezzez| |:----:|:----:|:----:|:----:| |物件新增新的屬性|否|否|否| |物件的屬性值修改|可|可|否| |物件的屬性刪除|可|否|否| |物件屬性特徵調整|可|否|否| |巢狀屬性的值可修改|可|可|可| ## 3 屬性列舉與原型的關係 ## 4 Getter 與 Setter,賦值運算不使用函式 get取得值的方法 set寫入值的方法 ## 4-1 實際使用的範例 ```javascript= const obj = { money:100, set save(num){ this.money = this.money + num }, // get可以與set同名,因為get是取得數值,所以不用傳入參數。 get save(){ return this.money / 2 } } // 用get取得數值 console.log(obj.save) //50 // 其中的"save":"..." 如果點開的話會直接出現最後的結果 console.log(obj) //{ //money:100 //save:"..." //} // 要用賦值的方式寫入 obj.save = 500 console.log(obj.money) //600 console.log(obj.save) // 300 ``` 下圖為save點開前 ![](https://i.imgur.com/qRcZzs2.jpg) 下圖為save點開後 ![](https://i.imgur.com/oH9QDHH.jpg) ## 4-2 用Object.defineProperty() 加入get 與 set ```javascript= const obj2 = { money:100 } Object.defineProperty(obj2, 'save',{ // enumerable:true, // configurable:true, get:function(){ return this.money }, set:function(num){ this.money = this.money + num } }) obj2.save = 200 console.log(obj2.money) // 300 console.log('save',obj2.save) // 300 console.log(Object.getOwnPropertyDescriptor(obj2, 'save')) // 可以看到eumerable(可列舉)與configurable(可刪除)都是false, // 可以在上方加入enumerable:true與 configurable:true ``` ## 4-3 使用Object.defineProperty修改Array.prototype ```javascript= Object.defineProperty(Array.prototype, 'lastIndex',{ get:function(){ return this[this.length - 1] } }) const arr = [1, 2, 3, 4] console.log(arr.lastIndex) // 4 // 補充使用 Array.prototype的方法 Array.prototype.last = function(){ return this[this.length - 1] } console.log(arr.last()) // 4 ``` [Getter 與 Setter,賦值運算不使用函式範例程式碼](https://codepen.io/efzdamnp-the-lessful/pen/QWmpWPO?editors=0011)