1、defineProperty和defineProperties
字面意义就表明一个是操作单个,一个是操作多个。
defineProperty的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性
Object.defineProperty(obj, prop, desc),defineProperty接收参数:
obj: 需要定义属性的当前对象
prop: 当前需要定义的属性名
desc: 属性描述符
let obj = {}
Object.defineProperty(obj, 'name', {
value: 2, // 值
configurable: true, // 是否可配置、可删除
writable: true, // 是否可修改
enumerable: true // 是否可枚举、遍历
})
console.log(obj) // {name: 2}
Object.defineProperty(obj, 'name', {
value: 3,
configurable: true,
writable: true,
enumerable: true
})
console.log(obj) // {name: 3}
Object.defineProperties() 方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
Object.defineProperties(obj, props), 接收参数:
obj: 需要定义属性的当前对象(同defineProperty)
props: 对象,包含需要定义的属性及其描述
let obj = {}
Object.defineProperties(obj, {
'name': {
value: 'lily', // 值
configurable: true, // 是否可配置、可删除
writable: true, // 是否可修改
enumerable: true // 是否可枚举、遍历
},
'age': {
value: 20, // 值
configurable: true, // 是否可配置、可删除
writable: true, // 是否可修改
enumerable: true // 是否可枚举、遍历
}
})
console.log(obj) // {name: 'lily', age: 20}
大家会看到这里每次设置属性描述都包含很多参数,这里列举一下desc中每个参数的意义
-
value:
对应属性的属性值
默认 undefined
只要 writable 和 configurable 有一个为 true,就允许改动 -
get:
一个函数,表示该属性的取值函数, 不接收参数, return 一个值
默认 undefined -
set:
一个函数,表示该属性的存值函数, 接受一个参数
默认 undefined -
configurable:
是否可配置、可删除, 布尔值
true表示该属性可通过defineProperty配置,也可删除
false则相反 -
writable:
是否可写,如obj.a = 3这样赋值, 布尔值
如果为 true , 则obj.a=3生效,反之赋值失败 -
enumerable:
是否可枚举、遍历,布尔值
如果为false, 比如for in、Object.keys()获取不到该值。
下面举几个例子看看对应的情况
1、描述默认值
1、普通赋值对象
let obj = {}
obj.a = 3
相当于
let obj = {}
Object.defineProperty(obj, 'a', {
value: 3, // 值
configurable: true, // 这里默认为true
writable: true, // 这里默认为true
enumerable: true // 这里默认为true
})
2、使用defineProperty定义
Object.defineProperty(obj, 'a', {
value: 3
})
相当于
Object.defineProperty(obj, 'a', {
value: 3, // 值
configurable: false,
writable: false,
enumerable: false
})
两种赋值方式所对应的描述默认值是不同的
2、存取描述get/set
let obj = {}
let aaa
Object.defineProperty(obj, 'a', {
get: function () {
console.log('getter')
return aaa
},
set: function (val) {
console.log('setter', val)
aaa = val
}
})
console.log(obj.a) // getter -> undefined
obj.a = 333 // setter 333
console.log(obj.a) // getter -> 333
3、configurable、writable单个和配合场景
'use strict'
let obj = {}
Object.defineProperty(obj, 'a', {
value: 2,
configurable: false,
writable: true,
enumerable: true
})
delete obj.a // 报错 Uncaught TypeError: Cannot delete property 'a' of #<Object>
obj.a = 333
console.log(obj.a) // configurable为false,writable为true时赋值成功
Object.defineProperty(obj, 'a', {
value: 444
})
console.log(obj.a) // 444 这个时候是配置成功的,为什么呢?
---------------------------------------------
我们把之前的配置中只配置configurable试试
Object.defineProperty(obj, 'a', {
value: 2,
configurable: false
})
Object.defineProperty(obj, 'a', {
value: 444
}) // 只配置configurable时报错 Uncaught TypeError: Cannot redefine property: a
通过上面得出configurable和writable配合的使用场景
- configurable=false,writable=false
不可配置、删除,不可赋值。 - configurable=true,writable=true
可配置、可删除,可赋值。 - configurable=false,writable=true
obj.a = 333和Object.defineProperty(obj, 'a', {
value: 444
})这两个都是生效的
但有个特殊情况需要注意,再次配置时可以把writable由true变成false,但不能由false变为true。
- configurable=true,writable=false
obj.a = 333这样赋值会报错
Object.defineProperty(obj, 'a', {
value: 444
})这样配置是生效的。
4、enumerable枚举配置
'use strict'
let obj = {}
Object.defineProperty(obj, 'a', {
value: 2,
configurable: true,
writable: true,
enumerable: false
})
obj.b = 3
console.log(Object.keys(obj)) // ['b']
console.log(obj.propertyIsEnumerable('a')) // false
console.log(obj.propertyIsEnumerable('b')) // true
2、getOwnPropertyDescriptor
获取指定对象的自身属性描述符。自身属性描述符是指直接在对象上定义(而非从对象的原型继承)的描述符。
getOwnPropertyDescriptor(obj, keyname)接收两个参数
obj: 必传,包含属性的对象
keyname: 必传,指定的属性名
'use strict'
let obj = {}
Object.defineProperty(obj, 'a', {
value: 2,
configurable: true,
writable: true,
enumerable: false
})
console.log(Object.getOwnPropertyDescriptor(obj, 'a')) // {value: 2, writable: true, enumerable: false, configurable: true}
这个方法对应也有获取所有属性的描述,返回一个对象,包含所有属性及描述
console.log(Object.getOwnPropertyDescriptors(obj)) // {a: {…}}
这里我们顺便回顾下默认值
'use strict'
let obj = {}
Object.defineProperty(obj, 'a', {
value: 2,
configurable: true,
writable: true,
enumerable: false
})
obj.b = 3
Object.defineProperty(obj, 'c', {
value: 4
})
console.log(Object.getOwnPropertyDescriptors(obj))
image.png
上面针对配置方法和获取配置方法做了一个简单的描述,那这些对我们来说有什么用或者有什么扩展呢?
1、给对象属性添加常量,不可修改,达到const a = 1这种效果。
这里我们就可以使用configurable和writable来实现
'use strict'
let obj = {}
Object.defineProperty(obj, 'a', {
value: 1,
configurable: false,
writable: false
})
obj.a = 2 // 报错
Object.defineProperty(obj, 'a', {
value: 2
}) // 报错
delete obj.a // 不可删除
2、禁止扩展
这里会使用到一个方法,Object.preventExtensions()
'use strict'
let obj = {a: 1}
Object.preventExtensions(obj) // 禁止扩展
obj.a = 2
console.log(obj.a) // 2
obj.b = 3 // Uncaught TypeError: Cannot add property b, object is not extensible
console.log(obj.b)
Object.defineProperty(obj, 'b', {
value: 3
}) // Cannot define property b, object is not extensible
console.log(obj.b)
使用了禁止扩展,修改原属性是可以的,但添加新属性都会报错
3、密封
这里也是一个新方法,Object.seal()会创建一个密封的对象,这个方法实际上会在一个现有对象上调用object.preventExtensions(...)并把所有现有属性标记为configurable:false。
'use strict'
let obj = {a: 1}
Object.seal(obj) // 密封
obj.a = 2
console.log(obj.a) // 2
Object.defineProperty(obj, 'a', {
value: 3
})
console.log(obj.a) // 3
Object.defineProperty(obj, 'a', {
value: 4,
configurable: true
}) // 报错
console.log(obj.a)
可赋值,不可修改配置
'use strict'
let obj = {a: 1}
Object.seal(obj) // 密封
obj.b = 3 // Uncaught TypeError: Cannot add property b, object is not extensible
console.log(obj.b)
Object.defineProperty(obj, 'b', {
value: 3
}) // Cannot define property b, object is not extensible
console.log(obj.b)
不可扩展
4、冻结
Object.freeze(),会创建一个冻结对象,这个方法实际上会在一个现有对象上调用Object.seal(),并把所有现有属性标记为writable: false,这样就无法修改它们的值。
'use strict'
let obj = {a: 1}
Object.freeze(obj) // 冻结
obj.a = 2 // 报错
console.log(obj.a)
Object.defineProperty(obj, 'a', {
value: 3
}) // 报错
console.log(obj.a)
Object.defineProperty(obj, 'a', {
value: 4,
configurable: true
}) // 报错
console.log(obj.a)
三者对比:
禁止扩展: 可修改原属性的值、可配置原属性、不可添加新属性
密封:可修改原属性值、不可配置原属性、不可添加新属性
冻结:不可修改原属性值、不可配置原属性、不可添加新属性
最后来尝试用defineProperty来实现数据双向绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
let data = {}
Object.defineProperty(data, 'val', {
get: function () {
return data.val
},
set: function (val) {
document.getElementsByClassName('input')[0].value = val
document.getElementsByClassName('showBox')[0].innerHTML = val
}
})
function change() {
data.val = (Math.random(0, 100) * 100).toFixed(0)
}
function inputChange(e) {
data.val = e.target.value
}
</script>
</head>
<body>
<div>
<input class="input" type="text" oninput="inputChange(event)">
<div class="showBox"></div>
<div onClick="change()">随机变化</div>
</div>
</body>
</html>
网友评论