【发布时间】:2012-04-23 18:01:50
【问题描述】:
我想知道什么时候应该使用
Object.defineProperty
为对象创建新属性。我知道我可以设置诸如
enumerable: false
但是你什么时候真的需要这个?如果你只是设置一个属性像
myObject.myprop = 5;
它的描述符都设置为真,对吧?当你们对 .defineProperty() 使用这种相当冗长的调用以及出于什么原因时,我实际上更好奇。
【问题讨论】:
我想知道什么时候应该使用
Object.defineProperty
为对象创建新属性。我知道我可以设置诸如
enumerable: false
但是你什么时候真的需要这个?如果你只是设置一个属性像
myObject.myprop = 5;
它的描述符都设置为真,对吧?当你们对 .defineProperty() 使用这种相当冗长的调用以及出于什么原因时,我实际上更好奇。
【问题讨论】:
我见过的defineProperty 的一个简洁用例是库向用户提供错误属性,如果在特定时间间隔内未访问该属性,您将自己记录错误。例如:
let logErrorTimeoutId = setTimeout(() => {
if (error) {
console.error('Unhandled (in <your library>)', error.stack || error);
}
}, 10);
Object.defineProperty(data, 'error', {
configurable: true,
enumerable: true,
get: () => {
clearTimeout(logErrorTimeoutId);
return error;
},
});
【讨论】:
例如that's how Vue.js keeps track of changes in the data object:
当您将纯 JavaScript 对象作为其
data选项传递给 Vue 实例时,Vue 将遍历其所有属性并使用Object.defineProperty将它们转换为getter/setters。这是一个仅限 ES5 且不可填充的功能,这就是 Vue 不支持 IE8 及更低版本的原因。getter/setter 对用户是不可见的,但在底层,它们使 Vue 能够在访问或修改属性时执行依赖关系跟踪和更改通知。
[...]
请记住,即使是 Vue.js 的超薄和基本版本也不仅仅使用Object.defineProperty,而是主要功能来自它:
在这里你可以看到一篇文章,作者实现了类似 Vue.js 的最小 PoC 版本:https://medium.com/js-dojo/understand-vue-reactivity-implementation-step-by-step-599c3d51cd6c
这里有一个演讲(西班牙语),演讲者在解释 Vue.js 中的反应性时构建了类似的东西:https://www.youtube.com/watch?v=axXwWU-L7RM
【讨论】:
一个非常有用的案例是监控某物的变化并对其采取行动。这很容易,因为您可以在设置值时触发回调函数。这是一个基本示例。
您有一个对象Player 可以播放或不播放。您希望在开始播放和停止播放时立即发生某些事情。
function Player(){}
Object.defineProperty(Player.prototype, 'is_playing', {
get(){
return this.stored_is_playing; // note: this.is_playing would result in an endless loop
},
set(newVal){
this.stored_is_playing = newVal;
if (newVal === true) {
showPauseButton();
} else {
showPlayButton();
}
}
});
const cdplayer = new Player();
cdplayer.is_playing = true; // showPauseButton fires
此答案与此处的其他几个答案相关,这是获取更多信息的良好步骤,但无需通过外部链接来阅读有关库或编程范例的信息。
【讨论】:
Object.defineProperty 防止您意外地将值分配给原型链中的某个键。使用此方法,您只分配给该特定对象级别(而不是原型链中的任何键)。
例如:
有一个像{key1: value1, key2: value2} 这样的对象,你不知道它的原型链,或者你错误地错过了它,然后原型链中的某个地方有一些属性“颜色”-
使用点(.)赋值-
此操作将为原型链中的 key 'color' 赋值(如果 key 存在于某处),您会发现对象没有任何变化,如 . obj.color='蓝色'; // obj 与 {key1: value1, key2: value2} 保持一致
使用 Object.defineProperty 方法-
Object.defineProperty(obj, 'color', {
value: 'blue'
});
// 现在 obj 看起来像 {key1: value1, key2: value2, color: 'blue'}。它将属性添加到同一级别。然后您可以使用方法 Object.hasOwnProperty() 安全地迭代。
【讨论】:
在 Javascript 中,对象是键值对的集合。
Object.defineProperty() 是一个函数,它可以在对象上定义一个新的属性,并且可以设置一个属性的以下属性:
<any>:与键关联的值<boolean>: 如果 writable 设置为 true 可以通过为其分配新值来更新该属性。如果设置为 false,则无法更改该值。<boolean>: 如果 enumerable 设置为 true 属性可以通过 for..in 循环访问。此外,Object.keys() 返回的唯一可枚举属性键
<boolean>: 如果可配置设置为 false,则无法更改属性属性(值/可写/可枚举/可配置),因为您无法更改值,因此无法使用delete 运营商。let obj = {};
Object.defineProperty(obj, 'prop1', {
value: 1,
writable: false,
enumerable: false,
configurable: false
}); // create a new property (key=prop1, value=1)
Object.defineProperty(obj, 'prop2', {
value: 2,
writable: true,
enumerable: true,
configurable: true
}); // create a new property (key=prop2, value=2)
console.log(obj.prop1, obj.prop2); // both props exists
for(const props in obj) {
console.log(props);
// only logs prop2 because writable is true in prop2 and false in prop1
}
obj.prop1 = 100;
obj.prop2 = 100;
console.log(obj.prop1, obj.prop2);
// only prop2 is changed because prop2 is writable, prop1 is not
delete obj.prop1;
delete obj.prop2;
console.log(obj.prop1, obj.prop2);
// only prop2 is deleted because prop2 is configurable and prop1 is not
【讨论】:
当您需要进行一些拦截或以优雅的方式应用经典的 Observer/Observable 模式时,这是一个很好的用途:
【讨论】:
t3chfest 就曾有过一次有趣的讨论@?
@杰拉德·辛普森
如果 'area' 应该是可枚举的,它也可以在没有 Object.defineProperty 的情况下编写。
var myObj = {
get area() { return this.width * this.height }
};
myObj.width = 20;
myObj.height = 20;
for (var key in myObj) {
if (myObj.hasOwnProperty(key)) {
console.log(key + " -> " + myObj[key]);
}
}
//area -> 400, width -> 20, height -> 20
【讨论】:
使用 Object.defineProperty 的一个很好的理由是,它允许您循环遍历对象中的函数作为计算属性,它执行函数而不是返回函数的主体。
例如:
var myObj = {};
myObj.width = 20;
myObj.height = 20;
Object.defineProperty(myObj, 'area', {
get: function() {
return this.width*this.height;
},
enumerable: true
});
for (var key in myObj) {
if (myObj.hasOwnProperty(key)) {
console.log(key + " -> " + myObj[key]);
}
}
//width -> 20, height -> 20, area -> 400
与将函数作为属性添加到对象字面量:
var myObj = {};
myObj.width = 20;
myObj.height = 20;
myObj.area = function() {
return this.width*this.height;
};
for (var key in myObj) {
if (myObj.hasOwnProperty(key)) {
console.log(key + " -> " + myObj[key]);
}
}
// width -> 20, height -> 20, area -> function() { return this.width*this.height;}
确保将 enumerable 属性设置为 true 以便循环遍历它。
【讨论】:
在我的经验中,很少使用像“可枚举”这样的功能。 主要用例是计算属性:
var myObj = {};
myObj.width = 20;
myObj.height = 20;
Object.defineProperty(myObj, 'area', {
get: function() {
return this.width*this.height;
}
});
console.log(myObj.area);
【讨论】:
Object.defineProperty 主要用于设置具有特定属性描述符的属性(例如只读(常量)、可枚举性(在for (.. in ..) 循环中不显示属性、getter、setter)。
"use strict";
var myObj = {}; // Create object
// Set property (+descriptor)
Object.defineProperty(myObj, 'myprop', {
value: 5,
writable: false
});
console.log(myObj.myprop);// 5
myObj.myprop = 1; // In strict mode: TypeError: myObj.myprop is read-only
此方法使用属性扩展Object 原型。只定义了getter,可枚举性设置为false。
Object.defineProperty(Object.prototype, '__CLASS__', {
get: function() {
return Object.prototype.toString.call(this);
},
enumerable: false // = Default
});
Object.keys({}); // []
console.log([].__CLASS__); // "[object Array]"
【讨论】:
.defineProperty创建getters和setters。
for (.. in ..) 循环中公开:Object.prototype.__defineGetter__('lol',function(){return 3});for(var i in [])alert(i); 显示"lol"。 Object.defineProperty 可用于定义一个getter/setter,也 不 出现在for (.. in ..) 循环中。
var foo = { get lol() { return 5;} };,但问题仍然存在。