JavaScript对象的不可变性

通过以下方式创建的对象默认是可配置的,可写的,可枚举的:

let person = {name:'noshower'};

let obj = new Object();
obj.name = 'noshower';

如果希望属性或对象是不可变的,那么该如何做?

请继续阅读->

首先,什么是对象的属性?

在 Javascript 中, 属性 由一个字符串类型的“名字”(name)和一个“属性描述符”(property descriptor)对象构成。

其中,属性描述符由value,writable,get,set,configurable,enumerable这些属性当中的某些组成。

  • writable 决定是否可以修改属性的值
  • configurable 决定了是否可以修改属性描述符或者删除属性
  • set 如果访问器属性没有设置set,该属性不可写

其次,如何修改或设置属性的属性描述符

使用Object.defineProperty(obj, prop, descriptor),该方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。

举个例子:

var person = {};
Object.defineProperty(person, "name", {
  configurable: false,
  writable: false,
  value: "noshower"
});
person.name = 'wang';
console.log(person.name); // 输出 noshower  严格模式下,会报错

上面的例子中,我们给person对象添加了一个新属性,并且将该属性设置为不可写,不可删除。

最后,如何查看对象属性的属性描述符?

Object.getOwnPropertyDescriptor(obj, prop),该方法返回指定对象上一个自有属性对应的属性描述符。

以上一个例子为例:

  console.log(Object.getOwnPropertyDescriptor(person,'name'))
  //输出如下:
 { value: 'noshower',
  writable: false,
  enumerable: true,
  configurable: false }

注意:接下来要讲的所有方法创建的都是浅不变性,只会影响目标对象和它的直接属性,并不会引用对象。

举个例子:

let person = {
    favorite:['apple']
}

Object.defineProperty(person,'favorite',{
    writable:false
})

person.favorite = [];
console.log(person.favorite); //[ 'apple' ]
person.favorite.push('orange');
console.log(person.favorite); //[ 'apple', 'orange' ]

上面的例子中,尝试修改person.favorite的引用,不成功。修改person.favorite引用的数组的值,成功了。

限制对象可变现的几种方法

1. 对象常量

要使对象的某个属性为常量(即不可修改,不可删除,不可重新定义)
可以按照上面讲的使用Object.defineProperty方法,将属性的属性描述符writable,configurable都设为false

2.禁止对象扩展

使用Object.preventExtensions()方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。

注意:不可扩展的对象,属性可能还能够被删除。

注意:Object.preventExtensions()仅阻止添加自身的属性。但属性仍然可以添加到对象原型。

举个例子:

let person = {
    name: 'noshower'
}
//使得person不可扩展
Object.preventExtensions(person);
person.age = 22;
console.log(person.age); // undefined  age属性没有被添加进来
delete person.name;
console.log(person.name); //undefined name属性被删除了

3. 密封对象

使用Object.seal()方法密封一个对象,这个方法实际上会在一个现有对象上调用Object.preventExtensions并把所有自身现有属性标记为configurable:false

密封之后,这个对象不能添加属性,不能重新配置或者删除任何现有属性。

不会影响从原型链上继承的属性。但__proto__ 属性的值也会不能修改。

举个例子:

let person = {
  name: 'noshower'
}
//密封person
Object.seal(person);
//添加age属性
person.age=23;
//输出name属性的属性描述符
console.log(Object.getOwnPropertyDescriptor(person,'name'));
// { value: 'noshower',writable: true, enumerable: true,configurable:false }

console.log(person.age); // undefined 表明不可添加新属性
delete person.name;
console.log(person.name); // noshower 表明不可删除属性

4. 冻结对象

Object.freeze()方法可以冻结一个对象。这个方法实际上会在一个现有对象上调用Object.seal()并把所有数据访问属性标记为writable:false,这样就无法修改它们了。

举个例子:

let person = {
    name: 'noshower'
}
 //冻结person对象
Object.freeze(person);

console.log(Object.getOwnPropertyDescriptor(person,'name'));
  // { value: 'noshower',writable: false, enumerable: true,configurable:false }

person.name='Jack';
console.log(person.name); // noshower 表明属性不可修改

总结

控制一个对象的不可变性,有四个方法:Object.defineProperty(或者Object.defineProperties),Object.preventExtensions,Object.seal,Object.freeze

记住了:这些方法创造的都是浅不变性

参考文献:

  1. 《你不知道的JavaScript上卷》
  2. MDN文档
    原文作者:打铁大师
    原文地址: https://www.jianshu.com/p/dbe0467bb5c4
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞