This is the 14th day of my participation in the August Text Challenge.More challenges in August

First, the Object is ()

ES5 compares whether two values are equal with only two operators: the equality operator (==) and the strict equality operator (===).

They both have the disadvantages that the former automatically converts the data type, the latter that NaN is not equal to itself, and +0 equals -0.

JavaScript lacks the operation that, in all environments, two values should be equal as long as they are the same.

ES6 proposes the “same-value equality” algorithm to solve this problem.

Object. Is is a new way to deploy this algorithm. It is used to compare whether two values are strictly equal, basically the same behavior as the strict comparison operator (===).

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

There are only two differences: +0 does not equal -0, and NaN equals itself.

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

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

ES5 can deploy object.is using the following code.

Object.defineProperty(Object.'is', {
  value: function(x, y) {
    if (x === y) {
      // If +0 does not equal -0
      returnx ! = =0 || 1 / x === 1 / y;
    }
    // For NaN
    returnx ! == x && y ! == y; },configurable: true.enumerable: false.writable: true
});
Copy the code

Second, the Object. The assign ()

1. Basic usage

The object.assign () method is used to merge objects. It copies all the enumerable properties of the source Object to the target Object.

const target = { a: 1 };

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

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

The object.assign () method takes the first argument to the target Object and the rest to the source Object.

Note that if the target object has an attribute with the same name as the source object, or if multiple source objects have an attribute with the same name, the following attribute overrides the preceding one.

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}
Copy the code

If there is only one argument, object.assign () returns that argument directly.

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

If the parameter is not an object, it is converted to an object and then returned.

typeof Object.assign(2) // "object"
Copy the code

Because undefined and NULL cannot be converted to objects, they are reported as arguments.

Object.assign(undefined) / / an error
Object.assign(null) / / an error
Copy the code

If non-object arguments occur at the location of the source object (that is, non-first arguments), the rules are different. First, these parameters are converted to objects, and if they cannot, they are skipped. This means that if undefined and null are not the first arguments, no error is reported.

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

Other types of values (that is, values, strings, and booleans) do not take the first argument and will not report an error. However, none of the values have any effect except that the string is copied into the target object as an array.

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" }
Copy the code

V1, v2, v3 are string, Boolean, and number, respectively. The result is that only the string is merged into the target object (in the form of a character array), and both the value and the Boolean are ignored. This is because only string wrapped objects produce enumerable properties.

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

[PrimitiveValue]], which cannot be copied by Object.assign(). Only the string wrapper object produces enumerable real sense properties, and those properties are copied.

Object.assign() copies only the source Object’s own properties (no inherited properties), and no non-enumerable properties (Enumerable: false).

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

In the above code, the Object to be copied by object.assign () has only one non-enumerable attribute invisible, which is not copied.

The attribute named Symbol is also copied by object.assign ().

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

2. Pay attention to the point

(1) Shallow copy

The object.assign () method executes shallow copies, not deep copies. That is, if the value of an attribute of the source object is an object, the target object copies a reference to that object.

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

obj1.a.b = 2;
obj2.a.b / / 2
Copy the code

In the code above, the a attribute of the source Object obj1 is an Object. Object.assign() copies a reference to this Object. Any changes to this object are reflected on the target object.

(2) The replacement of the same attribute

For such nested objects, object.assign () is handled by substitution, not addition, once an attribute of the same name is encountered.

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

{a: {b: ‘hello’, d: ‘e’}} the target attribute is replaced entirely by the source attribute. This is usually not what developers want and needs to be very careful.

Some libraries offer custom versions of Object.assign() (such as Lodash’s _.defaultsdeep () method) that allow for deep-copy merging.

(3) Array processing

Object.assign() can be used to work with arrays, but treats arrays as objects.

Object.assign([1.2.3], [4.5])
/ / [4, 5, 3]
Copy the code

In the above code, object.assign () treats the array as objects with attributes 0, 1, and 2, so attribute 4 0 of the source array overwrites attribute 1 0 of the target array.

(4) The processing of value functions

Object.assign() can only be used to copy values. If the value to be copied is a value function, it will be evaluated and copied.

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

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

In the code above, the foo attribute of the source Object is an option. object.assign () does not copy this option. instead, it copies the value as it gets it.

3. Common uses

The object.assign () method has many uses.

(1) Add attributes to the object

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

The above method adds the x and y attributes to the Object instance of the Point class using object.assign ().

(2) Add methods for objects

Object.assign(SomeClass.prototype, {
  someMethod(arg1, arg2){...}.anotherMethod(){...}});// This is the same as the following
SomeClass.prototype.someMethod = function (arg1, arg2) {...}. SomeClass.prototype.anotherMethod =function () {...}.Copy the code

The above code uses a concise representation of object properties, placing the two functions in curly braces and adding them to someclass.prototype using the assign() method.

(3) Clone objects

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

The code above copies the original object into an empty object, and you get a clone of the original object.

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

function clone(origin) {
  let originProto = Object.getPrototypeOf(origin);
  return Object.assign(Object.create(originProto), origin);
}
Copy the code

(4) Merge multiple objects

Merge multiple objects into an object.

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

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

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

(5) Specify default values for attributes

const DEFAULTS = {
  logLevel: 0.outputFormat: 'html'
};

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

In the above code, the DEFAULTS object is the default value and the Options object is the user-supplied parameter. The object.assign () method merges DEFAULTS and options into a new Object. If they have the same name, the options property overrides the DEFAULTS property.

Note that due to shallow copy problems, it is best to have simple values for all properties of DEFAULTS and Options objects and not to point to another object. Otherwise, the property of the DEFAULTS object probably won’t work.

const DEFAULTS = {
  url: {
    host: 'example.com'.port: 7070}}; processContent({url: {port: 8000}})/ / {
// url: {port: 8000}
// }
Copy the code

Port = 8000, url.host = 8000 The result is that the options.url overwrites the defaults. url, so url.host no longer exists.

Third, Object. GetOwnPropertyDescriptors ()

ES5 Object. GetOwnPropertyDescriptor () method returns an Object attribute description of Object (descriptor). ES2017 introduced Object. GetOwnPropertyDescriptors () method, which returns the specified all its attributes (not inherit property) a description of the Object.

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 } }
Copy the code

The above code, the Object. GetOwnPropertyDescriptors () method returns an Object, all of the original Object attribute names are attributes of the Object, the description of the corresponding attribute value is the attribute Object.

This method is very easy to implement.

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

This method is introduced to solve the problem that Object.assign() cannot correctly copy get and set attributes.

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 }
Copy the code

In the code above, the value of the source Object’s attribute foo is an assignment function. The object. assign method copies this attribute to the target1 Object, which becomes undefined. This is because object. assign always copies the value of an attribute, not the assignment or value method behind it.

. At this time, the Object getOwnPropertyDescriptors () method with the Object. The defineProperties () method, which can realize 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 }
Copy the code

In the code above, the logic for merging two objects can be written as a function.

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

Object. GetOwnPropertyDescriptors () method is another useful, is cooperating with the Object. The create () method, the Object attribute cloned into a new Object. This is a shallow copy.

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

/ / or

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

The above code will clone the object obj.

. In addition, the Object getOwnPropertyDescriptors () method can achieve an Object inheritance of another Object. In the past, inheriting from another object was often written as follows.

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

ES6 specifies that __proto__ must be deployed only in browsers, not in other environments. If __proto__ is removed, the code above would look like this.

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

/ / or

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

With the Object. GetOwnPropertyDescriptors (), we have another kind of writing.

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

Object. GetOwnPropertyDescriptors () can also be used to realize a Mixin (into) model.

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"
Copy the code

The above code returns a new object D, representing the operation of objects A and B mixed with object C.

For completeness, Object getOwnPropertyDescriptors after entering the standards, () will be the new Reflect. GetOwnPropertyDescriptors () method.

__proto__ attributes, Object.setProtoTypeof (), Object.getProtoTypeof ()

1. __proto__ properties

The __proto__ property (two underscores before and two underscores behind) reads or sets the prototype of the current object. This property is currently deployed in all browsers, including Internet Explorer 11.

// es5
const obj = {
  method: function() {... }}; obj.__proto__ = someOtherObj;// es6
var obj = Object.create(someOtherObj);
obj.method = function() {... };Copy the code

This attribute is not written in the body of ES6, but in the appendix, because the double underline around __proto__ indicates that it is essentially an internal attribute, not a formal external API, and was only added to ES6 due to widespread browser support. The standard clearly states that only browsers must deploy this property, not other runtime environments, and that new code is better off assuming this property does not exist. So don’t use this property for semantic reasons, or for compatibility reasons, Instead, use the following object.setPrototypeof () (write operation), Object.getPrototypeof () (read operation), object.create () (generate operation).

__proto__ calls Object.prototype.proto.

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

If an object itself deploys a __proto__ attribute, the value of that attribute is the prototype of the object.

Object.getPrototypeOf({ __proto__: null })
// null
Copy the code

2. Object.setPrototypeOf()

The object. setPrototypeOf method does the same thing as __proto__, setting the prototype of an Object and returning the Object itself. It is the official recommended method for setting prototype objects in ES6.

/ / format
Object.setPrototypeOf(object, prototype)

/ / usage
const o = Object.setPrototypeOf({}, null);
Copy the code

This method is equivalent to the following function.

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

Here’s 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
Copy the code

The code above sets the proto object to be a prototype of the OBJ object, so 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. But since the first parameter is still returned, this operation has no effect.

Object.setPrototypeOf(1. = = = {})1 // true
Object.setPrototypeOf('foo'. = = = {})'foo' // true
Object.setPrototypeOf(true. = = = {})true // true
Copy the code

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

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

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

3. Object.getPrototypeOf()

This method works with the Object.setPrototypeOf method and is used to read the prototype Object of an Object.

Object.getPrototypeOf(obj);
Copy the code

Here’s an example.

function Rectangle() {
  // ...
}

const rec = new Rectangle();

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

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

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

Object.getprototypeof (Number(1))
Object.getPrototypeOf(1)
// Number {[[PrimitiveValue]]: 0}

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

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
Copy the code

If the arguments are undefined or null, they cannot be converted to objects, so an error is reported.

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

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

Keys (), object.values (), object.entries ()

1. Object.keys()

ES5 introduced the Object.keys method, which returns an array of all the key names of the parameter Object’s own (not inherited) Enumerable properties.

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

ES2017 introduced object. values and object. entries with Object.keys as complementary means of traversing an Object for… Of recycling.

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]
}
Copy the code

2. Object.values()

The Object.values method returns an array of all the key values of an Enumerable property of the parameter Object itself (not including inheritance).

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

Returns the order of the members of an array, as described in the “Traversal of properties” section of this chapter.

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

In the above code, the property named value is traversed in ascending order of value, so the return order is B, C, and A.

Object.values returns only traversable properties of the Object itself.

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

In the above code, the Object attribute added to the second argument to the object. create method (attribute P) is not iterated by default if it is not explicitly declared, because an Object’s enumerable is false, and Object.values does not return it. Just change Enumerable to true, and Object.values returns the value of the property P.

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

Object.values filters properties with the property name Symbol value.

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

If the Object. Values method takes a string, it returns an array of characters.

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

In the above code, the string is first converted to an array-like object. Each character of the string is an attribute of the object. Therefore, Object.values returns the key value of each attribute, which is an array of characters.

If the parameter is not an Object, object. values will be converted to an Object first. Since numeric and Boilerwrapped objects, no non-inherited attributes are added to the instance. So, Object.values returns an empty array.

Object.values(42) / / []
Object.values(true) / / []
Copy the code

3. Object.entries()

The object.entries () method returns an array of all the key/value pairs of the parameter Object’s own (not inherited) enumerable properties.

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

This method behaves basically the same as Object.values, except that the return value is different.

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

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

In the above code, the original Object has two properties: Object.entries only output properties with property names that are not Symbol values. There will probably be a reflect.ownentries () method that returns all the properties of the object itself.

The basic use of ‘object. entries is to traverse Object properties.

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

Another use of the object. entries method is to turn objects into true Map structures.

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

Implementing the Object.entries method yourself is very simple.

// Version of the Generator function
function* entries(obj) {
  for (let key of Object.keys(obj)) {
    yield[key, obj[key]]; }}// The version of the non-generator function
function entries(obj) {
  let arr = [];
  for (let key of Object.keys(obj)) {
    arr.push([key, obj[key]]);
  }
  return arr;
}
Copy the code

Six, Object. FromEntries ()

The object.fromentries () method is the inverse of object.entries () and is used to turn an array of key-value pairs into objects.

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

The main purpose of this method is to restore the key-value pair’s data structure to an object, so it is particularly suitable for converting Map structures into objects.

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

/ / two cases
const map = new Map().set('foo'.true).set('bar'.false);
Object.fromEntries(map)
// { foo: true, bar: false }One use of this method is coordination`URLSearchParams`Object to turn the query string into an object.Object.fromEntries(new URLSearchParams('foo=bar&baz=qux'))
// { foo: "bar", baz: "qux" }
Copy the code