JavaScript中对象防止篡改

138 阅读3分钟

什么是属性

在 Javascript 中, 属性 由一个字符串类型的“名字”(name)和一个“属性描述符”(property descriptor)对象构成。我们平常只关注了属性值,而没有关注更多的属性描述符;

从以上的属性描述可以看出,我们可以修改属性的 属性描述符 来设置属性对外特性,这种特性会约束属性的操作或者对外提供的功能;

Object 作为js中对象的基础对象,提供了以下三个方法:

  • Object.freeze();
  • Object.seal();
  • Object.preventExtensions();

就三个提供的功能来讲,我们可以猜测是如何做到的?

参考:defineProperty 属性是有一组属性描述符组成的,具体含义查看MDN:

{
configurable: false,
enumerable: false,
value: undefined,
writable: false,
get() { 
		return ...
	},
set(x){
	... = x;
	}
}

以上三个方法,就是的参数都是对象,是对参数对象的进行作用,并且是对参数对象中所有属性的描述符进行修改,来达到方法的作用;

如何查看一个对象的属性描述符

Object.getOwnPropertyDescriptors(obj);

此方法,只有个参数,将需要查看的对象传入,便可以返回对象的每一个 属性的属性描述符;

比如:

Object.getOwnPropertyDescriptors({ a: 1,b: 2,c: 3 });

输出

{
  "a": {
    "value": 1,
    "writable": true,
    "enumerable": true,
    "configurable": true
  },
  "b": {
    "value": 2,
    "writable": true,
    "enumerable": true,
    "configurable": true
  },
  "c": {
    "value": 3,
    "writable": true,
    "enumerable": true,
    "configurable": true
  }
}

Object.freeze()

const obj = {a: 1};
Object.freeze(obj);
// 冻结前
{
  "a": {
    "value": 1,
    "writable": true,
    "enumerable": true,
    "configurable": true
  }
}
// 冻结后
{
  "a": {
    "value": 1,
    "writable": false,
    "enumerable": true,
    "configurable": false
  }
}

结论:

从属性描述符来看,更改了两个属性,writable=false, configurable=false,表明属性只读,不可以修改,同时属性描述符不可以再修改;

者就是我们平日看到的,被冻结后的现象;不能重新赋值了;

delete obj.a 也会是被,因为 configurable被设置为false,不允许将属性从对象中移除;

Object.preventExtensions()

阻止对象中属性扩展,既然是扩展,那么就是拒绝增加属性,但可以删除属性;

// 组织前
{
  "a": {
    "value": 1,
    "writable": true,
    "enumerable": true,
    "configurable": true
  }
}
// 阻止后
{
  "a": {
    "value": 1,
    "writable": true,
    "enumerable": true,
    "configurable": true
  }
}

已有属性描述符,并没有变化;

我们可以使用方法Object.isExtensible(obj)来查看当前对象是否 可以扩展,Object.preventExtensions 的作用标记是记录在对象本身上的;因此我们在给对象增加属性时,内部会参考这个标志来决定是扩展;

obj.b = 2;
console.log(obj); // 输出:{a: 1} 表明无法在增加属性

delete obj.a //返回 ture;
console.log(obj); // 输出:{} 表明删除属性成功

Object.seal()

密封对象的属性;

// 密封前
{
  "a": {
    "value": 1,
    "writable": true,
    "enumerable": true,
    "configurable": true
  }
}
// 密封后
{
  "a": {
    "value": 1,
    "writable": true,
    "enumerable": true,
    "configurable": false
  }
}

密封后

  • configurable属性,被设置为false;

所以密封后,我们是可以修改属性值 obj.a = 2;

delete obj.a 将是被,不可以删除;

  • Object.isExtensible(obj) 返回false,表示不可扩展,因此 obj.b = 2; 增加属性失败;

结论:

密封是来源于面向对象编程,对象被密封后,属性被保护起来了,禁止增加和删除属性,但属性值本身是可以修改的;

结论

  • preventExtensions 可删除和修改源属性,不可添加新属性
  • seal 可修改但是不可删除源属性,不可添加新属性
  • freeze 不可修改和源属性,不可添加新属性,三者中最严格;