"ES6 Standard Getting Started" (Ruan Yifeng) - 11 new methods of objects

1.Object.is()

ES5 compare two values are equal, only two operators: equality operator ( ==) and strict equality operator ( ===). They have disadvantages, the former will be automatically converted to data type, which is NaNnot equal to its own, and +0is equal to -0. JavaScript lack of a calculation, in all circumstances, as long as the two values are the same, they should be equal.

ES6 proposed "Same-value equality" (equal to the same value) algorithm is used to solve this problem. Object.isThe new method of this algorithm is deployed. It is used to compare two values are exactly equal, consistent with strict comparison operator (===) behavior.

Object.is('foo', 'foo')
// true
Object.is({}, {})
// false

The difference is only two: one is +0not equal to -0the second is NaNequal to itself.

+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

ES5 by the following code deployment Object.is.

Object.defineProperty(Object, 'is', {
  value: function(x, y) {
    if (x === y) {
      // 针对+0 不等于 -0的情况
      return x !== 0 || 1 / x === 1 / y;
    }
    // 针对NaN的情况
    return x !== x && y !== y;
  },
  configurable: true,
  enumerable: false,
  writable: true
});

2.Object.assign()

Basic Usage

Object.assignA method for merging the object, the source object (source) of all enumerated attribute, copied to the target object (target).

const target = { a: 1 };

const source1 = { b: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

Object.assignThe first parameter is the target object's method, the source object parameters are behind.

Note that if the source object and the target object has attributes with the same name, or a plurality of source objects of the same name attribute, after the attribute overwrite the previous attribute .

const target = { a: 1, b: 1 };

const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

If only one parameter, Object.assignwill return directly to the parameter.

const obj = {a: 1};
Object.assign(obj) === obj // true

If this parameter is not an object, it will turn into the first object and then returns.

typeof Object.assign(2) // "object"

Because undefined, and nullnot turn into an object, so if they are used as parameters, the error is reported.

Object.assign(undefined) // 报错
Object.assign(null) // 报错

If the non-object appears in a position of the source object (ie non-first parameter), then the processing rules are different. First, these parameters will turn into an object, if you can not turn into an object, will be skipped. This means that if undefinedand nullnot in the first parameter, the error will not.

let obj = {a: 1};
Object.assign(obj, undefined) === obj // true
Object.assign(obj, null) === obj // true

Values ​​(i.e., values, and Boolean string) of other types is not the first parameter is not given. However, in addition to an array of strings, copied into the target object, other values ​​will not be effective.

const v1 = 'abc';
const v2 = true;
const v3 = 10;

const obj = Object.assign({}, v1, v2, v3);
console.log(obj); // { "0": "a", "1": "b", "2": "c" }

In the above code, v1, v2, v3respectively, strings, Boolean values, and results only for strings into the target object (in the form of an array of characters), the numerical and Boolean values are ignored. This is because only the string wrapped object, will produce enumerable property.

Object(true) // {[[PrimitiveValue]]: true}
Object(10)  //  {[[PrimitiveValue]]: 10}
Object('abc') // {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"}

In the above code, boolean, numeric, string, and transfected into a corresponding object to be wrapped, to their original values can be seen in the properties inside the package object [[PrimitiveValue]]above, this property will not be Object.assigncopied. Wrapper object only strings, will have real meaning enumerable properties, those properties will be copied.

Object.assignCopies of property is limited, only a copy of their own property of the source object (not copy inherited attributes), nor copy can not be enumerated attributes ( enumerable: false).

Object.assign({b: 'c'},
  Object.defineProperty({}, 'invisible', {
    enumerable: false,
    value: 'hello'
  })
)
// { b: 'c' }

In the above code, Object.assignthe object to copy only a non-enumerated attribute invisible, this property has not been copied into.

Properties named Symbol values, will be Object.assigna copy.

Object.assign({ a: 'b' }, { [Symbol('c')]: 'd' })
// { a: 'b', Symbol(c): 'd' }

important point

(1) shallow copy

Object.assignThe method is practiced shallow copy, rather than a deep copy. That is, the value of a property if the source object is an object, then get a copy of the target object is a reference to the object.

const obj1 = {a: {b: 1}};
const obj2 = Object.assign({}, obj1);

obj1.a.b = 2;
obj2.a.b // 2

(2) replacement of the same name attribute

For this nested object, the event attribute of the same name, Object.assignthe processing method is to replace, rather than adding.

const target = { a: { b: 'c', d: 'e' } }
const source = { a: { b: 'hello' } }
Object.assign(target, source)
// { a: { b: 'hello' } }

The above code, targetobject aattributes are sourceobject aproperties throughout the replaced , and will not get { a: { b: 'hello', d: 'e' } }the results. This is usually not the developers want, need special care.

Some libraries offer Object.assigncustomized versions (such as Lodash of the _.defaultsDeepmethod) can be obtained with deep copies.

(3) processing array

Object.assignIt can be used to handle an array, but would arrays as objects .

Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]

In the above code, Object.assignthe array as an object property named 0,1,2, the source array property 0 4covered 0 properties of the target array 1.

Process (4) the value of the function

Object.assignCopy only values, if the value is a value to be copied function, then evaluated and then copy .

const source = {
  get foo() { return 1 }
};
const target = {};

Object.assign(target, source)
// { foo: 1 }

The above code, sourceobject fooattribute value is a function that Object.assigndoes not copy the values of the function, only later to get the value, copy this value in the past.

Common uses

Object.assignThere are many ways useful.

(1) was added for objects

class Point {
  constructor(x, y) {
    Object.assign(this, {x, y});
  }
}

By the above process Object.assignmethod, the xproperties and yadd attributes to Pointthe object class instance.

(2) a method of adding an object

Object.assign(SomeClass.prototype, {
  someMethod(arg1, arg2) {
    ···
  },
  anotherMethod() {
    ···
  }
});

// 等同于下面的写法
SomeClass.prototype.someMethod = function (arg1, arg2) {
  ···
};
SomeClass.prototype.anotherMethod = function () {
  ···
};

The above code uses a simple representation of object properties, the two functions directly in braces, then use the assignmethod to the SomeClass.prototypebeing.

(3) Cloning of the object

function clone(origin) {
  return Object.assign({}, origin);
}

The above object is copied to the original code is an empty object, get a clone of the original object.

However, this method clones, only clone of the original object values itself, it can not be cloned inherited value . If you want to keep the inheritance chain, the following code can be used.

function clone(origin) {
  let originProto = Object.getPrototypeOf(origin);
  return Object.assign(Object.create(originProto), origin);//Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
}

(4) combining a plurality of objects

Combine multiple objects to an object.

const merge =
  (target, ...sources) => Object.assign(target, ...sources);

If you want to consolidate to return a new object, you can rewrite the above function, an empty object to the merger.

const merge =
  (...sources) => Object.assign({}, ...sources);

(5) specified for the attribute default values

const DEFAULTS = {
  logLevel: 0,
  outputFormat: 'html'//输出格式
};

function processContent(options) {
  options = Object.assign({}, DEFAULTS, options);
  console.log(options);
  // ...
}

The above code, DEFAULTSthe object is the default optionsobject is a user-supplied parameters. Object.assignThe methods DEFAULTSand optionscombined into a new object if both have the same name attribute, optionsattribute value overrides the DEFAULTSattribute values.

Note that, due to the presence of shallow copy problems, DEFAULTSobjects and optionsall attributes of objects, preferably type are simple, do not point to another object. Otherwise, DEFAULTSthe properties of the object is likely to be ineffective.

const DEFAULTS = {
  url: {
    host: 'example.com',
    port: 7070
  },
};

processContent({ url: {port: 8000} })
// {
//   url: {port: 8000}
// }

The original intent of the code above is url.portchanged to 8000, url.hostremain unchanged. The actual result is options.urloverwritten DEFAULTS.url, it url.hostdoes not exist.

3.Object.getOwnPropertyDescriptors()

ES5 The Object.getOwnPropertyDescriptor()method returns an object attributes that describe the object (descriptor). ES2017 introduced Object.getOwnPropertyDescriptors()method, all the specified object attribute describes the object itself (non-inherited attributes).

const obj = {
  foo: 123,
  get bar() { return 'abc' }
};

Object.getOwnPropertyDescriptors(obj)
// { foo:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   bar:
//    { get: [Function: get bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }

The above code, the Object.getOwnPropertyDescriptors()method returns an object, attribute names are all the original object's property name, property value corresponding to the object is to describe the property.

Implementation of this method is very easy.

function getOwnPropertyDescriptors(obj) {
  const result = {};
  for (let key of Reflect.ownKeys(obj)) {
    result[key] = Object.getOwnPropertyDescriptor(obj, key);
  }
  return result;
}

The purpose of this method is introduced, mainly to solve Object.assign()not copy the correct getattributes and setproblem properties.

const source = {
  set foo(value) {
    console.log(value);
  }
};

const target1 = {};
Object.assign(target1, source);

Object.getOwnPropertyDescriptor(target1, 'foo')
// { value: undefined,
//   writable: true,
//   enumerable: true,
//   configurable: true }

The above code, sourceobject foovalue of the property is an assignment function, Object.assignmethod attribute copy to this target1object, the result becomes a value of the property undefined. This is because the Object.assignmethod always copy the value of a property, and will not copy assignment method or accessor methods behind it.

In this case, Object.getOwnPropertyDescriptors()the method with the Object.defineProperties()method, it is possible to achieve the correct copy.

const source = {
  set foo(value) {
    console.log(value);
  }
};

const target2 = {};
Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source));
Object.getOwnPropertyDescriptor(target2, 'foo')
// { get: undefined,
//   set: [Function: set foo],
//   enumerable: true,
//   configurable: true }

The above code, the two combined objects can be written a logic function.

const shallowMerge = (target, source) => Object.defineProperties(
  target,
  Object.getOwnPropertyDescriptors(source)
);

Object.getOwnPropertyDescriptors()Another method of use, is in line with Object.create()the method, object attributes cloned into a new object. It belongs to a shallow copy.

const clone = Object.create(Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj));

// 或者

const shallowClone = (obj) => Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj)
);

The above code clone obj.

Further, the Object.getOwnPropertyDescriptors()method may be implemented if an object inherits another object. Previously, another object inheritance, often written as follows.

const obj = {
  __proto__: prot,
  foo: 123,
};

ES6 provisions __proto__only browser to deploy, do not deploy in other environments. If removed __proto__, the code above would end up with the following.

const obj = Object.create(prot);
obj.foo = 123;

// 或者

const obj = Object.assign(
  Object.create(prot),
  {
    foo: 123,
  }
);

With Object.getOwnPropertyDescriptors(), we have another way.

const obj = Object.create(
  prot,
  Object.getOwnPropertyDescriptors({
    foo: 123,
  })
);

Object.getOwnPropertyDescriptors()It can also be used to implement Mixin (mixed) mode.

let mix = (object) => ({
  with: (...mixins) => mixins.reduce(
    (c, mixin) => Object.create(
      c, Object.getOwnPropertyDescriptors(mixin)
    ), object)
});

// multiple mixins example
let a = {a: 'a'};
let b = {b: 'b'};
let c = {c: 'c'};
let d = mix(c).with(a, b);

d.c // "c"
d.b // "b"
d.a // "a"

The above code returns a new object d, representing objects aand bis mixed object coperation.

For completeness, Object.getOwnPropertyDescriptors()after the entry criteria, the future will add Reflect.getOwnPropertyDescriptors()method.

4.__proto__属性,Object.setPrototypeOf(),Object.getPrototypeOf()

Object JavaScript language inheritance is achieved through the prototype chain. ES6 provided a method of operating more prototype object.

__proto__Attributes

__proto__Properties (before and after two underscores), provided for reading the current object or prototypeobjects. Currently, all browsers (including IE11) has deployed this property.

// es5 的写法
const obj = {
  method: function() { ... }
};
obj.__proto__ = someOtherObj;

// es6 的写法
var obj = Object.create(someOtherObj);
obj.method = function() { ... };

This property is not ES6 written text, but written appendix, because the __proto__double underline before and after, indicating that it is essentially an internal property, rather than a formal external API, but due to the widespread browser support, was He joined the ES6. Clearly defined standards, only browser must deploy this property, other operating environments do not necessarily need to be deployed, and the new code should think that this property does not exist. Therefore, both from the semantic point of view, or from a compatibility point of view, do not use this property, but the use of the following Object.setPrototypeOf()(a write operation), Object.getPrototypeOf()(read), (generatingObject.create() operation) instead.

The realization, __proto__the call is Object.prototype.__proto__specific implementation is as follows.

Object.defineProperty(Object.prototype, '__proto__', {
  get() {
    let _thisObj = Object(this);
    return Object.getPrototypeOf(_thisObj);
  },
  set(proto) {
    if (this === undefined || this === null) {
      throw new TypeError();
    }
    if (!isObject(this)) {
      return undefined;
    }
    if (!isObject(proto)) {
      return undefined;
    }
    let status = Reflect.setPrototypeOf(this, proto);
    if (!status) {
      throw new TypeError();
    }
  },
});

function isObject(value) {
  return Object(value) === value;
}

If an object itself deployed __proto__property, the value of the prototype is the object of the property.

Object.getPrototypeOf({ __proto__: null })
// null

Object.setPrototypeOf()

Object.setPrototypeOfThe method of action of the __proto__same, used to set a target prototypeobject, and returns the parameter object itself. It is recommended setting ES6 official prototype object.

// 格式
Object.setPrototypeOf(object, prototype)

// 用法
const o = Object.setPrototypeOf({}, null);

This method is equivalent to the following function.

function setPrototypeOf(obj, proto) {
  obj.__proto__ = proto;
  return obj;
}

Below is an example.

let proto = {};
let obj = { x: 10 };
Object.setPrototypeOf(obj, proto);

proto.y = 20;
proto.z = 40;

obj.x // 10
obj.y // 20
obj.z // 40

The above code protoobjects to objthe prototype object, so that the objcan read the object protoproperties of the object.

If the first argument is not an object, it will automatically be converted to objects. However, because the first parameter is returned, so the operation will not have any effect.

Object.setPrototypeOf(1, {}) === 1 // true
Object.setPrototypeOf('foo', {}) === 'foo' // true
Object.setPrototypeOf(true, {}) === true // true

Because undefined, and nullnot into the object, so if the first argument is undefinedor nullwill an error.

Object.setPrototypeOf(undefined, {})
// TypeError: Object.setPrototypeOf called on null or undefined

Object.setPrototypeOf(null, {})
// TypeError: Object.setPrototypeOf called on null or undefined

Object.getPrototypeOf()

The method and Object.setPrototypeOfmethod supporting an object for reading a prototype object.

Object.getPrototypeOf(obj);

Below is an example.

function Rectangle() {  //rectangle矩形
  // ...
}

const rec = new Rectangle();

Object.getPrototypeOf(rec) === Rectangle.prototype
// true

Object.setPrototypeOf(rec, Object.prototype);
Object.getPrototypeOf(rec) === Rectangle.prototype
// false

If the parameter is not an object, the object will be automatically converted.

// 等同于 Object.getPrototypeOf(Number(1))
Object.getPrototypeOf(1)
// Number {[[PrimitiveValue]]: 0}

// 等同于 Object.getPrototypeOf(String('foo'))
Object.getPrototypeOf('foo')
// String {length: 0, [[PrimitiveValue]]: ""}

// 等同于 Object.getPrototypeOf(Boolean(true))
Object.getPrototypeOf(true)
// Boolean {[[PrimitiveValue]]: false}

Object.getPrototypeOf(1) === Number.prototype // true
Object.getPrototypeOf('foo') === String.prototype // true
Object.getPrototypeOf(true) === Boolean.prototype // true

If the parameter is undefinedor null, they can not be converted to an object, it will be an error.

Object.getPrototypeOf(null)
// TypeError: Cannot convert undefined or null to object

Object.getPrototypeOf(undefined)
// TypeError: Cannot convert undefined or null to object

5.Object.keys(),Object.values(),Object.entries()

Object.keys()

ES5 introduced Object.keysmethod returns an array, the members of the object itself is a parameter (not inherited) all traverse (Enumerable) attribute keys.

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

ES2017 introduced the now Object.keyssupporting Object.valuesand Object.entries, as a supplementary means of traversing an object, for for...ofrecycling.

let {keys, values, entries} = Object;
let obj = { a: 1, b: 2, c: 3 };

for (let key of keys(obj)) {
  console.log(key); // 'a', 'b', 'c'
}

for (let value of values(obj)) {
  console.log(value); // 1, 2, 3
}

for (let [key, value] of entries(obj)) {
  console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
}

Object.values()

Object.valuesThe method returns an array, the members of the object itself is a parameter (not inherited) all traverse (Enumerable) key attribute.

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

Consistent with the rules described in section returns an array arrangement order of members, and "traversal attribute" chapter.

const obj = { 100: 'a', 2: 'b', 7: 'c' };
Object.values(obj)
// ["b", "c", "a"]

In the above code, called Attribute value is the value in accordance with the size, from small to large traversed, so that the sequence returns b, c, a.

Object.valuesOnly returns the object itself can traverse the property.

const obj = Object.create({}, {p: {value: 42}});
Object.values(obj) // []

In the above code, Object.createthe object attribute (add a second parameter method p), if not explicitly declared, a default is not traversable, because pthe properties of the object described enumerableby default false, Object.valueswill not return to this property. As long as the enumerablechange true, Object.valuesit will return property pvalues.

const obj = Object.create({}, {p:
  {
    value: 42,
    enumerable: true
  }
});
Object.values(obj) // [42]

Object.valuesSymbol will filter attribute named property value.

Object.values({ [Symbol()]: 123, foo: 'abc' });
// ['abc']

If the Object.valuesmethod parameter is a string, it returns an array of individual characters.

Object.values('foo')
// ['f', 'o', 'o']

In the above code, we will first turn into a string-like object array. Each character in the string is a property of the object. Therefore, the Object.valuesreturn key values for each attribute is an array of individual characters.

If the parameter is not an object, Object.valuesit will first turn its objects. Since the value of the object and packaging Boolean value, will not be added as an example of non-inherited properties. Therefore, Object.valuesit returns an empty array.

Object.values(42) // []
Object.values(true) // []

Object.entries()

Object.entries()The method returns an array, the members of the object itself is a parameter (not inherited) all traverse (Enumerable) key attribute array.

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

In addition to the return value is not the same, and the behavior of this method Object.valuesare basically the same.

If the property name of the original object is a Symbol value, the property will be ignored.

Object.entries({ [Symbol()]: 123, foo: 'abc' });
// [ [ 'foo', 'abc' ] ]

In the above code, there are two original object attributes, Object.entriesonly the output of the non-Symbol Attribute name value. There may be a future Reflect.ownEntries()method that returns the object of all their property.

Object.entriesThe basic purpose is to traverse the object attributes.

let obj = { one: 1, two: 2 };
for (let [k, v] of Object.entries(obj)) {
  console.log(
    `${JSON.stringify(k)}: ${JSON.stringify(v)}`
  );
}
// "one": 1
// "two": 2

Object.entriesAnother useful method is that an object into a real Mapstructure.

const obj = { foo: 'bar', baz: 42 };
const map = new Map(Object.entries(obj));
map // Map { foo: "bar", baz: 42 }

Realize his Object.entriesmethod is very simple.

// Generator函数的版本
function* entries(obj) {
  for (let key of Object.keys(obj)) {
    yield [key, obj[key]];
  }
}

// 非Generator函数的版本
function entries(obj) {
  let arr = [];
  for (let key of Object.keys(obj)) {
    arr.push([key, obj[key]]);
  }
  return arr;
}

6.Object.fromEntries()

Object.fromEntries()It is Object.entries()the inverse operation, for an array of key-value pair into the object .

Object.fromEntries([
  ['foo', 'bar'],
  ['baz', 42]
])
// { foo: "bar", baz: 42 }

The main purpose of this method, is to restore the data structure of the key-value for the object, it is particularly suitable structure into the Map objects.

// 例一
const entries = new Map([
  ['foo', 'bar'],
  ['baz', 42]
]);

Object.fromEntries(entries)
// { foo: "bar", baz: 42 }

// 例二
const map = new Map().set('foo', true).set('bar', false);
Object.fromEntries(map)
// { foo: true, bar: false }

One use of this method is in line with URLSearchParamsthe object, the query string to object.

Object.fromEntries(new URLSearchParams('foo=bar&baz=qux'))
// { foo: "bar", baz: "qux" }

Guess you like

Origin www.cnblogs.com/dxy9527/p/12370608.html
Recommended