Object.defineProperty(obj, prop, descriptor)
在对象 obj 上新建或修改一个属性 prop, 并返回新 obj
obj: 要定义属性的对象
prop: 要定义或修改的属性的名称或 Symbol
descriptor: 要定义或修改的属性的描述符
descriptor.configurable: 对象的属性的描述符的特性
属性的 descriptor (描述符)分两种[数据描述符, 存取描述符(访问描述符)], descriptor 只能是其中一种;
descriptor 为数据描述符时, 该属性称为数据属性;为存取描述符时, 该属性称为存取属性;
数据属性可以用 [对象.属性 = 值] 方式定义;
存取属性只能通过 Object.defineProperty() 来定义;
descriptor 的属性称为特性, 共[configurable, enumerable, value, writable, get, set]6个, 但这6个不可以共存;
// [对象.属性 = 值] 方式的 数据描述符 的特性的默认值
descriptor = {
configurable: true,
enumerable: true,
value: undefined,
writable: true
}
// defineProperty 方式的 数据描述符 的特性的默认值
descriptor = {
configurable: false,
enumerable: false,
value: undefined,
writable: false
}
// 存取描述符 的特性的默认值
descriptor = {
configurable: false,
enumerable: false,
get: undefined,
set: undefined
}
特性使用条件:
- configurable, enumerable 是公共特性
- value, writable 属于数据描述符的特性
- get, set 属于存取描述符的特性
- descriptor 有[value, writable] 时, 即为数据描述符;有[get, set] 时, 即为存取描述符
- descriptor 为空时, 默认为数据描述符
- [value, writable] 与 [get, set] 不能同时存于一个 descriptor, 否则异常
公用可选键值(特性):
configurable
当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。
默认为 false。
enumerable
当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。
默认为 false。
数据描述符还具有以下可选键值(特性):
value
该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。
默认为 undefined。
writable
当且仅当该属性的 writable 键值为 true 时,属性的值,也就是上面的 value,才能被赋值运算符改变。
默认为 false。
存取描述符还具有以下可选键值(特性):
get
属性的 getter 函数,如果没有 getter,则为 undefined。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。
默认为 undefined。
set
属性的 setter 函数,如果没有 setter,则为 undefined。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。
默认为 undefined。
详解见下面demo
<html>
<head>
<title>js Object.defineProperty() 描述符的特性功能详解</title>
</head>
<body>
<script type="text/javascript">
"use strict"
function section1() {
let _obj = {
}
let descriptor = Object.create(null) // 相当于没有 __proto__ 的 {}
descriptor.value = 1
// 默认 false, 写不写都一样
// descriptor.writable = false
// descriptor.enumerable = false
// descriptor.configurable = false
Object.defineProperty(_obj, "val", descriptor);
console.log("_obj: 对象")
console.log("\"val\": 对象的属性名")
console.log("descriptor: 对象的属性的描述符")
console.log("descriptor.value: 对象的属性的描述符的特性")
}
function section2() {
let _obj = {
}
Object.defineProperty(_obj, "val", {
value: "val 未修改",
// writable: false,
// enumerable: false,
configurable: false
});
Object.defineProperty(_obj, "val2", {
value: "val2 未修改",
// writable: false,
// enumerable: false,
configurable: true
});
try {
Object.defineProperty(_obj, "val", {
value: "val 修改",
});
} catch (error) {
console.warn("[val] configurable: false, 该属性的描述符不可以修改")
}
Object.defineProperty(_obj, "val2", {
value: "val2 修改",
});
try {
delete _obj.val
} catch (error) {
console.warn("[val] configurable: false, 该属性不可以被 delete")
}
console.log(_obj.val) // val 未修改
console.log(_obj.val2) // val2 修改
delete _obj.val2
console.log(_obj.val2) // undefined
// val2 被 delete 了
// 描述符不可以修改指, 除了 writable: true 改 false, [value, writable, configurable, enumerable, get, set] 都不可以修改
// 前后值相同不算"修改"
}
function section3() {
let _obj = {
}
Object.defineProperty(_obj, "val", {
value: "val 未修改",
writable: false,
// enumerable: false,
// configurable: false,
});
try {
_obj.val = "val 修改"
} catch (error) {
console.warn("[val] writable: false, 该属性不能被重新赋值")
}
console.log(_obj.val) // val 未修改
}
function section4() {
let _obj = {
}
Object.defineProperty(_obj, "val", {
value: "val enumerable: false",
// writable: false,
enumerable: false,
// configurable: false,
});
Object.defineProperty(_obj, "val2", {
value: "val2 enumerable: true",
// writable: false,
enumerable: true,
// configurable: false,
});
for (var i in _obj) {
console.log(_obj[i]);
}
console.log(Object.keys(_obj)) // ["val2"]
_obj.propertyIsEnumerable("val") // false
_obj.propertyIsEnumerable("val2") // true
console.log("[val] enumerable: false, 该属性不能被[for in], Objeck.keys() 等循环方法枚举")
}
function section5() {
let _obj = {
_val: null,
_val2: null,
}
Object.defineProperty(_obj, "val", {
get() {
console.log("get")
console.warn("在 get 中调用本属性, 会进入死循环")
return this._val;
},
set(newValue) {
console.log("set", newValue)
this._val = newValue;
},
// enumerable: false,
// configurable: false
});
// 赋值触发 set, 新值作为参数传入
_obj.val = 1
// 调用触发 get
_obj.val
}
function section6() {
let _obj = {
}
var _val = Object.defineProperty(_obj, "val", {
});
// 描述符为空时, 默认为数据描述符, { configurable: false, enumerable: false, value: undefined, writable: false }
console.log(Object.getOwnPropertyDescriptor(_obj, "val"))
Object.defineProperty(_obj, "val2", {
get() {
}
});
// get, set 默认为 undefined
console.log(Object.getOwnPropertyDescriptor(_obj, "val2"))
_obj.val3 = 3
// 直接赋值的属性 [configurable, enumerable, writable] 为 true
console.log(Object.getOwnPropertyDescriptor(_obj, "val3"))
_obj.__proto__.val4 = 4
// getOwnPropertyDescriptor 只能取自有属性, 也就是不能取原型链 __proto__ 上的属性
console.log(Object.getOwnPropertyDescriptor(_obj, "val4"))
}
section1()
section2()
section3()
section4()
section5()
section6()
</script>
</body>
</html>
参考资料:
Object.defineProperty() - JavaScript | MDN
end