javascript常用方法 - Object

// 1.Object.assign(target, ...sources)
// 通过复制一个或多个对象来创建一个新的对象。
// 描述:如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性。
{
  //1.1 复制一个对象
  {
    const obj = { a: 1 };
    const copy = Object.assign({}, obj);
    console.log(copy); // { a: 1 }
  }


  //1.2 合并具有相同属性的对象(覆盖)
  {
    const o1 = { a: 1, b: 1, c: 1 };
    const o2 = { b: 2, c: 2 }; //覆盖 b,c
    const o3 = { c: 3 }; // 再覆盖 c

    const obj = Object.assign({}, o1, o2, o3);
    console.log(obj); // { a: 1, b: 2, c: 3 }
  }

  // 1.3 拷贝 symbol 类型的属性
  {
    const o1 = { a: 1 };
    const o2 = { [Symbol('foo')]: 2 };

    const obj = Object.assign({}, o1, o2);
    console.log(obj); // { a : 1, [Symbol("foo")]: 2 } (cf. bug 1207182 on Firefox)
    Object.getOwnPropertySymbols(obj); // [Symbol(foo)]
  }

  // 1.4 继承属性和不可枚举属性是不能拷贝的
  {
    const obj = Object.create(
      { foo: 1 }, // foo 是个继承属性。
      {
        bar: {
          value: 2  // bar 是个不可枚举属性。
        },
        baz: {
          value: 3,
          enumerable: true  // baz 是个自身可枚举属性。
        }
      });
    console.log(obj); // {baz: 3, bar: 2}
    const copy = Object.assign({}, obj);
    console.log(copy); // { baz: 3 }
  }
}


// 2. Object.create(proto[, propertiesObject])
/**
 * @description 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
 * 
 * @param {Object} proto 新创建对象的原型对象。
 * @param {Object} propertiesObject 可选。如果没有指定为 undefined,
 * 则是要添加到新创建对象的不可枚举(默认)属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。
 * 这些属性对应 Object.defineProperties() 的第二个参数。
 * @returns {*} 一个新对象,带着指定的原型对象和属性。
*/
{
  console.log("--> 2. Object.create(proto[, propertiesObject])");

  var o;

  // 创建一个原型为null的空对象
  o = Object.create(null);


  o = {};
  // 以字面量方式创建的空对象就相当于:
  o = Object.create(Object.prototype);


  o = Object.create(Object.prototype, {
    // foo会成为所创建对象的数据属性
    foo: {
      writable: true,
      configurable: true,
      value: "hello"
    },
    // bar会成为所创建对象的访问器属性
    bar: {
      configurable: false,
      get: function () { return 10 },
      set: function (value) {
        console.log("Setting `o.bar` to", value);
      }
    }
  });


  function Constructor() { }
  o = new Constructor();
  // 上面的一句就相当于:
  o = Object.create(Constructor.prototype);
  // 当然,如果在Constructor函数中有一些初始化代码,Object.create不能执行那些代码


  // 创建一个以另一个空对象为原型,且拥有一个属性p的对象
  o = Object.create({}, { p: { value: 42 } })

  // 省略了的属性特性默认为false,所以属性p是不可写,不可枚举,不可配置的:
  o.p = 24
  o.p
  //42

  o.q = 12
  for (var prop in o) {
    console.log(prop)
  }
  //"q"

  delete o.p
  //false

  //创建一个可写的,可枚举的,可配置的属性p
  o2 = Object.create({}, {
    p: {
      value: 42,
      writable: true,
      enumerable: true,
      configurable: true
    }
  });
}


// 3. Object.defineProperty(obj, prop, descriptor)
// 给对象添加或修改一个属性并指定该属性的配置。
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
/**
 * @description 该方法允许精确添加或修改对象的属性。
 * 通过赋值操作添加的普通属性是可枚举的,能够在属性枚举期间呈现出来(for...in 或 Object.keys 方法), 
 * 这些属性的值可以被改变,也可以被删除。这个方法允许修改默认的额外选项(或配置)。
 * 默认情况下,使用 Object.defineProperty() 添加的属性值是[不可修改]的。
 *
 * @param {Object}        obj 要在其上定义属性的对象。
 * @param {String}      props 要定义或修改的属性的名称。
 * @oaram {Object} descriptor 将被定义或修改的属性描述符。
 *     ------数据描述符和存取描述符均具有-----
 *     @param {Boolean} configurable (defualt is false)
 *     当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除
 *     @param {Boolean} enumerable (defualt is false)
 *     当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。
 *     ---------------数据描述符-------------
 *     @param {*} value (defualt is undefined)
 *     该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。
 *     @param {Boolean} writable (defualt is false) 
 *     当且仅当该属性的writable为true时,value才能被赋值运算符改变。
 *     ---------------存取描述符-------------
 *     @param {*} get (defualt is undefined)
 *     一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,
 *     方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。
 *     @param {*} set (defualt is undefined)
 *     一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。
 *     该方法将接受唯一参数,即该属性新的参数值。
 * @returns {*} 被传递给函数的对象。
 */
{
  console.log("--> 3. Object.defineProperty(obj, prop, descriptor)");

  //示例:创建属性
  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

  // o.b的值现在总是与bValue相同,除非重新定义o.b

  try {
    // 数据描述符和存取描述符不能混合使用
    Object.defineProperty(o, "conflict", {
      value: 0x9f91102,
      get: function () {
        return 0xdeadbeef;
      }
    });
  } catch (error) {
    console.log("数据描述符和存取描述符不能混合使用:");
    console.info(error);
  }
}

// 4. Object.defineProperties(obj, props)
// 给对象添加或修改多个属性并分别指定它们的配置。
/**
 * @description 本质上定义了obj 对象上props的可枚举属性相对应的所有属性。
 *
 * @param {Object} obj 在其上定义或修改属性的对象。
 * @param {Object} props 
 *     @param {Boolean} configurable (defualt is false)
 *     @param {Boolean}   enumerable (defualt is false)
 *     @param {*}              value (defualt is undefined)
 *     @param {Boolean}     writable (defualt is false)
 *     @param {*}                get (defualt is undefined)
 *     @param {*}                set (defualt is undefined)
 */
{
  console.log("--> 4. Object.defineProperties(obj, props)");

  var obj = {};
  Object.defineProperties(obj, {
    'property1': {
      value: true,
      writable: true
    },
    'property2': {
      value: 'Hello',
      writable: false
    }
    // etc. etc.
  });
  console.log(obj);
}

// 5. Object.entries(obj)
// 返回给定对象自身可枚举属性的 [key, value] 数组。
/**
 * @description 返回一个给定对象自身可枚举属性的键值对数组,
 * 其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。
 * 
 * @param {Object} obj 可以返回其可枚举属性的键值对的对象。
 * @returns {Array} 给定对象自身可枚举属性的键值对数组。
*/
{
  console.log("--> 5. Object.entries(obj)");

  const obj1 = { foo: 'bar', baz: 42 };
  console.log(Object.entries(obj1)); // [ ['foo', 'bar'], ['baz', 42] ]

  // array like object
  const obj2 = { 0: 'a', 1: 'b', 2: 'c' };
  console.log(Object.entries(obj2)); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]

  // array like object with random key ordering
  const anObj = { '100': 'a', 2: 'b', 7: 'c' };
  console.log(Object.entries(anObj)); // [ ['2', 'b'], ['7', 'c'], ['100', 'a'] ]

  // getFoo is property which isn't enumerable
  const myObj = Object.create({}, { getFoo: { value() { return this.foo1; } } });
  myObj.foo1 = 'bar';
  console.log(Object.entries(myObj)); // [ ['foo', 'bar'] ]

  // non-object argument will be coerced to an object
  console.log(Object.entries('foos')); // [ ['0', 'f'], ['1', 'o'], ['2', 'o'],['3', 's'] ]

  // iterate through key-value gracefully
  const obj3 = { a: 5, b: 7, c: 9 };
  for (const [key, value] of Object.entries(obj3)) {
    console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
  }

  // Or, using array extras
  Object.entries(obj).forEach(([key, value]) => {
    console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
  });
}

// 6. Object.freeze(obj)
// 冻结对象:其他代码不能删除或更改任何属性。
/**
 * @description 被冻结对象自身的所有属性都不可能以任何方式被修改。数据属性的值不可更改.
 * 其他类型:不可修改。
 * 对象:如果一个属性的值是个对象,对象中的属性是可以修改的.
 * 
 * @param {Object} obj 要被冻结的对象。
 * @returns {*} 被冻结的对象。
 * 
 * ----------------------------
 * 对比 Object.seal()
 * 用Object.seal()密封的对象可以改变它们现有的属性。使用Object.freeze() 冻结的对象中现有属性是不可变的。
*/
{
  console.log("--> 6. Object.freeze(obj)");

  obj1 = {
    internal: { a: "bbb" },
    a: "ccc"
  };

  Object.freeze(obj1);
  obj1.a = 'aValue';
  obj1.internal.a = 'aValue';

  console.log(obj1);

  // {
  //   internal: { a: "aValue" },
  //   a:"ccc"
  // };
}

// 7. Object.getOwnPropertyDescriptor(obj, prop)
// 返回对象指定的属性配置。
/**
 * @description 该方法允许对一个属性的描述进行检索。
 * 在 Javascript 中, 属性 由一个字符串类型的“名字”(name)和一个“属性描述符”(property descriptor)对象构成。
 * 更多关于属性描述符类型以及他们属性的信息可以查看:Object.defineProperty.
 * 一个属性描述符是一个记录,由下面属性当中的某些组成的:
 * 
 * @param {Object} obj 需要查找的目标对象
 * @param {String} prop 目标对象内属性名称
 * @returns {Object} 如果指定的属性存在于对象上,则返回其属性描述符对象(property descriptor),否则返回 undefined。
 */
{
  console.log("--> 7. Object.getOwnPropertyDescriptor(obj, prop)");

  var o, d;

  o = { get foo() { return 17; } };
  d = Object.getOwnPropertyDescriptor(o, "foo");
  console.log(d);

  // d {
  //   configurable: true,
  //   enumerable: true,
  //   get: /*the getter function*/,
  //   set: undefined
  // }
}


// 8. Object.getOwnPropertyNames(obj)
// 返回一个数组,它包含了指定对象所有的可枚举或不可枚举的属性名。
/**
 * @description Object.getOwnPropertyNames() 返回一个数组,该数组对元素是 obj自身拥有的枚举或不可枚举属性名称字符串。 
 * 数组中枚举属性的顺序与通过 for...in 循环(或 Object.keys)迭代该对象属性时一致。数组中不可枚举属性的顺序未定义。
 * 
 * @param {Object} obj 一个对象,其自身的可枚举和不可枚举属性的名称被返回。
 * @returns {Array} 在给定对象上找到的自身属性对应的字符串数组。
 */
{
  console.log("--> 8. Object.getOwnPropertyNames(obj)");

  var arr = ["a", "b", "c"];
  console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"]

  // 类数组对象
  var obj = { 0: "a", 1: "b", 2: "c" };
  console.log(Object.getOwnPropertyNames(obj).sort()); // ["0", "1", "2"]

  // 使用Array.forEach输出属性名和属性值
  Object.getOwnPropertyNames(obj).forEach(function (val, idx, array) {
    console.log(val + " -> " + obj[val]);
  });
  // 输出
  // 0 -> a
  // 1 -> b
  // 2 -> c

  //不可枚举属性
  var my_obj = Object.create({}, {
    getFoo: {
      value: function () { return this.foo; },
      enumerable: false
    }
  });
  my_obj.foo = 1;

  console.log(Object.getOwnPropertyNames(my_obj).sort()); // ["foo", "getFoo"]
}

// 9. Object.getOwnPropertySymbols(obj)
// 返回一个数组,它包含了指定对象自身所有的符号属性。
/**
 * @description 与 Object.getOwnPropertyNames()类似,您可以将给定对象的所有符号属性作为 Symbol 数组获取。 
 * 请注意,Object.getOwnPropertyNames()本身不包含对象的 Symbol 属性,只包含字符串属性。
 * 
 * @param {Object} obj 要返回 Symbol 属性的对象。
 * @returns {Array} 在给定对象自身上找到的所有 Symbol 属性的数组。
 * -----------------------------------
 * 
 * 因为所有的对象在初始化的时候不会包含任何的 Symbol,除非你在对象上赋值了 Symbol ,
 * 否则Object.getOwnPropertySymbols()只会返回一个空的数组。
 */
{
  console.log("--> 9. Object.getOwnPropertySymbols(obj)");

  var obj = {};
  var a = Symbol("a");
  var b = Symbol.for("b");

  obj[a] = "localSymbol";
  obj[b] = "globalSymbol";

  var objectSymbols = Object.getOwnPropertySymbols(obj);

  console.log(objectSymbols)         // [Symbol(a), Symbol(b)]
  console.log(objectSymbols.length); // 2
  console.log(objectSymbols[0])      // Symbol(a)
}

// 10. Object.getPrototypeOf(object)
// 返回指定对象的原型对象。
/**
 * @description 返回指定对象的原型。(内部[[Prototype]]属性的值)。
 * 
 * @param {Object} obj 要返回其原型的对象。
 * @returns {Object} 给定对象的原型。如果没有继承属性,则返回 null 。
 * -----------------------------------
 *
 * Object.getPrototypeOf(Object)  不是  Object.prototype
 */
{
  console.log("--> 10. Object.getPrototypeOf(object)");

  var proto = {};
  var obj = Object.create(proto);
  Object.getPrototypeOf(obj) === proto; // true

  var reg = /a/;
  Object.getPrototypeOf(reg) === RegExp.prototype; // true

  /**
   * Object.getPrototypeOf(Object)  不是  Object.prototype
   * JavaScript中的 Object 是构造函数(创建对象的包装器)。
   * 
   * 一般用法是:
   */
  var obj = new Object();

  // 所以:
  Object.getPrototypeOf(Object);               // ƒ () { [native code] }
  Object.getPrototypeOf(Function);             // ƒ () { [native code] }

  Object.getPrototypeOf(Object) === Function.prototype;        // true

  /**
   * Object.getPrototypeOf(Object)是把Object这一构造函数看作对象,
   * 返回的当然是函数对象的原型,也就是 Function.prototype。
   * 
   * 正确的方法是,Object.prototype是构造出来的对象的原型。
   */
  var obj = new Object();
  Object.prototype === Object.getPrototypeOf(obj);              // true

  Object.prototype === Object.getPrototypeOf({});               // true
}



// 11. Object.is(value1, value2)
// 比较两个值是否相同。所有 NaN 值都相等(这与==和===不同)。
/**
 * @description 判断两个值是否相同。
 * 这种相等性判断逻辑和传统的 == 运算不同,== 运算符会对它两边的操作数做隐式类型转换(如果它们类型不同),
 * 然后才进行相等性比较,(所以才会有类似 "" == false 等于 true 的现象),但 Object.is 不会做这种类型转换。
 * -----------------------------------------------------
 * 这与 === 运算符的判定方式也不一样。
 * === 运算符(和== 运算符)将数字值 -0 和 +0 视为相等,并认为 Number.NaN 不等于 NaN。
 * 
 * @param {*} value1 第一个需要比较的值
 * @param {*} value2 第二个需要比较的值
 * @returns  表示两个参数是否相同的布尔值。
 */
{
    console.log('--> 11. Object.is(value1, value2)');

    Object.is('foo', 'foo');     // true
    Object.is(window, window);   // true

    Object.is('foo', 'bar');     // false
    Object.is([], []);           // false

    var foo = { a: 1 };
    var bar = { a: 1 };
    Object.is(foo, foo);         // true
    Object.is(foo, bar);         // false

    Object.is(null, null);       // true

    // 特例
    Object.is(0, -0);            // false
    Object.is(0, +0);            // true
    Object.is(-0, -0);           // true
    Object.is(NaN, 0 / 0);         // true
}

// 12. Object.isExtensible(obj)
// 判断对象是否可扩展。
/**
 * @description (是否可以在它上面添加新的属性)。默认情况下,对象是可扩展的:即可以为他们添加新的属性。以及它们的 __proto__ 属性可以被更改。
 * Object.preventExtensions,Object.seal 或 Object.freeze 方法都可以标记一个对象为不可扩展(non-extensible)。
 * 
 * @param {Object} obj 需要检测的对象
 * @returns  表示给定对象是否可扩展的一个Boolean 。
 */
{
    console.log('--> 12. Object.isExtensible(obj)');

    // 新对象默认是可扩展的.
    var empty = {};
    var result = Object.isExtensible(empty); // === true
    console.log(result);

    // ...可以变的不可扩展.
    Object.preventExtensions(empty);
    result = Object.isExtensible(empty); // === false
    console.log(result);

    // 密封对象是不可扩展的.
    var sealed = Object.seal({});
    result = Object.isExtensible(sealed); // === false
    console.log(result);

    // 冻结对象也是不可扩展.
    var frozen = Object.freeze({});
    result = Object.isExtensible(frozen); // === false
    console.log(result);
}

// 13. Object.isFrozen(obj)
// 判断对象是否已经冻结。
/**
 * @description 一个对象是冻结的是指它不可扩展,所有属性都是不可配置的,且所有数据属性(即没有getter或setter组件的访问器的属性)都是不可写的。
 * 
 * @param {Object} obj 需要检测的对象
 * @returns  表示给定对象是否被冻结的Boolean。
 */
{
    console.log('--> 13. Object.isFrozen(obj)');

    // 一个对象默认是可扩展的,所以它也是非冻结的.
    Object.isFrozen({}); // === false

    // 一个不可扩展的空对象同时也是一个冻结对象.
    var vacuouslyFrozen = Object.preventExtensions({});
    Object.isFrozen(vacuouslyFrozen) //=== true;

    // 一个非空对象默认也是非冻结的.
    var oneProp = { p: 42 };
    Object.isFrozen(oneProp) //=== false

    // 让这个对象变的不可扩展,并不意味着这个对象变成了冻结对象,
    // 因为p属性仍然是可以配置的(而且可写的).
    Object.preventExtensions(oneProp);
    Object.isFrozen(oneProp) //=== false

    // 此时,如果删除了这个属性,则它会成为一个冻结对象.
    delete oneProp.p;
    Object.isFrozen(oneProp) //=== true

    // 一个不可扩展的对象,拥有一个不可写但可配置的属性,则它仍然是非冻结的.
    var nonWritable = { e: "plep" };
    Object.preventExtensions(nonWritable);
    Object.defineProperty(nonWritable, "e", { writable: false }); // 变得不可写
    Object.isFrozen(nonWritable) //=== false

    // 把这个属性改为不可配置,会让这个对象成为冻结对象.
    Object.defineProperty(nonWritable, "e", { configurable: false }); // 变得不可配置
    Object.isFrozen(nonWritable) //=== true

    // 一个不可扩展的对象,拥有一个不可配置但可写的属性,则它仍然是非冻结的.
    var nonConfigurable = { release: "the kraken!" };
    Object.preventExtensions(nonConfigurable);
    Object.defineProperty(nonConfigurable, "release", { configurable: false });
    Object.isFrozen(nonConfigurable) //=== false

    // 把这个属性改为不可写,会让这个对象成为冻结对象.
    Object.defineProperty(nonConfigurable, "release", { writable: false });
    Object.isFrozen(nonConfigurable) //=== true

    // 一个不可扩展的对象,值拥有一个访问器属性,则它仍然是非冻结的.
    var accessor = { get food() { return "yum"; } };
    Object.preventExtensions(accessor);
    Object.isFrozen(accessor) //=== false

    // ...但把这个属性改为不可配置,会让这个对象成为冻结对象.
    Object.defineProperty(accessor, "food", { configurable: false });
    Object.isFrozen(accessor) //=== true

    // 使用Object.freeze是冻结一个对象最方便的方法.
    var frozen = { 1: 81 };
    Object.isFrozen(frozen) //=== false
    Object.freeze(frozen);
    Object.isFrozen(frozen) //=== true

    // 一个冻结对象也是一个密封对象.
    Object.isSealed(frozen) //=== true

    // 当然,更是一个不可扩展的对象.
    Object.isExtensible(frozen) //=== false
}


// 14. Object.isSealed(obj)
// 判断对象是否已经密封。
/**
 * @description 密封对象是指那些不可 扩展 的,且所有自身属性都不可配置且因此不可删除(但不一定是不可写)的对象。
 * 
 * @param {Object} obj 需要检测的对象
 * @returns  表示给定对象是否被密封的一个Boolean 。
 */
{
    console.log('--> 14. Object.isSealed(obj)');

    // 新建的对象默认不是密封的.
    var empty = {};
    Object.isSealed(empty); // === false

    // 如果你把一个空对象变的不可扩展,则它同时也会变成个密封对象.
    Object.preventExtensions(empty);
    Object.isSealed(empty); // === true

    // 但如果这个对象不是空对象,则它不会变成密封对象,因为密封对象的所有自身属性必须是不可配置的.
    var hasProp = { fee: "fie foe fum" };
    Object.preventExtensions(hasProp);
    Object.isSealed(hasProp); // === false

    // 如果把这个属性变的不可配置,则这个对象也就成了密封对象.
    Object.defineProperty(hasProp, "fee", { configurable: false });
    Object.isSealed(hasProp); // === true

    // 最简单的方法来生成一个密封对象,当然是使用Object.seal.
    var sealed = {};
    Object.seal(sealed);
    Object.isSealed(sealed); // === true

    // 一个密封对象同时也是不可扩展的.
    Object.isExtensible(sealed); // === false

    // 一个密封对象也可以是一个冻结对象,但不是必须的.
    Object.isFrozen(sealed); // === true ,所有的属性都是不可写的
    var s2 = Object.seal({ p: 3 });
    Object.isFrozen(s2); // === false, 属性"p"可写

    var s3 = Object.seal({ get p() { return 0; } });
    Object.isFrozen(s3); // === true ,访问器属性不考虑可写不可写,只考虑是否可配置
}



// 15. Object.keys(obj)
// 返回一个包含所有给定对象自身可枚举属性名称的数组。
/**
 * @description 返回一个所有元素为字符串的数组,其元素来自于从给定的object上面可直接枚举的属性。
 * 这些属性的顺序与手动遍历该对象属性时的一致。
 * 
 * @param {Object} obj 要返回其枚举自身属性的对象。
 * @returns  一个表示给定对象的所有可枚举属性的字符串数组。
 */
{
    console.log('--> 15. Object.keys(obj)');

    // simple array
    var arr = ['a', 'b', 'c'];
    console.log(Object.keys(arr)); // console: ['0', '1', '2']

    // array like object
    var obj = { 0: 'a', 1: 'b', 2: 'c' };
    console.log(Object.keys(obj)); // console: ['0', '1', '2']

    // array like object with random key ordering
    var anObj = { 100: 'a', 2: 'b', 7: 'c' };
    console.log(Object.keys(anObj)); // console: ['2', '7', '100']

    // getFoo is a property which isn't enumerable
    var myObj = Object.create({}, {
        getFoo: {
            value: function () { return this.foo; }
        }
    });
    myObj.foo = 1;
    console.log(Object.keys(myObj)); // console: ['foo']
}


// 16. Object.preventExtensions(obj)
// 防止对象的任何扩展。
/**
 * @description 如果一个对象可以添加新的属性,则这个对象是可扩展的。Object.preventExtensions()将对象标记为不再可扩展,
 * 因此它将永远不会具有超出它被标记为不可扩展的属性。注意,一般来说,不可扩展对象的属性可能仍然可被删除。
 * 尝试将新属性添加到不可扩展对象将静默失败或抛出TypeError(最常见但不排除其他情况,如在strict mode中)。
 * ---------------------------------------
 * 仅阻止添加自身的属性。但属性仍然可以添加到对象原型。一旦使其不可扩展,就无法再对象进行扩展。
 * 
 * @param {Object} obj 将要变得不可扩展的对象。
 * @returns  已经不可扩展的对象。
 */
{
    console.log('--> 16. Object.preventExtensions(obj)');

    // Object.preventExtensions将原对象变的不可扩展,并且返回原对象.
    var obj = {};
    var obj2 = Object.preventExtensions(obj);
    obj === obj2;  // true

    // 字面量方式定义的对象默认是可扩展的.
    var empty = {};
    Object.isExtensible(empty) //=== true

    // ...但可以改变.
    Object.preventExtensions(empty);
    Object.isExtensible(empty) //=== false

    try {
        // 使用Object.defineProperty方法为一个不可扩展的对象添加新属性会抛出异常.
        var nonExtensible = { removable: true };
        Object.preventExtensions(nonExtensible);
        Object.defineProperty(nonExtensible, "new", { value: 8675309 }); // 抛出TypeError异常
    } catch (error) {
        console.log(error);
    }


    try {
        // 在严格模式中,为一个不可扩展对象的新属性赋值会抛出TypeError异常.
        function fail() {
            "use strict";
            nonExtensible.newProperty = "FAIL"; // throws a TypeError
        }
        fail();
    } catch (error) {
        console.log(error);
    }


    try {
        // 一个不可扩展对象的原型是不可更改的,__proto__是个非标准魔法属性,可以更改一个对象的原型.
        var fixed = Object.preventExtensions({});
        fixed.__proto__ = { oh: "hai" }; // 抛出TypeError异常
    } catch (error) {
        console.log(error);
    }
}

// 17. Object.seal(obj)
// 防止其他代码删除对象的属性。
/**
 * @description 通常,一个对象是可扩展的(可以添加新的属性)。密封一个对象会让这个对象变的不能添加新属性,且所有已有属性会变的不可配置。
 * 属性不可配置的效果就是属性变的不可删除,以及一个数据属性不能被重新定义成为访问器属性,或者反之。但属性的值仍然可以修改。
 * 尝试删除一个密封对象的属性或者将某个密封对象的属性从数据属性转换成访问器属性,结果会静默失败或抛出TypeError(在严格模式 中最常见的,但不唯一)。
 * 
 * ---------------------------------------------------
 * 不会影响从原型链上继承的属性。但 __proto__ (  ) 属性的值也会不能修改。返回被密封对象的引用。
 * 
 * @param {Object} obj 将要被密封的对象。
 * @returns  被密封的对象。
 */
{
    console.log("--> 17. Object.seal(obj)");

    var obj = {
        prop: function () { },
        foo: 'bar'
    };

    // New properties may be added, existing properties
    // may be changed or removed.
    obj.foo = 'baz';
    obj.lumpy = 'woof';
    delete obj.prop;

    var o = Object.seal(obj);

    o === obj; // true
    Object.isSealed(obj); // === true

    // Changing property values on a sealed object
    // still works.
    obj.foo = 'quux';

    try {
        // But you can't convert data properties to accessors,
        // or vice versa.
        Object.defineProperty(obj, 'foo', {
            get: function () { return 'g'; }
        }); // throws a TypeError
    } catch (error) {
        console.log(error);
    }

    // Now any changes, other than to property values,
    // will fail.
    obj.quaxxor = 'the friendly duck';
    // silently doesn't add the property
    delete obj.foo;
    // silently doesn't delete the property

    try {
        // ...and in strict mode such attempts
        // will throw TypeErrors.
        function fail() {
            'use strict';
            delete obj.foo; // throws a TypeError
            obj.sparky = 'arf'; // throws a TypeError
        }
        fail();
    } catch (error) {
        console.log(error);
    }

    try {
        // Attempted additions through
        // Object.defineProperty will also throw.
        Object.defineProperty(obj, 'ohai', {
            value: 17
        }); // throws a TypeError
    } catch (error) {
        console.log(error);
    }

    Object.defineProperty(obj, 'foo', {
        value: 'eit'
    }); // changes existing property value
}


// 18. Object.setPrototypeOf(obj, prototype)
// 设置对象的原型(即内部 [[Prototype]] 属性)。
/**
 * @description 如果对象的[[Prototype]]被修改成不可扩展(通过 Object.isExtensible()查看),就会抛出 TypeError异常。如果prototype参数不是一个对象或者null
 * (例如,数字,字符串,boolean,或者 undefined),则什么都不做。否则,该方法将obj的[[Prototype]]修改为新的值。
 * 
 * ---------------------------------------------------
 * Object.setPrototypeOf()是ECMAScript 6最新草案中的方法,相对于 Object.prototype.__proto__ ,它被认为是修改对象原型更合适的方法
 * 
 * @param {Object} obj 要设置其原型的对象.
 * @param {Object} prototype 该对象的新原型(一个对象 或 null).
 */
{
    var dict = Object.setPrototypeOf({}, null);
}


// 19. Object.values(obj)
// 返回给定对象自身可枚举值的数组。
/**
 * @description 返回一个数组,其元素是在对象上找到的可枚举属性值。属性的顺序与通过手动循环对象的属性值所给出的顺序相同。
 * 
 * @param {Object} obj 被返回可枚举属性值的对象。
 * @returns {Array} 一个包含对象自身的所有可枚举属性值的数组。
 */
{
    console.log('--> 19. Object.values(obj)');

    var obj = { foo: 'bar', baz: 42 };
    console.log(Object.values(obj)); // ['bar', 42]

    // array like object
    var obj = { 0: 'a', 1: 'b', 2: 'c' };
    console.log(Object.values(obj)); // ['a', 'b', 'c']

    // array like object with random key ordering
    // when we use numeric keys, the value returned in a numerical order according to the keys
    var an_obj = { 100: 'a', 2: 'b', 7: 'c' };
    console.log(Object.values(an_obj)); // ['b', 'c', 'a']

    // getFoo is property which isn't enumerable
    var my_obj = Object.create({}, { getFoo: { value: function () { return this.foo; } } });
    my_obj.foo = 'bar';
    console.log(Object.values(my_obj)); // ['bar']

    // non-object argument will be coerced to an object
    console.log(Object.values('foo')); // ['f', 'o', 'o']
}


posted @ 2020-01-07 14:01  【唐】三三  阅读(344)  评论(0编辑  收藏  举报