# ES6-Object

Object是js最重要的数据结构,es6对其进行了重大升级。除了解构 (opens new window)外,Object还提供了大量的基础方法。另外Object对象属性及其方法太常用,有些相似的方法容易使用错误,故根据MDN (opens new window)归类整理。注意文中重点标注的文字以及角标标注的ES发布版本。

# 1. Key/Value

# 1.1 Object.keys(obj)ES5

该方法返回一个给定对象的自身可枚举属性组成的数组。

只列出自身的枚举属性(内部使用obj.hasOwnProperty(prop)指示对象自身属性中是否具有指定的属性)

Object.keys = (function() {
  return function() {
    var result = [];
    for (var prop in obj) {
      if (hasOwnProperty.call(obj, prop)) result.push(prop);
    }

    return result;
  }
})()

# 1.2 Object.values(obj)ES8

该方法返回一个给定对象自身的所有可枚举属性值的数组。

只列出自身的枚举属性值

# 1.3 Object.entries(obj)ES8

该方法返回一个给定对象自身可枚举属性的键值对数组。

只列出自身枚举的键值对数组。

Object.fromEntries()Stage 3是其逆方法,把键值对列表转换为一个对象.

# 1.4 Object.getOwnPropertyNames(obj)

该方法返回一个数组,该数组对元素是 obj自身拥有的枚举或不可枚举属性名称字符串。

自身的枚举和不枚举属性都会列出

# 1.5 for ... in

for...in语句以任意顺序遍历一个对象的可枚举属性(包括原型链上的可枚举属性)。包括原型链上的可枚举属性。

自身和原型链上的属性。

# 2. Descriptor

# 2.1 Object.defineProperty(obj, prop, descriptor)

该方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。

  • descriptor
    • configurable。 如果为false,则任何尝试删除目标属性或修改属性特性(writable, configurable, enumerable)的行为将被无效化。所以通常属性都有特性时,可以把configurable设置为true即可。
    • writable 是否可写。设置成 false,则任何对该属性改写的操作都无效(但不会报错,严格模式下会报错),默认false。
    • enumerable。是否能在for-in循环中遍历出来或在Object.keys中列举出来。

Reflect.defineProperty(target, propertyKey, attributes)和以上Object.defineProperty类似,不过它返回一个Boolean值

Object.getOwnPropertyDescriptor()返回对象对应的属性描述符。

# 案例:

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

object1.property1 = 77;
// throws an error in strict mode

console.log(object1.property1);
// expected output: 42

# 实际应用

  1. 代理:
// old
//加入有一个目标节点, 我们想设置其位移时是这样的
var targetDom = document.getElementById('target');
var transformText = 'translateX(' + 10 + 'px)';
targetDom.style.webkitTransform = transformText;
targetDom.style.transform = transformText;

// new
Object.defineProperty(dom, 'translateX', {
set: function(value) {
         var transformText = 'translateX(' + value + 'px)';
        dom.style.webkitTransform = transformText;
        dom.style.transform = transformText;
}
//这样再后面调用的时候, 十分简单
dom.translateX = 10;
dom.translateX = -10;
  1. express获取属性时警告内容
[
  'json',
  'urlencoded',
  'bodyParser',
  'compress',
  ...
].forEach(function (name) {
  Object.defineProperty(exports, name, {
    get: function () {
      throw new Error('Most middleware (like ' + name + ') is no longer bundled with Express and must be installed separately.');
    },
    configurable: true
  });
});

# 2.2 Object.defineProperties(obj, props)

该方法直接在一个对象上定义一个或多个新的属性或修改现有属性,并返回该对象。

Object.getOwnPropertyDescriptors()ES8返回对象所有属性描述符。该方法引入目的是为了解决Object.assign()无法正确拷贝get属性和set属性的问题,详见此 (opens new window)

# 3. Other

# 3.1 Object.create(proto, [props])

根据已有的对象作为原型,创建新的对象。

// Object.create polyfill
Object.create = function(proto) {
  function F() {
    F.prototype = proto
  }
  return new F()
}

# 举例:

const person = {
  isHuman: false,
  printIntroduction: function () {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
  }
};
const me = Object.create(person);

me.name = "Matthew"; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten
me.printIntroduction();

const myObject = Object.create(null)

# 思考题:为什么要有Object.create(null)?

Object.create(null)创建一个对象,但这个对象的原型链为null,即Fn.prototype = null

let a = null
let b = Object.create(null) // 返回纯{}对象,无prototype
let c = {}
let d = Object.create({})

a.attr // throw error
a.__proto__ // throw error

b // {}
b.attr // undefined
b.__proto__ // undefined
b.toString() // throw error

// d表现与c一致,毕竟只是多嵌套了一层原型链
c // { __proto__: {constructor: ƒ, ...} }
c.attr // undefined
c.__proto__ // {constructor: ƒ, ...}
c.toString() // "[object Object]"

需要Object.create(null)的时候

  • 需要一个非常干净且高度可定制的对象当作数据字典的时候
  • 不考虑for in到原型链的属性(与Object.keys()等效)

所以绝大部分时候是直接使用{}

# 3.2 Object.assign(target, ...sources)ES6

该方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。(翻看源码能知道,它是一层浅拷贝)

# 3.3 Object.is(value1, value2)ES6

该方法判断两个值是否是相同的值。

解决ES5中只有===和==判断的不足Equality comparisons and sameness (opens new window)

# 3.4 Object.freeze(obj)Stage 1

该方法可以冻结一个对象,冻结指的是不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。该方法返回被冻结的对象。目前该方法在tc39 Stage1阶段,兼容性需要看后续发展。

Object.isFrozen(obj)判断一个对象是否被冻结

# 3.5 对象的扩展

// 对象属性简写
const foo = 'bar';
const baz = {foo}; // 等价于{ foo: foo }

// 对象方法简写
const baz1 = {
  func1() {}, // 等价于 func1: function() {}
  foo
};

# 参考文档: