(八)控制对象属性-Object.defineProperty

前言

创建对象有许多方式,最常用的就是使用字面量的方式来创建,如下

js 复制代码
let obj = {
  name: "obj",
  age: 18,
};

但如果要精准控制对象的每个属性,比如增加一个属性height,这个属性是否可以删除、修改、遍历等,只靠对象字面量这种方式创建就没办法做到了,于是要引出今天的主题Object.defineProperty

Object.defineProperty 详讲

语法规则

Object.defineProperty(obj, property, propertyDescriptorMap)

  • obj: 要定义属性的对象。
  • property : 一个字符串或 Symbol,指定了要定义或修改的属性键。
  • propertyDescript : 要定义或修改的属性的描述符。
    返回值与参数一obj指向相同,所以一般不会有特定的变量来接收。

属性描述符

Object.defineProperty第三个参数为属性描述符,属性描述符分两种:数据属性描述符 和 访问器属性描述符

但是不论数据属性描述符还是访问器属性描述符都会有两个通用的可选键,configerable和enumerable,在此我先把这两个可选键统称为通用属性描述符 。

数据属性描述符

数据属性描述符主要来控制对象属性是否可写 ,其有两个键writable和value。

  • writable:控制对象属性是否可修改,默认值为false。
  • value:对象属性的值,默认值为undefined
js 复制代码
let obj = {
  name: "obj",
  age: 18,
};
Object.defineProperty(obj, "height", {
  writable: false,
  value: 1.85,
});
obj.height = 2
console.log(obj.height); //1.85

上面的案例可以看出,如果writable为false则不能对height属性进行修改,如果放在严格模式下修改height属性值则会报错。

访问器属性描述符

访问器属性描述符是由 getter/setter 函数对描述的属性,其有两个键get和set,这两个健对应的值都为函数类型。

  • get:读取对象属性时的拦截器,只要读取属性则会调用该函数,函数返回值被用作该属性的值。
  • set:设置对象属性时的拦截器,只要给属性赋值则会调用该函数,并把值传递给该函数。
js 复制代码
function Obj() {
  let height = null;
  Object.defineProperty(this, "height", {
    get: function () {
      console.log("对象height属性被读取");
      return height;
    },
    set: function (value) {
      console.log("对象height属性被设置");
      height = value;
    },
  });
}
let newObj = new Obj();
newObj.height = 1.9;  //log:对象height属性被设置
console.log(newObj.height);
/*log:对象height属性被读取    1.9*/

我觉得上面案例应该不需要解释什么,如果换成下面的案例会发生什么?

js 复制代码
let obj = {
  name: "obj",
};

Object.defineProperty(obj, "height", {
  get: function () {
    return obj.height;
  },

  set: function (val) {
    obj.height = val;
  },
});

obj.height = 2;

首先对obj.height赋值会调用setter,而setter里又会对obj.height赋值,结果又会调用了setter....一直循环下去,变成了死循环。

^v^ 这是我依然会犯的错误

通用属性描述符

通用属性描述符包含两种configurable和enumerable

  • configurable:表示属性是否可以删除 属性和是否可以再次配置属性描述符
  • enumerable:是否可以遍历该属性,如果为false,打印obj对象则不会显示该属性。使用Object.keys也不会显示该属性。使用Object.values也不会获取到对应的值。但该属性是真实存在的,只能通过对象.属性来获取该值。
js 复制代码
let obj = {
  name: "obj",
};

Object.defineProperty(obj, "height", {
  configurable: false,
  value: 2,
});

delete obj.height;

console.log(obj.height);

Object.defineProperty(obj, "height", {
  configurable: false,
  writable: true,
  value: 2,
});

obj.height = 3;  // 异常:Uncaught TypeError: can't redefine non-configurable property "height"

上面案例可以看出如果给属性的configurable设置为false,则该属性删除是没有效果的,如果在严格模式下则会报错,接下来我们又打算对height属性重新定义属性描述符,发现会报错,不能再次给height属性配置属性描述符。

js 复制代码
let obj = {
  name: "obj",
};

Object.defineProperty(obj, "height", {
  enumerable:false,
  value: 2,
});

console.log(Object.keys(obj));  //Array [ "name" ]

console.log(Object.values(obj)); //Array [ "obj" ]

console.log(obj.height); //2

该案例展示了height为不可遍历状态

属性描述符注意事项

数据属性描述符和访问器属性描述符不可以同时存在。也就是value和writable不能与get和set同时出现,但可以与通用属性描述符同时出现。

configurable enumerable writable value get set
数据属性描述符 可以 可以 可以 可以 不可以 不可以
访问器属性描述符 可以 可以 不可以 不可以 可以 可以

思考

js 复制代码
let obj = {
  name: "obj",
};

字面量定义的对象,obj.name都有什么属性描述符?

可以通过Object.getOwnPropertyDescriptor(obj, "name")输出看下。

相关推荐
鬼手点金7 小时前
nanoGPT/nanochat 能做出 DeepSeek/Qwen 这样的大模型吗?
服务器·前端·javascript·bug·openclaw
软件技术NINI7 小时前
美妆商城html+Css+js 4页
javascript·css·html
蛋蛋的就会顺顺的7 小时前
hot100 p0——栈,贪心算法,动态规划
java·前端·javascript·算法·leetcode·力扣
码艺-Alimjan8 小时前
Tauri 源码备份 · 打包 & 解压 总结,源码收集之路
前端·javascript·vue.js
DevUp11 小时前
标着 JavaScript 的 Wiki 与知识库:既不是 CMS 也不是 SSG(番外三)
javascript·node.js·cms
kyriewen11 小时前
我上手了 Vite+ 1.0:Node 不用自己装,lint 却翻车了
前端·javascript·程序员
鹏多多12 小时前
前端是如何死去的?| 完整时间线
前端·javascript·程序员
古夕13 小时前
iframe 授权失败时露出 60000 JSON 的复盘
前端·javascript
程序员Sunday13 小时前
ES Module 和 CommonJS 有什么区别?循环依赖时会发生什么?
前端·javascript·面试
有味道的男人14 小时前
搜好货平台关键词商品搜索接口:分页容错与商品数据轻量化提取方案
服务器·前端·javascript