Javascript 什么时候使用Object.defineProperty()

Javascript 什么时候使用Object.defineProperty(),javascript,ecmascript-5,Javascript,Ecmascript 5,我在想我什么时候该用 Object.defineProperty 为对象创建新特性的步骤。我知道我可以设置如下内容 enumerable: false 但你什么时候真的需要这个?如果您只设置一个属性,如 myObject.myprop = 5; 它的描述符都设置为true,对吗?实际上,我更好奇的是,当你们使用对.defineProperty()的那个相当冗长的调用时,是出于什么原因。主要用于设置具有特定属性描述符的属性(例如只读(常量)、可枚举性(不在循环中显示属性、getter、set

我在想我什么时候该用

Object.defineProperty
为对象创建新特性的步骤。我知道我可以设置如下内容

enumerable: false
但你什么时候真的需要这个?如果您只设置一个属性,如

myObject.myprop = 5;
它的描述符都设置为true,对吗?实际上,我更好奇的是,当你们使用对.defineProperty()的那个相当冗长的调用时,是出于什么原因。

主要用于设置具有特定属性描述符的属性(例如只读(常量)、可枚举性(不在循环中显示属性、getter、setter)

例子 此方法使用属性扩展
对象
原型。仅定义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.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
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;
    },
  });
与将函数作为属性添加到对象文字相比:

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,以便循环使用它。

@Gerard Simpson

如果“区域”应该是可枚举的,那么它也可以在没有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

我所看到的
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
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;
    },
  });
让logErrorTimeoutId=setTimeout(()=>{
如果(错误){
console.error('Unhandled(in'),error.stack | | error);
}
}, 10);
Object.defineProperty(数据“错误”{
对,,
可枚举:正确,
获取:()=>{
clearTimeout(logErrorTimeoutId);
返回误差;
},
});

此代码的源代码:

当您需要执行一些拦截或以优雅的方式应用经典的观察者/可观察模式时,一个很好的用途是:

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

  • 与键关联的值
  • writable
    如果writable设置为
    true
    ,则可以通过为其分配新值来更新该属性。如果设置为
    false
    ,则无法更改该值
  • enumerable
    如果enumerable设置为
    true,则可以通过
    for..在
    循环中访问
    属性。此外,与
    Object.keys()一起返回的可枚举属性键是唯一的
  • 可配置
    如果可配置设置为
    false
    ,则无法更改属性属性属性(值/可写/可枚举/可配置),同时,由于无法更改值,因此无法使用
    删除
    操作符将其删除
例子:
让obj={};
Object.defineProperty(对象“prop1”{
价值:1,
可写:false,
可枚举:false,
可配置:false
});//创建一个新属性(key=prop1,value=1)
Object.defineProperty(obj,“prop2”{
价值:2,
可写:对,
可枚举:正确,
可配置:true
});//创建一个新属性(key=prop2,value=2)
console.log(obj.prop1,obj.prop2);//两个prop都存在
用于(obj中的常量道具){
控制台日志(道具);
//只记录prop2,因为可写在prop2中为true,在prop1中为false
}
obj.prop1=100;
obj.prop2=100;
控制台日志(obj.prop1,obj.prop2);
//只更改了prop2,因为prop2可写,而prop1不可写
删除obj.prop1;
删除obj.prop2;
控制台日志(obj.prop1,obj.prop2);

//仅删除prop2,因为prop2是可配置的,而prop1不是对象。defineProperty可防止您意外地将值分配给其原型链中的某个键。。使用此方法,您只能将值分配给该特定对象级别(而不是原型链中的任何键)

例如: 有一个像
{key1:value1,key2:value2}
这样的对象,您不知道它的原型链,或者错误地错过了它,并且原型链中的某个地方有一些属性“color”-

使用点(.)赋值-

此操作将为原型链中的键“颜色”赋值(如果某个地方存在键),您将发现对象没有变化。 obj.color='blue';//obj保持与{key1:value1,key2:value2}相同

使用Object.defineProperty方法-

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

当您将普通JavaScript对象作为其
数据
选项传递给Vue实例时,Vue将遍历其所有属性,并使用
对象将其转换为
getter/setters
。defineProperty
。这是一个仅限ES5且不可填充的功能,这就是Vue不支持IE8及以下版本的原因

getter/setter对用户是不可见的,但在后台,它们使Vue能够在访问或修改属性时执行依赖项跟踪和更改通知

[……]

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

在这里,您可以看到一篇文章,作者实现了Vue.js之类的最低PoC版本:

这里是一个演讲(西班牙语),演讲者在解释Vue.js中的反应时构建了类似的内容:

非常有用