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(不过引用的对象依然不受影响)

你可以深度冻结一个对象,也就是把他调用的所有对象都一起冻结了,但是这样可能会出现一些问题,比如冻结了一些共享对象之类的

猜你喜欢

转载自blog.csdn.net/Aproducer/article/details/82635179