【每日一题】如何设置对象的属性不可添加或删除

前端印记

共 5017字,需浏览 11分钟

 ·

2021-09-02 01:55

人生苦短,总需要一点仪式感。比如学前端~

如何设置对象不可添加或者删除属性

一共有如下几种方式:

  1. Object.defineProperty()
  2. Object.seal()
  3. Object.freeze()
  4. Proxy

前置知识:对象属性的数据描述符

对象的属性有几个数据描述符:

  1. configurable 表示是否可配置,作用有2:
    • 属性数据描述符是否可被改变 (除value和writable特性外的其他描述符)
    • 属性是否可被删除
  2. enumerable: 表示属性是否可枚举
  3. value 表示属性的值
  4. writable 表示属性的值是否可以修改

默认值

使用 Object.defineProperty() 添加的属性值是不可修改(immutable)的
这主要和几个属性的默认值有关:

const object1 = {};

Object.defineProperty(object1, 'property1', {
});
// 不填写属性数据描述符的前提下,获取生成的描述符默认值
console.log(Object.getOwnPropertyDescriptor(object1, 'property1'))
属性描述符默认值

查看对象属性描述符

Object.getOwnPropertyDescriptor() 方法,用来查看指定对象上某个非原型属性的属性描述符。

特别记忆:

对象属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。

当试图改变不可配置属性的值时 (除了 value 和 writable 属性之外) ,会抛出TypeError,除非当前值和新值相同。

尝试修改导致的报错

实现方法讲解如下

defineProperty、defineProperties

根据上边属性描述符的作用我们可以推断,如果将 configurable 的值为 false 的时候,属性就无法从对象上删除。

Object.defineProperty(obj, "name", {
  configurablefalse// 当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。
  enumerablefalse// 当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。
  writablefalse// 表示是否可以修改属性的值。
  value"小石头"// 该属性(name)对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。
});

像上边代码这样设置之后,name属性就变成了不能删除、不可重新修改特性、不可枚举、不能修改属性值的状态。

但是只这么设置的话,目前obj对象只实现了元素无法被删除,我们还是能往对象中添加新的属性。

obj.address = "beijing";
console.log(obj); // { address: "beijing", name: "小石头" }

Object.seal()

一个对象是可扩展的(可以添加新的属性)。而Object.seal()方法会封闭一个对象:

  • 阻止添加新属性
  • 并将所有现有属性标记为不可配置(即不可删除和不可修改属性的某几个数据描述符)
  • 不过,当前属性的值只要原来是可写的就可以改变。也就是说属性的值仍然可以修改。

尝试删除一个密封对象的属性或者将某个密封对象的属性从数据属性转换成访问器属性,结果会静默失败或抛出 TypeError (在严格模式中最常见的,但不唯一)。

const obj = { name"小石头" };
Object.getOwnPropertyDescriptors(obj);
查看属性数据描述符

封闭对象:

Object.seal(obj);
Object.getOwnPropertyDescriptors(obj);
封闭普通对象后的效果

这时如果再去配置就失效了

Object.defineProperty(obj, "name", {
  configurabletrue
});
Object.getOwnPropertyDescriptors(obj);
configurable 已经不可以配置、属性也不可删除了

同时也没法添加新属性了

obj.age = 18
对象也不可添加新属性了

Object.freeze()

Object.freeze() 方法可以冻结一个对象。冻结后的对象有如下特点:

  • 一个被冻结的对象再也不能被修改;
  • 不能向冻结对象添加新的属性,
  • 不能删除冻结对象的已有属性,
  • 不能修改冻结对象已有属性的可枚举性、可配置性、可写性以及不能修改已有属性的值。
  • 此外,冻结一个对象后该对象的原型也不能被修改

⚠️注意:Object.freeze()方法实现的是浅冻结

const obj = {
  name"小石头",
  info: {
    address"beijing",
  },
};
const freezeObj = Object.freeze(obj); // freeze()返回和传入的参数相同的对象
// 不能修改冻结对象已有属性值
freezeObj.name = "石头姐";
console.log(freezeObj.name); // 仍然是小石头
// 不能往冻结对象新加内容
freezeObj.newName = '石头姐'
console.log(freezeObj.name); // undefined,增加失败
// 删除冻结对象的已有属性也不成功
delete freezeObj.name
console.log(freezeObj.name); // 仍然是小石头
冻结后的对象和原对象看上去没啥区别

严格模式下,操作冻结对象的属性会报错

("use strict");
freezeObj.name = "石头姐"// TypeError
//Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>'
报错

深层嵌套的对象没有被冻结:

// 在这里,info 是没有被冻结的
freezeObj.info.newName = "BJ";
console.log(freezeObj.info.newName); // BJ

ES6 的 Proxy 方法

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
From:https://es6.ruanyifeng.com/#docs/proxy

它在 API 使用者和对象之间扮演了一个中间人的角色。我们可以使用 Proxy 来控制被访问的底层对象的属性的行为。

const target = {
  a"我是不能删除的",
  b"我是不能修改的",
  c"也不能添加,我只有3个元素",
};
const lockTraget = new Proxy(target, {
  // 拦截对象属性的设置,返回false不让设置属性
  set(target, property, value) {
    console.log("拦截修改、新增操作", target, property, value);
    return false;
  },
  // 拦截delete proxy[propKey]的操作,返回false不让删除
  deleteProperty(target, property) {
    console.log("拦截删除操作");
    return false;
  },
  // 拦截Object.defineProperty()、Object.defineProperties()操作,
  defineProperty(target, property, descriptor) {
    console.log("defineProperty()");
    return false;
  },
});
lockTraget.newName = "石头姐";
console.log(lockTraget.newName); // undefined,新增失败

delete lockTraget.a;
console.log(lockTraget.a); // "我是不能删除的", 删除失败

lockTraget.b = '小石头'
console.log(lockTraget.b); // "我是不能修改的", 修改失败

最后,我们挣扎的想用Object.defineProperty修改属性数据描述符,也报错了


END
愿你历尽千帆,归来仍是少年。


让我们一起携手同走前端路!

关注公众号回复【加群】即可

● 工作中常见页面布局的n种实现方法

● 三栏响应式布局(左右固宽中间自适应)的5种方法

● 两栏自适应布局的n种实现方法汇总

● 工作中常见的两栏布局案例及分析

● 垂直居中布局的一百种实现方式

● 常用九宫格布局的几大方法汇总

● 为什么操作DOM会影响WEB应用的性能?

● 移动端滚动穿透的6种解决方案

● Vue + TypeScript 踩坑总结

浏览 117
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报