Object.defineProperty() 和 Object.defineProperties()

2,728 阅读6分钟

1. Object.defineProperty()

首先看 MDN 中是如何定义的

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

语法:Object.defineProperty(obj, prop, descriptor)

参数:

  • obj 要定义属性的对象。
  • prop 要定义或修改的属性的名称或 Symbol 。
  • descriptor 要定义或修改的属性描述符。

返回值: 被传递给函数的对象。

let object1 = {};

Object.defineProperty(object1, 'property1', {
  value: 42,
  writable: false
});

object1.property1 = 77;

console.log(object1.property1);
// 42

对象里目前存在的属性描述符有两种主要形式:数据描述符存取描述符

注意:一个描述符只能是这两者其中之一,不能同时是两者。

数据描述符具有以下可选键值:

  • value:该属性对应的值。可以是任何有效的JavaScript值(数值,对象,函数等)。 默认为 undefined。
  • writable:当 writable 属性设置为 false 时,该属性被称为“不可写的”。它不能被重新赋值。 默认为 false不可修改。

举个例子🌰就知道了:

let obj = {}

Object.defineProperty(obj,'a',{
    value'666',
    writablefalse
});

delete obj.a;  // false
obj.a = '333'
obj.a  // '666'

从上面例子可以看出,obj.a 不可以被删除也不可以被修改。

存取描述符具有以下可选键值:

  • get:属性的 getter 函数,如果没有 getter,则为 undefined。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。 默认为 undefined。
  • set:属性的 setter 函数,如果没有 setter,则为 undefined。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。 默认为 undefined。

共享键值(默认值是指在使用 Object.defineProperty() 定义属性时的默认值):

  • configurable:当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除(不能保证该属性能被修改,只有当 writable 为 true 时,才能修改该属性)。默认为 false。(configurable属性控制了属性描述对象的可写性。为false时不能修改属性描述对象)
  • enumerable:enumerable 定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举。 默认为 false。

举个例子🌰:

let obj = {name: 'xm', age: 18}

Object.defineProperty(obj,'a',{
    value'666',
    enumerablefalse
});

for (i in obj){
    console.log(i)
}
// name
// age

Object.keys(obj)  // ["name", "age"]

Object.defineProperty(obj,'b',{
    value'666',
    configurabletrue
});

obj.b = '333'

obj.b  // '666'

delete obj.b  // true

//  可以修改描述符对象中的属性
Object.defineProperty(obj,'b',{
    value'666',
    configurabletrue,
    writabletrue
});

obj.b = '333'
obj.b  // '333'

// 将属性c的描述符对象设为不可修改
Object.defineProperty(obj,'c',{
    value'666',
    configurablefalse,
    writabletrue
});

// 可以将 writable: true 修改为 false,反过来不行,enumerable不能修改
Object.defineProperty(obj,'c',{
    configurablefalse,
    writablefalse
});

// 会报错
Object.defineProperty(obj,'c',{
    configurablefalse,
    writabletrue
});

「描述符默认值」:

① 拥有布尔值的键 configurableenumerablewritable 的默认值都是 false

② 属性值和函数的键 valuegetset 字段的默认值为 undefiend

注意:如果一个描述符不具有 value、writable、get 和 set 中的任意一个键,那么它将被认为是一个数据描述符。如果一个描述符同时拥有 value 或 writable 和 get 或 set 键,则会产生一个异常。

也就是属性描述符只能有两种形式:

let Person = {};

Object.defineProperty(Person'age', {
    value15,
    writablefalse,   // 是否可以修改
    configurablefalse,  //是否可配置
    enumerabletrue   //是否可被枚举
})
let Person2 = {};

Object.defineProperty(Person2'name', {
    get() {
        return name
    },
    set(val) {
        name = val
    },
    enumerablefalse,  //是否可配置
})

我们还可以自定义 SettersGetters

下面的例子展示了如何实现一个自存档对象。当设置temperature 属性时,archive 数组会收到日志条目。

function Archiver() {
  var temperature = null;
  var archive = [];

  Object.defineProperty(this, 'temperature', {
    get: function() {
      console.log('get!');
      return temperature;
    },
    set: function(value) {
      temperature = value;
      archive.push({ val: temperature });
    }
  });

  this.getArchive = function() { return archive; };
}

var arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]

还可以简单的实现一个 Vue 双向数据绑定。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <input type="text" id="a" />
      <span id="b"></span>
    </div>
  </body>
  <script>
    var obj = {}; //定义一个空对象

    var val = "zhao"//赋予初始值

    Object.defineProperty(obj, "val", {
      //定义要修改对象的属性
      getfunction () {
        return val;
      },
      setfunction (newVal) {
        val = newVal; //定义val等于修改后的内容

        document.getElementById("a").value = val; //让文本框的内容等于val
        document.getElementById("b").innerHTML = val; //让span的内容等于val
      },
    });
    document.addEventListener("keyup"function (e) {
      //当在文本框输入内容时让对象里你定义的val等于文本框的值
      obj.val = e.target.value;
    });
  </script>
</html>

2. Object.defineProperties()

MDN定义:Object.defineProperties() 方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。

语法:Object.defineProperties(obj, props)

参数与 Objec.defineProperty() 相似。

  • obj 在其上定义或修改属性的对象。
  • props 要定义其可枚举属性或修改的属性描述符的对象。对象中存在的属性描述符主要有两种:数据描述符和访问器描述符(更多详情,请参阅Object.defineProperty())。

描述符与 Objec.defineProperty() 相同

返回值:传递给函数的对象。

从语法和参数上来看 Objec.defineProperty() 与 Object.defineProperties 十分相似,区别是一个只能定义一个属性,一个可以定义多个属性。

例子:

var obj = {};
Object.defineProperties(obj, {
  'property1': {
    valuetrue,
    writabletrue
  },
  'property2': {
    value'Hello',
    writablefalse
  }
});