JavaScript 學習筆記 之 對象 (二) - 屬性描述符

複製對象

複製對象是平時比較常遇到的需求之一

可惜的是,這裏並沒有什麼一個內置的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
//	}
  1.  Writable 決定是否可以修改屬性的值,在嚴格模式下修改一個writable爲false的屬性還會出現TypeError錯誤

  2. Configurable 決定是否可以修改屬性描述符,不管是不是嚴格模式都會出現一個TypeError錯誤,而且如果設定configurable爲false,這將是一個單向操作,無法被撤銷(不過有一個例外,即便設定了false,我們還是可以將writable的狀態由true改爲false,但是無法由false改爲true)除了無法修改,狀態爲false時甚至無法刪除這個屬性

  3. Enumerable 決定這個屬性是否可枚舉 這個描述符控制的是屬性是否會出現在對象的屬性枚舉中,比如for..in循環,你可以正常的訪問到這個屬性,但是無法被for..in遍歷到(因此數組使用for..in時要小心,因爲for..in不僅會包含所有數值索引,還會包含所有的可枚舉屬性)

屬性描述符的應用

不變性

有的時候你可能會希望你你所創建的某個屬性或者對象是不變的

這時候需要引入一個淺不變性和深不變性的概念

ES5中有很多方法來實現不變性,但是這些方法都是淺不變性的,也就是隻會更改對象的直接屬性

對象的引用屬性,比如存儲了對其他對象,函數,數組的引用,這些對象不受影響

 

1.對象常量

結合writable:falseconfigurable:false就可以創建一個真正的常量屬性(不可修改,重定義或刪除),修改方法見上

2.禁止拓展

Object.preventExtensions(..)可以禁止一個對象添加新屬性,但是目前已有的屬性不受影響,在嚴格模式下強行添加會出現TypeError錯誤

3.密封

Object.seal(..)會創建一個密封的對象,實際上等同於調用了一個preventExtensions(..)並把所有屬性標記爲configurable:false

因此密封之後的對象不能添加新屬性也不能刪除或重新配置,但是可以修改

4.凍結

注意,這是你能引用在對象上最高級別的不可變性

Object.freeze(..)會創建一個凍結對象,實際上等同於調用了preventExtensions(..)並把所有屬性標記爲configurable:falsewritable:false(不過引用的對象依然不受影響)

你可以深度凍結一個對象,也就是把他調用的所有對象都一起凍結了,但是這樣可能會出現一些問題,比如凍結了一些共享對象之類的

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章