Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

The new method for ES6 objects


May 08, 2021 ES6


Table of contents


1. Object.is()

ES5 compares whether two values are 相等运算符 with only two operators: the equal operator , and the 严格相等运算符 equal operator , . T hey all have drawbacks: the former automatically converts the data type, the naN of the latter is not equal to itself, and the 0 is equal to -0. JavaScript lacks an operation that should be equal in all environments as long as the two values are the same.

ES6 “Same-value equality” algorithm to solve this problem. Object.is a new way to deploy this algorithm. It is used to compare whether the two values are strictly equal and behave in much the same way as the strict comparison operator.

  1. Object.is('foo', 'foo')
  2. // true
  3. Object.is({}, {})
  4. // false

There are only two differences: one is that 0 is not equal to -0, and the other is that NaN is equal to itself.

  1. +0 === -0 //true
  2. NaN === NaN // false
  3. Object.is(+0, -0) // false
  4. Object.is(NaN, NaN) // true

ES5 can deploy the code below to Object.is.

  1. Object.defineProperty(Object, 'is', {
  2. value: function(x, y) {
  3. if (x === y) {
  4. // 针对+0 不等于 -0的情况
  5. return x !== 0 || 1 / x === 1 / y;
  6. }
  7. // 针对NaN的情况
  8. return x !== x && y !== y;
  9. },
  10. configurable: true,
  11. enumerable: false,
  12. writable: true
  13. });

2. Object.assign()

Basic usage

Object.assign is used for the merge of 源对象 copying all enumerated 可枚举 of the source object to the target object.

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

The first argument to the Object.assign method is the target object, followed by the source object.

Note that if the target object has properties with the same name as the source object, or if multiple source objects have properties with the same name, subsequent properties override the previous properties.

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

If there is only one argument, Object.assign returns it directly.

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

If the argument is not an object, it is turned into an object and then returned.

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

Because undefined and null cannot be converted to objects, if they are parameters, they are reported as errors.

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

If a non-object argument appears at the location of the source object (that is, a non-first argument), the processing rules are different. F irst, these parameters are converted to objects, and if they cannot be converted to objects, they are skipped. This means that if undefined and null are not in the first argument, no errors are reported.

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

Other types of values (i.e. values, strings, and boolean values) are not in the first argument and are not reported as errors. However, no other value has an effect except that the string is copied into the target object as an array.

  1. const v1 = 'abc';
  2. const v2 = true;
  3. const v3 = 10;
  4. const obj = Object.assign({}, v1, v2, v3);
  5. console.log(obj); // { "0": "a", "1": "b", "2": "c" }

In the above code, v1, v2, v3 are strings, Boolean values, and values, and as a result only strings are joined into the target object (in the form of an array of characters), both values and Boolean values are ignored. This is because only the wrapper object of the string produces enumerable properties.

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

In the above code, Boolean values, values, strings are 内部属性 converted into corresponding wrapper objects, you can see that their original values are wrapped on the object's internal property, "PrimitiveValue", this property will not be copied by Object.assign. Only wrapper objects of strings produce enumerable solid properties, which are copied.

Object.assign copies properties are limited, copying only the source 不可枚举 object's own properties (not copying inherited properties) and not enumerable properties (enumerable: false).

  1. Object.assign({b: 'c'},
  2. Object.defineProperty({}, 'invisible', {
  3. enumerable: false,
  4. value: 'hello'
  5. })
  6. )
  7. // { b: 'c' }

In the above code, Object.assign only one object to copy, the 不可枚举属性 property invisible, which is not copied in.

A property named Symbol value is also copied by Object.assign.

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

Note the point

(1) Shallow copy

Object.assign method implements 浅拷贝 not a deep copy. That is, if the value of a property of the source object is an object, the target object copy gets a reference to the object.

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

In the above code, the value of the a property of the source object obj1 is an object, and the Object.assign copy gets a reference to that object. Any changes to this object are reflected above the target object.

(2) Replacement of properties with the same name

For such nested objects, Object.assign is handled by replacing, not adding, properties with the same name.

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

In the above code, the target object's a property is replaced entirely by the source object's a property without the result of the . This is usually not what developers want and requires special care.

Some libraries provide customized versions of Object.assign, such as Lodash's .defaultsDeep method, and you can get deep-copy merges.

(3) The processing of the array

Object.assign be 处理数组 but they are treated as objects.

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

In the above code, Object.assign treats the array as an object with properties named 0, 1, 2, so property 4 of the source array overrides property 0 of the target array.

(4) The processing of the value-taking function

Object.assign only be copied 复制 and if the value you want to copy is a value-taking function, it will be valued and then copied.

  1. const source = {
  2. get foo() { return 1 }
  3. };
  4. const target = {};
  5. Object.assign(target, source)
  6. // { foo: 1 }

In the above code, the foo property of the source object is a value-taking function, and Object.assign does not copy the value-taking function, but only copies the value after it has been obtained.

Common uses

The Object.assign method is of many use.

(1) Add properties to the object

  1. class Point {
  2. constructor(x, y) {
  3. Object.assign(this, {x, y});
  4. }
  5. }

The above method adds the x and y properties to the object instance of the Point class through the Object.assign method.

(2) Add a method to the object

  1. Object.assign(SomeClass.prototype, {
  2. someMethod(arg1, arg2) {
  3. ···
  4. },
  5. anotherMethod() {
  6. ···
  7. }
  8. });
  9. // 等同于下面的写法
  10. SomeClass.prototype.someMethod = function (arg1, arg2) {
  11. ···
  12. };
  13. SomeClass.prototype.anotherMethod = function () {
  14. ···
  15. };

The above code uses a concise notation of object properties, places two functions directly in braces, and adds them to SomeClass.prototype using the assign method.

(3) Clone the object

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

The above code copies the original object to an empty object and obtains a clone of the original object.

However, by cloning in this way, you can only clone the value of the original object itself, not the value it inherited. If you want to keep the inheritance chain, you can use the following code.

  1. function clone(origin) {
  2. let originProto = Object.getPrototypeOf(origin);
  3. return Object.assign(Object.create(originProto), origin);
  4. }

(4) Merge multiple objects

Merge multiple objects into an object.

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

If you want to merge and return a new object, you can override the above function and merge an empty object.

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

(5) Specify the default value for the property

  1. const DEFAULTS = {
  2. logLevel: 0,
  3. outputFormat: 'html'
  4. };
  5. function processContent(options) {
  6. options = Object.assign({}, DEFAULTS, options);
  7. console.log(options);
  8. // ...
  9. }

In the above code, the DEFAULTS object is the default value, and the options object is the user-provided parameter. The Object.assign method merges DEFAULTS and options into a new object, and if they both have properties of the same name, the property values of options override the property values of DEFAULTS.

Note that due to a shallow copy problem, the values of all properties of the DEFAULTS object and the options object are preferably simple types and do not point to another object. Otherwise, the property of the DEFAULTS object is likely to have no effect.

  1. const DEFAULTS = {
  2. url: {
  3. host: 'example.com',
  4. port: 7070
  5. },
  6. };
  7. processContent({ url: {port: 8000} })
  8. // {
  9. // url: {port: 8000}
  10. // }

The original intention of the above code is to change url.port to 8000, url.host unchanged. The actual result is options.url overwriting DEFAULTS.url, so url.host doesn't exist.

3. Object.getOwnPropertyDescriptors()

ES5's Object.getOwnPropertyDescriptor() method returns a description object (descriptor) for an object property. 描述对象 ES2017 introduces the Object.getOwnPropertyDescriptors() returns a description of all of the 自身属性 object's own properties (non-inherited properties).

  1. const obj = {
  2. foo: 123,
  3. get bar() { return 'abc' }
  4. };
  5. Object.getOwnPropertyDescriptors(obj)
  6. // { foo:
  7. // { value: 123,
  8. // writable: true,
  9. // enumerable: true,
  10. // configurable: true },
  11. // bar:
  12. // { get: [Function: get bar],
  13. // set: undefined,
  14. // enumerable: true,
  15. // configurable: true } }

In the above code, Object.getOwnPropertyDescriptors() an object, and the property name of all the original objects is the property name of the object, and the corresponding property value is the description object of the property. 对象

The implementation of this method is very easy.

  1. function getOwnPropertyDescriptors(obj) {
  2. const result = {};
  3. for (let key of Reflect.ownKeys(obj)) {
  4. result[key] = Object.getOwnPropertyDescriptor(obj, key);
  5. }
  6. return result;
  7. }

This method was introduced primarily to address the problem that Object.assign() does not correctly copy the get and set properties.

  1. const source = {
  2. set foo(value) {
  3. console.log(value);
  4. }
  5. };
  6. const target1 = {};
  7. Object.assign(target1, source);
  8. Object.getOwnPropertyDescriptor(target1, 'foo')
  9. // { value: undefined,
  10. // writable: true,
  11. // enumerable: true,
  12. // configurable: true }

In the above code, the value foo property of the source object is an assignment 赋值函数 and the Object.assign method copies the property to the target1 object, resulting in the value of the property becoming undefined. This is because the Object.assign method always copies the value of a property, not the assignment method or value method behind it.

At this point, the Object.getOwnPropertyDescriptors() method, in conjunction with the Object.defineProperties() method, can be copied correctly.

  1. const source = {
  2. set foo(value) {
  3. console.log(value);
  4. }
  5. };
  6. const target2 = {};
  7. Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source));
  8. Object.getOwnPropertyDescriptor(target2, 'foo')
  9. // { get: undefined,
  10. // set: [Function: set foo],
  11. // enumerable: true,
  12. // configurable: true }

In the above code, the logic of the merge of two objects can be written as a function.

  1. const shallowMerge = (target, source) => Object.defineProperties(
  2. target,
  3. Object.getOwnPropertyDescriptors(source)
  4. );

Another use of the Object.getOwnPropertyDescriptors() method is to clone object properties to a new object in conjunction with the Object.create() method. This is a shallow copy.

  1. const clone = Object.create(Object.getPrototypeOf(obj),
  2. Object.getOwnPropertyDescriptors(obj));
  3. // 或者
  4. const shallowClone = (obj) => Object.create(
  5. Object.getPrototypeOf(obj),
  6. Object.getOwnPropertyDescriptors(obj)
  7. );

The above code clones the object obj.

In addition, the Object.getOwnPropertyDescriptors() method enables one object to inherit another. Previously, inheriting another object was often written like this.

  1. const obj = {
  2. __proto__: prot,
  3. foo: 123,
  4. };

ES6 states that proto is only deployed by browsers and not by other environments. If you remove proto, the above code will change to the following.

  1. const obj = Object.create(prot);
  2. obj.foo = 123;
  3. // 或者
  4. const obj = Object.assign(
  5. Object.create(prot),
  6. {
  7. foo: 123,
  8. }
  9. );

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

  1. const obj = Object.create(
  2. prot,
  3. Object.getOwnPropertyDescriptors({
  4. foo: 123,
  5. })
  6. );

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

  1. let mix = (object) => ({
  2. with: (...mixins) => mixins.reduce(
  3. (c, mixin) => Object.create(
  4. c, Object.getOwnPropertyDescriptors(mixin)
  5. ), object)
  6. });
  7. // multiple mixins example
  8. let a = {a: 'a'};
  9. let b = {b: 'b'};
  10. let c = {c: 'c'};
  11. let d = mix(c).with(a, b);
  12. d.c // "c"
  13. d.b // "b"
  14. d.a // "a"

The above code returns a new object, d, which represents the operation of objects a and b being mixed into object c.

For integrity reasons, The Object.getOwnPropertyDescriptors() will be added to the Standard, and the Reflect.getOwnPropertyDescriptors() method will be added in the future.

4. Proto Properties, Object.setPrototypeOf(), Object.getPrototypeOf()

JavaScript the JavaScript language is 原型链 prototype chain. ES6 provides more ways to operate prototype objects.

Proto property

__proto__ (two underlined before and after) to read or set the prototype 原型对象 current object. Currently, this property is deployed by all browsers, including IE11.

  1. // es5 的写法
  2. const obj = {
  3. method: function() { ... }
  4. };
  5. obj.__proto__ = someOtherObj;
  6. // es6 的写法
  7. var obj = Object.create(someOtherObj);
  8. obj.method = function() { ... };

Instead of writing to the body of ES6, the property is written to the 内部属性 appendix because the double underscore before and after proto indicates that it is essentially an internal property, not a formal external API, and was added to ES6 only because of widespread browser support. T he standard clearly states that only browsers must deploy this property, that other operating environments do not necessarily need to be deployed, and that the new code would do well to consider this property non-existent. Therefore, do not use this property from a semantic or compatibility perspective, but instead use Object.setPrototypeOf() Object.getPrototypeOf() Object.create() (build operation).

On the implementation, __proto__ called by Object.prototype.__proto__ which is implemented as follows.

  1. Object.defineProperty(Object.prototype, '__proto__', {
  2. get() {
  3. let _thisObj = Object(this);
  4. return Object.getPrototypeOf(_thisObj);
  5. },
  6. set(proto) {
  7. if (this === undefined || this === null) {
  8. throw new TypeError();
  9. }
  10. if (!isObject(this)) {
  11. return undefined;
  12. }
  13. if (!isObject(proto)) {
  14. return undefined;
  15. }
  16. let status = Reflect.setPrototypeOf(this, proto);
  17. if (!status) {
  18. throw new TypeError();
  19. }
  20. },
  21. });
  22. function isObject(value) {
  23. return Object(value) === value;
  24. }

If an object itself deploys the proto property, the value of the property is the prototype of the object.

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

Object.setPrototypeOf()

Object.setPrototypeOf __proto__ setting a prototype object 原型对象 an object and returning the parameter object itself. It is the officially recommended method for setting up prototype objects by ES6.

  1. // 格式
  2. Object.setPrototypeOf(object, prototype)
  3. // 用法
  4. const o = Object.setPrototypeOf({}, null);

This method is equivalent to the following function.

  1. function setPrototypeOf(obj, proto) {
  2. obj.__proto__ = proto;
  3. return obj;
  4. }

Here's an example.

  1. let proto = {};
  2. let obj = { x: 10 };
  3. Object.setPrototypeOf(obj, proto);
  4. proto.y = 20;
  5. proto.z = 40;
  6. obj.x // 10
  7. obj.y // 20
  8. obj.z // 40

The above code set proto object as a obj object, so the properties of the proto object can be read from the obj object.

If the first argument is not an object, it is automatically converted to an object. However, because the first argument is returned, this operation has no effect.

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

Because undefined and null cannot be converted to objects, if the first argument is undefined or null, an error is reported.

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

Object.getPrototypeOf()

This method is Object.setPrototypeOf method and is used to read a prototype object 原型对象

  1. Object.getPrototypeOf(obj);

Here's an example.

  1. function Rectangle() {
  2. // ...
  3. }
  4. const rec = new Rectangle();
  5. Object.getPrototypeOf(rec) === Rectangle.prototype
  6. // true
  7. Object.setPrototypeOf(rec, Object.prototype);
  8. Object.getPrototypeOf(rec) === Rectangle.prototype
  9. // false

If the argument is not an object, it is automatically converted to an object.

  1. // 等同于 Object.getPrototypeOf(Number(1))
  2. Object.getPrototypeOf(1)
  3. // Number {[[PrimitiveValue]]: 0}
  4. // 等同于 Object.getPrototypeOf(String('foo'))
  5. Object.getPrototypeOf('foo')
  6. // String {length: 0, [[PrimitiveValue]]: ""}
  7. // 等同于 Object.getPrototypeOf(Boolean(true))
  8. Object.getPrototypeOf(true)
  9. // Boolean {[[PrimitiveValue]]: false}
  10. Object.getPrototypeOf(1) === Number.prototype // true
  11. Object.getPrototypeOf('foo') === String.prototype // true
  12. Object.getPrototypeOf(true) === Boolean.prototype // true

If the parameters are undefined or null, they cannot be converted to objects, so errors are reported.

  1. Object.getPrototypeOf(null)
  2. // TypeError: Cannot convert undefined or null to object
  3. Object.getPrototypeOf(undefined)
  4. // TypeError: Cannot convert undefined or null to object

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

Object.keys()

ES5 introduces the Object.keys array of key names for all traversable (enumerable) properties of the parameter object itself (without inheritance). 数组

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

ES2017 introduces Object.keys and Object.entries as a complement to traversing an object for... Object.values of recycling.

  1. let {keys, values, entries} = Object;
  2. let obj = { a: 1, b: 2, c: 3 };
  3. for (let key of keys(obj)) {
  4. console.log(key); // 'a', 'b', 'c'
  5. }
  6. for (let value of values(obj)) {
  7. console.log(value); // 1, 2, 3
  8. }
  9. for (let [key, value] of entries(obj)) {
  10. console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
  11. }

Object.values()

Object.values returns an array 数组 values for all traversable (enumerable) properties of the parameter object itself (without inheritance).

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

Returns the order of the members of the array, consistent with the arrangement rules described in the Traversal of Properties section of this chapter.

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

In the code above, 属性名 数值 of numeric values, which is traversed from small to large by numeric size, so the order returned is b, c, a.

Object.values return only the traversable properties of the object itself.

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

In the above code, the object property (property p) added by the second parameter of the Object.create method, if not explicitly declared, is not traversal by default because the property of p describes the object's enumerable as false by default, and Object.values does not return this property. As soon as you change enumerable to true, Object.values return the value of the property p.

  1. const obj = Object.create({}, {p:
  2. {
  3. value: 42,
  4. enumerable: true
  5. }
  6. });
  7. Object.values(obj) // [42]

Object.values filters properties named Symbol values.

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

If the parameters of the Object.values method are a string, an array of characters is returned.

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

In the above code, the string is first turned into an array-like object. E ach character of the string is a property of the object. Therefore, Object.values return the key value of each property, which is an array of characters.

If the argument is not an object, Object.values first converts it to an object. B ecause of the wrapper objects of the values and Boolean values, between the values and the Boolean values, no non-inherited properties are added to the instance. Therefore, Object.values returns an empty array.

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

Object.entries()

Object.entries() returns an array of key-to-array key values for all traversable (enumerable) properties of the parameter object itself (without inheritance).

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

The method behaves in much the same way as Object.values, except that the return value is different.

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

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

In the above code, the original object has two properties, Object.entries with a property name other than the Symbol value. In the future, there Reflect.ownEntries() that returns all the properties of the object itself.

Object.entries is to traverse the properties of an object.

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

Object.entries method is to turn the 对象 a true Map 结构

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

Implementing the Object.entries method yourself is very simple.

  1. // Generator函数的版本
  2. function* entries(obj) {
  3. for (let key of Object.keys(obj)) {
  4. yield [key, obj[key]];
  5. }
  6. }
  7. // 非Generator函数的版本
  8. function entries(obj) {
  9. let arr = [];
  10. for (let key of Object.keys(obj)) {
  11. arr.push([key, obj[key]]);
  12. }
  13. return arr;
  14. }

6. Object.fromEntries()

Object.fromEntries() method is an inverse operation of Object.entries() that converts a key value to an array into an object.

  1. Object.fromEntries([
  2. ['foo', 'bar'],
  3. ['baz', 42]
  4. ])
  5. // { foo: "bar", baz: 42 }

The primary purpose of this method is to restore 键值对 value pair to the 对象 it is particularly appropriate to convert the Map structure to an object.

  1. // 例一
  2. const entries = new Map([
  3. ['foo', 'bar'],
  4. ['baz', 42]
  5. ]);
  6. Object.fromEntries(entries)
  7. // { foo: "bar", baz: 42 }
  8. // 例二
  9. const map = new Map().set('foo', true).set('bar', false);
  10. Object.fromEntries(map)
  11. // { foo: true, bar: false }

One use of this method is to match URLSearchParams turn the query string 字符串 an 对象

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