複製對象
複製對象是平時比較常遇到的需求之一
可惜的是,這裏並沒有什麼一個內置的copy函數能夠簡單的完成這一步
事實上JavaScrip中複製一個對象比我們想象的要複雜的多
複製一個對象首先要決定複製方式是深複製還是淺複製
對於淺複製來說,複製出來的新對象的屬性的值會直接複製舊對象中的值
但是由於對象中的方法並不是對象的值,對象存儲的只是對這個方法函數的引用而已
淺複製出來的對象中的方法其實跟舊對象中的方法是一樣的,都是指向同一個函數
而深複製,不僅要複製屬性值,還要複製屬性引用的方法,那麼如果數個方法中出現了引用嵌套呢?
那麼又要繼續複製引用的函數,這樣就會由於出現循環引用而導致死循環
對於JSON安全的對象來說有個巧妙的方法
(JSON安全是指可以被序列化爲一個JSON對象並且可以根據這個字符串解析出一個結構和值完全一樣的對象)
var newObj = JSON.parse(JSON.stringify(oldObj));
ES6中的對象複製
相比深複製,淺複製問題要少很多,所以ES6定義了Object.assign(..)方法來實現淺複製
Object.assign()方法接受的第一個對象是目標對象,之後跟一個或多個源對象
它會遍歷所有源對象的所有可枚舉(接下來會講到)的屬性,並把它們複製到目標對象然後返回這個目標對象
var oldObj1 = {
a: 1,
foo: foo
}
var oldObj2 = {
b: 2
}
function foo() {
console.log(this.a, this.b);
}
var newObj = Object.assign({}, oldObj1, oldObj2);
newObj.foo(); //1,2
屬性描述符
ES5開始,所有的屬性都具備了屬性描述符(用來描述屬性特性,比如是否可寫,是否可枚舉等)
var obj={
a:1
}
console.log(
Object.getOwnPropertyDescriptor(obj,"a")
)
//{
// value: 1, 屬性的值
// writable: true, 是否可寫
// enumerable: true, 是否可枚舉
// configurable: true 是否可修改屬性配置符
// }
創建屬性的時候,屬性描述符會使用默認值
我們也可以自己設置或者修改一個屬性的屬性描述符
var obj={
a:1
}
Object.defineProperty(obj,"a",{
value: 2,
writable: true,
enumerable: true,
configurable: true
})
console.log(
Object.getOwnPropertyDescriptor(obj,"a")
)
//{
// value: 2,
// writable: true,
// enumerable: true,
// configurable: true
// }
-
Writable 決定是否可以修改屬性的值,在嚴格模式下修改一個writable爲false的屬性還會出現TypeError錯誤
-
Configurable 決定是否可以修改屬性描述符,不管是不是嚴格模式都會出現一個TypeError錯誤,而且如果設定configurable爲false,這將是一個單向操作,無法被撤銷(不過有一個例外,即便設定了false,我們還是可以將writable的狀態由true改爲false,但是無法由false改爲true)除了無法修改,狀態爲false時甚至無法刪除這個屬性
-
Enumerable 決定這個屬性是否可枚舉 這個描述符控制的是屬性是否會出現在對象的屬性枚舉中,比如for..in循環,你可以正常的訪問到這個屬性,但是無法被for..in遍歷到(因此數組使用for..in時要小心,因爲for..in不僅會包含所有數值索引,還會包含所有的可枚舉屬性)
屬性描述符的應用
不變性
有的時候你可能會希望你你所創建的某個屬性或者對象是不變的
這時候需要引入一個淺不變性和深不變性的概念
ES5中有很多方法來實現不變性,但是這些方法都是淺不變性的,也就是隻會更改對象的直接屬性
對象的引用屬性,比如存儲了對其他對象,函數,數組的引用,這些對象是不受影響的
1.對象常量
結合writable:false和configurable:false就可以創建一個真正的常量屬性(不可修改,重定義或刪除),修改方法見上
2.禁止拓展
Object.preventExtensions(..)可以禁止一個對象添加新屬性,但是目前已有的屬性不受影響,在嚴格模式下強行添加會出現TypeError錯誤
3.密封
Object.seal(..)會創建一個密封的對象,實際上等同於調用了一個preventExtensions(..)並把所有屬性標記爲configurable:false
因此密封之後的對象不能添加新屬性也不能刪除或重新配置,但是可以修改
4.凍結
注意,這是你能引用在對象上最高級別的不可變性
Object.freeze(..)會創建一個凍結對象,實際上等同於調用了preventExtensions(..)並把所有屬性標記爲configurable:false和writable:false(不過引用的對象依然不受影響)
你可以深度凍結一個對象,也就是把他調用的所有對象都一起凍結了,但是這樣可能會出現一些問題,比如凍結了一些共享對象之類的