defineProperty AND defineProperties

Object.defineProperty(obj, prop, descriptor) 该要领许可准确增加或修正对象的属性。经由过程赋值来增加的平常属性会建立在属性罗列时期展现的属性(for...in Object.keys 要领), 这些值可以被转变,也可以被删除。这类要领许可这些分外的细节从默许值转变。默许情况下,运用Object.defineProperty()增加的属性值是不可变的。

`Object.defineProperties(obj, props)`、` Object.defineProperties`本质上定义了`obj` 对象上`props`的`可罗列属性`相对应的一切属性。
`Object.defineProperties(obj, props)`完成

function defineProperties(obj, properties) {
  function convertToDescriptor(desc) {
    function hasProperty(obj, prop) {
      return Object.prototype.hasOwnProperty.call(obj, prop);
    }

    function isCallable(v) {
      // NB: modify as necessary if other values than functions are callable.
      return typeof v === 'function';
    }

    if (typeof desc !== 'object' || desc === null)
      throw new TypeError('bad desc');

    var d = {};

    if (hasProperty(desc, 'enumerable'))
      d.enumerable = !!desc.enumerable;
    if (hasProperty(desc, 'configurable'))
      d.configurable = !!desc.configurable;
    if (hasProperty(desc, 'value'))
      d.value = desc.value;
    if (hasProperty(desc, 'writable'))
      d.writable = !!desc.writable;
    if (hasProperty(desc, 'get')) {
      var g = desc.get;

      if (!isCallable(g) && typeof g !== 'undefined')
        throw new TypeError('bad get');
      d.get = g;
    }
    if (hasProperty(desc, 'set')) {
      var s = desc.set;
      if (!isCallable(s) && typeof s !== 'undefined')
        throw new TypeError('bad set');
      d.set = s;
    }

    if (('get' in d || 'set' in d) && ('value' in d || 'writable' in d))
      throw new TypeError('identity-confused descriptor');

    return d;
  }

  if (typeof obj !== 'object' || obj === null)
    throw new TypeError('bad obj');

  properties = Object(properties);

  var keys = Object.keys(properties);
  var descs = [];

  for (var i = 0; i < keys.length; i++)
    descs.push([keys[i], convertToDescriptor(properties[keys[i]])]);

  for (var i = 0; i < descs.length; i++)
    Object.defineProperty(obj, descs[i][0], descs[i][1]);

  return obj;
}

### 参数

`obj `在其上定义或修正属性的对象。
`prop` 要定义或修正的属性的称号。
`descriptor` 将被定义或修正的属性形貌符。
`返回值 ` `被传递给函数的对象`。
`props` 要定义其可罗列属性或修正的属性形貌符的对象。对象中存在的属性形貌符重要有两种:`数据形貌符`和`接见器形貌符`。形貌符具有以下键:
数据形貌符和存取形貌符均具有以下可选键值:
对象里如今存在的属性形貌符有两种重要情势:数据形貌符和存取形貌符。数据形貌符是一个具有值的属性,该值多是可写的,也可以不是可写的。`存取形貌符`是由`getter-setter`函数对形貌的属性。形貌符必需是这两种情势之一;不能同时是二者。
  `configurable` true 当且仅当该属性形貌符的范例可以被转变而且该属性可以从对应对象中删除。`默许为 false`
  `enumerable` true 当且仅当在罗列响应对象上的属性时该属性展现。` 默许为 false`  for ... in obj  obj.keys() 完成内部道理
数据形貌符同时具有以下可选键值:
  `value` 与属性关联的值。可以是任何有用的JavaScript值(数字,对象,函数等)。 ` 默许为 undefined`.
  `writable` true当且仅当与该属性相关联的值可以用assignment operator转变时。 `默许为 false`  当须要些一些不可以被变动的属性时可以运用
存取形貌符同时具有以下可选键值:
  `get `作为该属性的 getter 函数,假如没有 getter 则为undefined。函数返回值将被用作属性的值。默许为 undefined
  `set `作为属性的 setter 函数,假如没有 setter 则为undefined。函数将仅接收参数赋值给该属性的新值。默许为 undefined

假如一个形貌符不具有value,writable,get 和 set 恣意一个关键字,那末它将被以为是一个数据形貌符。假如一个形貌符同时有(value或writable)和(get或set)关键字,将会发生一个非常。


记着,这些选项不一定是本身属性,假如是继续来的也要斟酌。为了确认保存这些默许值,你可以要在这之前凝结 Object.prototype,明白指定一切的选项,或许将__proto__属性指向null。
   运用 __proto__
   var obj = {};
   var descriptor = Object.create(null); // 没有继续的属性
   // 默许没有 enumerable,没有 configurable,没有 writable
   descriptor.value = 'static';
   Object.defineProperty(obj, 'key', descriptor);

   // 显式
   Object.defineProperty(obj, "key", {
     enumerable: false,
     configurable: false,
     writable: false,
     value: "static"
   });

   // 轮回运用统一对象
   function withValue(value) {
     var d = withValue.d || (
       withValue.d = {
         enumerable: false,
         writable: false,
         configurable: false,
         value: null
       }
     );
     d.value = value;
     return d;
   }
   // ... 而且 ...
   Object.defineProperty(obj, "key", withValue("static")); 
   // 假如 freeze 可用, 防备代码增加或删除对象原型的属性
   // (value, get, set, enumerable, writable, configurable)
   (Object.freeze||Object)(Object.prototype);
###作用:
☆建立属性
当形貌符中省略某些字段时,这些字段将运用它们的默许值。具有布尔值的字段的默许值都是false。value,get和set字段的默许值为undefined。一个没有get/set/value/writable定义的属性被称为“通用的”,并被“键入”为一个数据形貌符。
```
var o = {}; // 建立一个新对象

在对象中增加一个属性与数据形貌符的示例
Object.defineProperty(o, "a", {
  value : 37,
  writable : true,
  enumerable : true,
  configurable : true
});

// 对象o具有了属性a,值为37

// 在对象中增加一个属性与存取形貌符的示例
var bValue;
Object.defineProperty(o, "b", {
  get : function(){
    return bValue;
  },
  set : function(newValue){
    bValue = newValue;
  },
  enumerable : true,
  configurable : true
});
o.b = 38;
// 对象o具有了属性b,值为38
// 转变bValue也没法转变o.b
console.log(bValue);
  
// o.b的值如今老是与bValue雷同,除非从新定义o.b

// 数据形貌符和存取形貌符不能混合运用
Object.defineProperty(o, "conflict", {
  value: 0x9f91102, 
  get: function() { 
    return 0xdeadbeef; 
  } 
});
throws a TypeError: value appears only in data descriptors, get appears only in accessor descriptors
```
☆修正属性
假如属性已存在,`Object.defineProperty()`将尝试依据形貌符中的值以及对象当前的设置来修正这个属性。假如旧形貌符将其`configurable` 属性设置为`false`,则该属性被以为是`“不可设置的”`,而且没有属性可以被转变(除了单向转变 `writable` 为 `false`)。当属性不可设置时,不能在数据和接见器属性范例之间切换。
当试图转变不可设置属性(除了writable 属性以外)的值时会抛出{jsxref("TypeError")}},除非当前值和新值雷同。
1 `Writable` 属性 当writable属性设置为false时,该属性被称为“不可写”。它不能被从新分配。 试图写入非可写属性不会转变它,也不会激发毛病。
```
var o = {}; // Creates a new object

Object.defineProperty(o, 'a', {
  value: 37,
  writable: false
});

console.log(o.a); // logs 37
o.a = 25; // No error thrown
// (it would throw in strict mode,
// even if the value had been the same)
console.log(o.a); // logs 37. The assignment didn't work.

// strict mode
(function() {
  'use strict';
  var o = {};
  Object.defineProperty(o, 'b', {
    value: 2,
    writable: false
  });
  o.b = 3; // throws TypeError: "b" is read-only
  return o.b; // returns 2 without the line above
}());
   
  2 `Enumerable `特征 enumerable定义了对象的属性是不是可以在 for...in 轮回和 Object.keys() 中被罗列默许是true。
  var o = {};
  Object.defineProperty(o, "a", { value : 1, enumerable:true });
  Object.defineProperty(o, "b", { value : 2, enumerable:false });
  Object.defineProperty(o, "c", { value : 3 }); // enumerable defaults to false
  o.d = 4; // 假如运用直接赋值的体式格局建立对象的属性,则这个属性的enumerable为true

  for (var i in o) {    
    console.log(i);  
  }
  打印 'a' 和 'd' (in undefined order)

  Object.keys(o); // ["a", "d"]

  o.propertyIsEnumerable('a'); // true
  o.propertyIsEnumerable('b'); // false
  o.propertyIsEnumerable('c'); // false
  ```
  3 Configurable 特征 configurable特征示意对象的属性是不是可以被删除,以及除writable特征外的其他特征是不是可以被修正。
  假如o.a的configurable属性为true,则不会抛出任何毛病,而且该属性将在末了被删除。
    var o = {};
    Object.defineProperty(o, "a", { get : function(){return 1;},  configurable : false } ); //为true时,不会报错 效果为12  undefined(可以删除)

    // throws a TypeError Cannot redefine property: a
    Object.defineProperty(o, "a", {configurable : true}); 
    // // throws a TypeError
    Object.defineProperty(o, "a", {enumerable : true}); 
    // // throws a TypeError (set was undefined previously) 
    Object.defineProperty(o, "a", {set : function(){}}); 
    // // throws a TypeError (even though the new get does exactly the same thing) 
    Object.defineProperty(o, "a", {get : function(){return 1;}});
    // // throws a TypeError
    Object.defineProperty(o, "a", {value : 12});

    console.log(o.a); // logs 1
    delete o.a; // Nothing happens
    console.log(o.a); // logs 1
```
  ☆  增加多个属性和默许值
  斟酌特征被给予的默许特征值非常重要,平常,运用点运算符和Object.defineProperty()为对象的属性赋值时,数据形貌符中的属性默许值是差别的,如下例所示。
  
  ```
  var o = {};

  o.a = 1;
  // 等同于 :
  Object.defineProperty(o, "a", {
    value : 1,
    writable : true,
    configurable : true,
    enumerable : true
  });


  // 另一方面,
  Object.defineProperty(o, "a", { value : 1 });
  // 等同于 :
  Object.defineProperty(o, "a", {
    value : 1,
    writable : false,
    configurable : false,
    enumerable : false
  });
  ```
  ☆ 平常的 `Setter` 和 `Getters`
  下面的例子展现了怎样完成一个自存档对象。 当设置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 }]
  ```
  // 或
  
  ```
  var pattern = {
      get: function () {
          return 'I alway return this string,whatever you have assigned';
      },
      set: function () {
          this.myname = 'this is my name string';
      }
  };


  function TestDefineSetAndGet() {
      Object.defineProperty(this, 'myproperty', pattern);
  }


  var instance = new TestDefineSetAndGet();
  instance.myproperty = 'test';

  // 'I alway return this string,whatever you have assigned'
  console.log(instance.myproperty);
  // 'this is my name string'
  console.log(instance.myname);
  ```
  
  ☆ ☆兼容性问题
  1 数组的 length 属性重定义是可以的,然则会遭到平常的重定义限定。(length 属性初始为 non-configurable,non-enumerable 以及 writable。关于一个内容稳定的数组,转变其 length 属性的值或许使它变成 non-writable 是可以的。然则转变其可罗列性和可设置性或许当它是 non-writable 时尝试转变它的值或是可写性,这二者都是不许可的。)但是,并非一切的浏览器都许可 Array.length 的重定义。

  在 Firefox 4 至 22 版本中尝试去重定义数组的 length 属性都邑抛出一个 TypeError 非常。

  有些版本的Chrome中,Object.defineProperty() 在某些情况下会疏忽差别于数组当前length属性的length值。有些情况下转变可写性并不起作用(也不抛出非常)。同时,比方Array.prototype.push的一些数组操纵要领也不会斟酌不可读的length属性。

  有些版本的Safari中,Object.defineProperty()  在某些情况下会疏忽差别于数组当前length属性的length值。尝试转变可写性的操纵会一般实行而不抛出毛病,但事实上并未转变属性的可写性。

  只在Internet Explorer 9及今后版本和Firefox 23及今后版本中,才完整地正确地支撑数组length属性的从新定义。如今不要依赖于重定义数组length 属性可以起作用,或在特定情况下起作用。与此同时,纵然你可以依赖于它,你也没有适宜的来由如许做。
  2 Internet Explorer 8 详细案例
  Internet Explorer 8 完成了 Object.defineProperty() 要领,但 只能在 DOM 对象上运用。 须要注重的一些事变:

  尝试在原生对象上运用 Object.defineProperty()会报错。
  属性特征必需设置一些特定的值。关于数据属性形貌符,configurable, enumerable 和 writable 特征必需悉数设置为 true;关于接见器属性形貌符,configurable 必需设置为 true,enumerable 必需设置为 false。(?) 任何试图供应其他值(?)将致使一个毛病抛出。
  从新设置一个属性起首须要删除该属性。假如属性没有删除,就如同从新设置前的尝试。
    原文作者:Just_do
    原文地址: https://segmentfault.com/a/1190000015629643
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞