何时使用 Object.defeProperty()

我在想我什么时候该用

Object.defineProperty

为一个对象创建新的属性。我知道我可以设置

enumerable: false

但是你什么时候真正需要这个呢? 如果你只是设置一个属性,如

myObject.myprop = 5;

它的描述都是真的,对吗?其实我更好奇的是你们用那个冗长的电话。DefeProperty ()以及出于什么原因。

18414 次浏览

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]"

像“可枚举”这样的特性在我的经验中很少使用。 主要的用例是计算属性:

var myObj = {};


myObj.width = 20;
myObj.height = 20;


Object.defineProperty(myObj, 'area', {
get: function() {
return this.width*this.height;
}
});
console.log(myObj.area);

使用 Object.defeProperty 的一个很好的理由是,它允许您以计算属性的形式遍历对象中的一个函数,它执行函数而不是返回函数的主体。

例如:

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;}

确保将可枚举属性设置为 true,以便循环访问该属性。

@ Gerard Simpson

如果“ area”应该是可枚举的,那么也可以不使用 Object.defeProperty 来编写它。

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

我在 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;
},
});

此代码的来源: https://github.com/apollographql/react-apollo/blob/ddd3d8faabf135dca691d20ce8ab0bc24ccc414e/src/graphql.tsx#L510

一个很好的用法是当你需要进行一些拦截或者以一种优雅的方式应用一个经典的观察者/观察者模式:

Https://www.monterail.com/blog/2016/how-to-build-a-reactive-engine-in-javascript-part-1-observable-objects

摘要:

在 Javascript 对象中是键-值对的集合。 Object.defineProperty()是一个函数,它可以在对象上定义一个新的属性,并可以设置属性的下列属性:

  • <any>: 与键关联的值
  • 可写 <boolean>: 如果可写被设置为 true属性可以通过赋予一个新值来更新。如果设置为 false,则不能更改该值。
  • 可枚举 <boolean>: 如果可枚举设置为 true属性,可以通过 for..in循环访问。而且是 Object.keys()返回的唯一可枚举属性键
  • 可配置的 <boolean>: 如果可配置的设置为 false,那么您就不能更改属性属性(value/writable/枚举/configable) ,也因为您不能更改值,所以不能使用 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

使用这种方法,您只分配给该特定的对象级别(而不是原型链中的任何键)。

例如: 有一个对象像 {key1: value1, key2: value2},你不知道确切的原型链,或错过了它,有一些属性’颜色’在原型链的某个地方,然后-

使用点(.)赋值-

这个操作将为原型链 中的键‘ color’赋值(如果键存在于某个地方) ,并且您将发现对象没有更改为。 Color = ‘ blue’;//obj 保持与{ key1: value1,key2: value2}相同

使用 Object.defeProperty 方法 -

Object.defineProperty(obj, 'color', {
value: 'blue'
});

//现在 obj 看起来像 {key1: value1, key2: value2, color: 'blue'}。它将属性添加到相同的级别。然后可以使用方法 Object.hasOwnProperty()安全地进行迭代。

例如,这就是 Vue.js 跟踪 data对象变化的方法:

当您将一个普通的 JavaScript 对象作为其 data选项传递给 Vue 实例时,Vue 将遍历其所有属性并使用 Object.defineProperty将它们转换为 getter/setters。这是一个仅支持 ES5且不可移动的特性,这就是 Vue 不支持 IE8及以下版本的原因。

Getter/setter 对于用户来说是不可见的,但是在底层,它们使 Vue 能够在访问或修改属性时执行依赖关系跟踪和更改通知。

[...]

请记住,即使是超级苗条和基本版本的 Vue.js 也会使用一些不仅仅是 Object.defineProperty的东西,但主要功能来自于它:

Vue.js's Reactivity Cycle

这里您可以看到一篇文章,其中作者实现了类似 Vue.js: https://medium.com/js-dojo/understand-vue-reactivity-implementation-step-by-step-599c3d51cd6c的最小 PoC 版本

这里有一个演讲(用西班牙语) ,演讲者在用 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

这个答案与这里的一些其他答案有关,这些答案是获取更多信息的很好的步骤点,但是不需要通过外部链接来阅读有关库或编程范例的内容。