定义或作用:
efineProperty()`方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。该方法允许精确地添加或修改对象的属性。
用法:
Object.defineProperty(obj, prop, descriptor)
- obj: 要定义属性的对象(也是返回值)
- prop: 要定义或者修改的属性名称/Symbol
- descriptor: 要定义或者修改的属性描述符
- 定义/修改:
let obj = {}
console.log(obj.a); // undefined
Object.defineProperty(obj,'a',{
value:1
})
console.log(obj.a); // 1
- 返回此对象:
let obj = {}
let newObj = Object.defineProperty(obj,'a',{
value:1
})
console.log(obj === newObj); // true
描述
该方法允许精确地添加或修改对象的属性。通过赋值操作添加的普通属性是额可以枚举的,在枚举对象属性时会被枚举到for...in / Object.keys 方法,可以改变这些属性的值也可以删除。这个方法允许修改默认的额外选项/配置。默认情况下,使用Object.defineProperty()添加的属性值是不可以修改的。
- 枚举的差异
let obj = {}
obj.a = 'test'
// 有输出
for(let k in obj){
console.log('normal');
console.log(k);
}
// 非空数组
let objKeys = Object.keys(obj) // ['a']
console.log(objKeys);
let objSp = {}
Object.defineProperty(obj,'a',{
value:1
})
// 没有输出
for(let k in objSp){
console.log('Object.defineProperty');
console.log(k);
}
// 输出空数组
let objSpKeys = Object.keys(objSp) // []
console.log(objSpKeys);
- 枚举的拓展
let obj = {}
Object.prototype.b = '2'
Object.defineProperty(obj,'a',{
value:1
})
// 不仅遍历自身的,还会遍历原型的属性
for(let k in obj){
console.log(k); // b
}
// 只遍历自身可以枚举的属性
console.log(Object.keys(obj));
// 遍历自身的属性 无视是否可以被枚举
console.log(Object.getOwnPropertyNames(obj));
- 改变/删除
// 可以被修改和删除
let obj = {}
obj.a = '1'
console.log(obj); // {a:"1"}
obj.a = '2'
console.log(obj); // {a:"2"}
delete obj.a
console.log(obj); // {}
// 不可以被修改和删除
let objSp = {}
Object.defineProperty(objSp,'a',{
value:'1'
})
console.log(objSp); // {a:"1"}
objSp.a = '2'
console.log(objSp); // {a:"1"}
delete objSp.a
console.log(objSp); // {a:"1"}
descriptor对象存在有两种主要形式的属性描述符: 数据描述符和存取描述符。数据描述符是具有值的属性。存储描述符是有getter和setter函数所描述的属性,一个描述符只能是这两者其中之一,不能同时是两者。
它们共享以下可选键值:
configurable: 只有为true时,1. 该属性的描述符才能改变,2. 同时该属性也能从对应对象上被删除。默认false。
// configurable
let objSp = {}
Object.defineProperty(objSp,'a',{
configurable:true,
value:"2"
})
console.log(objSp);
// 删除属性
delete objSp.a
console.log(objSp); // {}
// 改变描述符
Object.defineProperty(objSp,'a',{
configurable:true,
value:"3"
})
console.log(objSp); // {a:"3"}
let objSpSecond = {}
Object.defineProperty(objSpSecond,'a',{
configurable:false,
value:"2"
})
console.log(objSpSecond);
// 删除属性
delete objSp.a
console.log(objSpSecond); // {a:"2"}
// 改变描述符
Object.defineProperty(objSpSecond,'a',{
configurable:false,
value:"3"
})
console.log(objSpSecond); // 报错
enumerable:只有为true时,才可以被枚举。默认false。
// enumerable
let objSp = {}
Object.defineProperty(objSp,'a',{
enumerable:true,
value:"2"
})
// 有输出
for(let k in objSp){
console.log(k);
}
// 非空数组
let objSpKeys = Object.keys(objSp) // ['a']
console.log(objSpKeys);
数据描述符:
value:设置属性对应的值,可以试任何有效的js值(数值,对象,函数等)。默认undefined。
let objSp = {}
Object.defineProperty(objSp,'a',{
value:"2"
})
console.log(objSp); // {a:"2"}
writable:当为true是,value才能被复制运算符修改,即属性的值才可以修改。默认false。
let objSp = {}
Object.defineProperty(objSp,'a',{
value:"2",
writable:true
})
console.log(objSp); // {a:"2"}
objSp.a = '3'
console.log(objSp); // {a:"3"}
get:访问属性时,会调用这个函数,执行时不会传入任何参数,会传入this对象。返回值默认undefined。set: 当属性被修改时执行这个函数,接受一个参数(也就是被赋予的新值),会传入this对象。 返回值默认undefined。
let objSp = {}
let value = null
Object.defineProperty(objSp,'a',{
get(){
console.log('查看a的值');
return value
},
set(newVal){
console.log('改变a的值');
value = newVal
}
})
console.log(objSp.a); // 查看a的值 1
objSp.a = 2
console.log(objSp.a);
拥有布尔值的键: configurable,enumerable,writable的默认值都是false
属性值和函数的键: value,get,set的字段默认值都是undefined
注意:描述符不能同时拥有value/writable和get/set键,不然会报错。就是说有了get或者set中的一个就不能有value或者writable,反之同理。
前置知识
- return
如果if里面有return的话,就不会再执行if外面后面的函数了
function setDataProp(data,dataDefine){
// console.log(data,dataDefine);
// 对于传入的data进行判断
if(typeof data !== 'object' || data === null){
console.log('test');
throw TypeError('we need object or array')
}
// 因为if里面有return就不会走到后面的代码了!
if(!Array.isArray(data)){
return strictData(data,dataDefine)
}
// 第一个return为函数的,第二个为map的
return data.map((obj)=>{
return strictData(obj,dataDefine)
})
}
方法的应用
需求:通过前置处理数据的属性,达到精确控制数据的属性目的,同时也提供可以修改描述符的方法。
返回的数据:
{
name: "zxx",
age: 21,
job: "前端工程师",
key: 45,
},
{
name: "plw",
age: 18,
job: "ui工程师",
key: 46,
},
{
name: "test",
age: 24,
job: "后端工程师",
key: 47,
},
];
设置属性值:
export default {
name:{
configurable:true,
enumerable:true,
writable:false
},
age:{
configurable:true,
enumerable:true,
writable:false
},
job:{
configurable:true,
enumerable:true,
writable:true
},
key:{
configurable:true,
enumerable:false,
writable:false
}
}
index.js:
;(()=>{
// 处理函数
let res = setDataProp(data,dataDefine)
})()
处理函数:
function setDataProp(data,dataDefine){
// 对于传入的data进行判断
// 先过滤非Obj
// 在根据Array和Obj进行不同操作
if(typeof data !== 'object' || data === null){
console.log('test');
throw TypeError('we need object or array')
}
if(!Array.isArray(data)){
return strictData(data,dataDefine)
}
// 第一个return为
return data.map((obj)=>{
return strictData(obj,dataDefine)
})
}
function strictData(obj,dataDefine){
// console.log(obj,dataDefine);
let keys = Object.keys(dataDefine)
// console.log(keys);
// 这里用新的对象,而不是旧的对象
// 便于后面再对象的原型上挂载修改属性的方法
let newObj = new CreateNewObj()
keys.forEach((key)=>{
Object.defineProperty(newObj,key,{
value:obj[key],
...dataDefine[key]
})
})
return newObj
}
构造函数:
// 注意这里的构造函数只能在写在另外的文件,因为import会把这个js执行一遍
// 如果写在原来的页面,挂载原型的方法会在后面执行,会对后续的业务要求有差别
// 原型上挂载一个方法可以修改属性的true/false
CreateNewObj.prototype.setConfig = function(key,prop,value){
Object.defineProperty(this,key,{
[prop]:value
})
}
// 会产生一个现象:用for in 会遍历出来原型的对象,Object.keys到不会
// 因此再对原型对象进行操作
function CreateNewObj(){
let keys = Object.keys(CreateNewObj.prototype)
keys.map((key)=>{
Object.defineProperty(CreateNewObj.prototype,key,{
configurable:false,
enumerable:false,
writable:false
})
})
}
// 至此大功告成!