初识Object.defineProperty
创始人
2025-05-28 19:39:56
0

作用:直接在一个对象上新增一个属性或修改已有属性的值,并返回这个对象。

let obj = {age: 18
}
// 给obj新增一个属性name
Object.defineProperty(obj, 'name', {value: 'liHua'
})
// 修改obj的age属性的值
const p = Object.defineProperty(obj, 'age', {value: 99,
})
console.log(obj.name); // liHua
console.log(obj.age); // 99
console.log(p === obj, p); // true {age: 99, name: 'liHua'}

可见,该方法的语法为:Object.defineProperty(obj, prop, options)

  • obj为要操作的对象
  • prop为要添加或修改的属性
  • options为配置对象,其中value用于给属性赋值,还有enumerable,writable和configurable属性,以及get和set方法。

下面将逐一讲解这些属性和方法。

enumerable

我们给obj用Object.defineProperty添加sex属性,用for…in…循环遍历,结果没有找到这个属性。但是我们却能通过点运算符获取属性值,这是为什么?

let obj = {}
obj.age = 18
Object.defineProperty(obj, 'sex', {value: '男'
})
for (let item in obj) {console.log('for循环遍历的元素',item);
}
// for循环遍历的元素 age
console.log(obj.sex); // 男

原来,如果我们通过点运算符直接给对象添加属性,options中的属性都默认为true。

例如,上面的obj.age = 18等同于下面的代码,其中,enumerable为true,则表示该属性被可枚举。

Object.defineProperty(obj, "age", {value: 18,writable: true, configurable: true,enumerable: true
});

而通过Object.defineProperty添加的属性,options中的属性都默认为false。

例如:Object.defineProperty(obj, 'sex', { value: '男' }) 等同于下面的代码,enumerable为false,自然就不能通过遍历的方式找到它了。

Object.defineProperty(obj, "sex", {value: '男',writable: false, configurable: false,enumerable: false
});

如果我们想要遍历这个元素,只需要将属性设置为可被枚举即可。

let obj = {}
obj.age = 18
Object.defineProperty(obj, 'sex', {value: '男',enumerable:true
})
for (let item in obj) {console.log('for循环遍历的元素', item);
}
// for循环遍历的元素 age
// for循环遍历的元素 sex

writable

我们用Object.defineProperty给obj添加sex属性,然后修改age和sex属性,发现age属性修改成功,而sex属性并没有修改成功。

let obj = {age:18}
Object.defineProperty(obj, 'sex', {value: '男',
})
obj.age = 20
obj.sex = '女'
console.log(obj);
// {age: 20, sex: '男'}

这也是因为用该方法定义的属性的writable默认为false,表示不可写的,不能被赋值运算符改变。如果想要修改属性的值,可以将writable改为true,或者通过Object.defineProperty修改属性值。

let obj = {age: 18
}
Object.defineProperty(obj, 'sex', {value: '男',writable:true
})
obj.age = 20
obj.sex = '女'
console.log(obj);
// {age: 20, sex: '女'}

configurable

如果我们尝试用Object.defineProperty修改属性值,发现会报”重复定义属性”的错误:Uncaught TypeError: Cannot redefine property: sex at Function.defineProperty () ,这不是啪啪打脸吗

let obj = {}
Object.defineProperty(obj, 'sex', {value: '男',
})
Object.defineProperty(obj, 'sex', {value: '女',
})

其实,这是因为configurable默认为false,则不能通过Object.defineProperty改变某属性的值,所以会报错。只需要一开始将configurable设置为true,即可修改成功。

let obj = {}
Object.defineProperty(obj, 'sex', {value: '男',configurable:true
})
Object.defineProperty(obj, 'sex', {value: '女',
})
console.log(obj.sex); // 女

同时,configurable表示对象的属性是否可以被删除。还能控制enumerable,configurable,set和get方法是否能被修改。

如下例,删除sex属性失败,因为用Object.defineProperty定义的属性,configurable默认为false

let obj = {}
Object.defineProperty(obj, 'sex', {value:'男',
})
delete obj.sex
console.log(obj); // {sex: '男'}

configurable改为true后,即可删除成功。

let obj = {}
Object.defineProperty(obj, 'sex', {value:'男',configurable:true
})
delete obj.sex
console.log(obj); // {}

writable和configurable的主要区别:

我们用Object.defineProperty定义了一个属性,writable是控制是否能用点运算符修改这个属性的值;而configurable是控制是否能用Object.defineProperty修改这个属性的值。

get方法

除了value可以给属性赋值,也可以调用get方法赋值,该方法的返回值会被作为属性的值。

例如,我们给obj添加sex属性,通过get方法赋值,但是打印obj却没有找到这个属性。

let obj = {}
Object.defineProperty(obj, 'sex', {get(){return '男'}
})
console.log(obj); // {}
console.log(obj.sex); // 男

这是因为这个属性被藏起来了,当打开这个对象,就会发现sex属性,它的值为(…),点开即可看到值。或者也可以通过点运算符查看属性的值。

请添加图片描述

另外,不能同时用get方法和value属性赋值。

如下面的代码会报错:Uncaught TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute

let obj = {}
Object.defineProperty(obj, 'sex', {get(){return '男'},value:'女'
})

set方法

当属性值被修改时,会调用该方法,该方法接收一个参数(也就是被赋予的新值)。

let obj = {}
Object.defineProperty(obj, 'sex', {get(){return '男'},set(newValue){console.log(`有人要修改sex的值,值是${newValue}`);},
})
obj.sex = '女'
// 控制台输出: 有人修改了sex的值,新值是女

因为get和set都是方法,可以执行别的操作,例如:当值改变了,就去更新页面,vue2的响应式就是通过Object.defineProperty的get和set实现的。

下面的例子,将p和person联系起来了,person就像是程序员定义的属性,经过处理后,p中含有person中的所有属性,我们再将p给程序员使用。如果要获取name属性,就返回person.name的值;如果要修改name属性,set方法也能监听到,就重新给person.name赋值,同时更新页面的值,从而实现响应式更新。

const person = {name: 'lisi'}let p = {}
// 给p添加name属性
Object.defineProperty(p, 'name', {get() {console.log('有人获取p中的name属性了');return person.name},set(newValue) {console.log(`有人修改p中的name属性了,值为${newValue},我要更新页面`);person.name = newValue}
})

值得注意的是:定义了get或set,就不能定义writable或value,否则会报错。

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...