Object extension

Object extension

Concise representation of attributes

const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}

function f(x, y) {
  return {x, y};
}
f(1, 2) // Object {x: 1, y: 2}

In addition to attributes, methods can also be abbreviated.

const o = {
  method() {
    return "Hello!";
  }
};

Concise writing is also useful when printing objects.

let user = {
  name: 'test'
};

let foo = {
  bar: 'baz'
};
console.log({user, foo})
// {user: {name: "test"}, foo: {bar: "baz"}}

Note that the abbreviated object method cannot be used as a constructor, and an error will be reported.

const obj = {
  f() {
    this.foo = 'bar';
  }
};

new obj.f() // report errors

In the above code, f is a short object method, so obj F cannot be used as a constructor.

Property name expression

Before ES6, objects (braces) were defined in literal form. You can only use the following methods.

var obj = {
  foo: true,
  abc: 123
};

However, there are two ways to define the properties of an object.

// Method 1
obj.foo = true;

// Method 2
obj['a' + 'bc'] = 123;

ES6 allows you to use method 2 as the attribute name of an object when defining an object literally.

let propKey = 'foo';

let obj = {
  [propKey]: true,
  ['a' + 'bc']: 123
};

Expressions can also be used to define method names.

let obj = {
  ['h' + 'ello']() {
    return 'hi';
  }
};

obj.hello() // hi

Note that the attribute name expression and concise representation cannot be used at the same time, and an error will be reported.

// report errors
const foo = 'bar';
const bar = 'abc';
const baz = { [foo] };

// correct
const foo = 'bar';
const baz = { [foo]: 'abc'};

Note that if the attribute name expression is an object, the object will be automatically converted to a string [object Object object] by default. Be careful.

const keyA = {a: 1};
const keyB = {b: 2};

const myObject = {
  [keyA]: 'valueA',
  [keyB]: 'valueB'
};

myObject // Object {[object Object]: "valueB"}

In the above code, both [keyA] and [keyB] get [object], so [keyB] will overwrite [keyA], and myObject has only one [object] attribute.

name attribute of method

The name property of the function returns the function name. Object methods are also functions, so they also have the attribute name.
If the object's method uses a getter and a setter, the name attribute is not on the method, but on the get and set attributes of the description object of the method's attributes. The return value is the method name plus get and set.

const obj = {
  get foo() {},
  set foo(x) {}
};

obj.foo.name
// TypeError: Cannot read property 'name' of undefined

const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo');

descriptor.get.name // "get foo"
descriptor.set.name // "set foo"

There are two special cases: for the Function created by the bind method, the name attribute returns bound plus the name of the original Function; For the Function created by the Function constructor, the name attribute returns anonymous.

(new Function()).name // "anonymous"

var doSomething = function() {
  // ...
};
doSomething.bind().name // "bound doSomething"

If the method of the object is a Symbol value, the name attribute returns the description of the Symbol value.

const key1 = Symbol('description');
const key2 = Symbol();
let obj = {
  [key1]() {},
  [key2]() {},
};
obj[key1].name // "[description]"
obj[key2].name // ""

In the above code, the Symbol value corresponding to key1 has a description, but key2 does not.

Enumerability and traversal of attributes

Enumerability

Object.getOwnPropertyDescriptor can get the description object of the property.

let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
//  {
//    value: 123,
//    writable: true,
//    enumerable: true,
//    configurable: true
//  }

Enumerable is enumerable. If the property is false, it means that some operations will ignore the current property.
Currently, there are four operations that ignore the enumerable property as false.

  • for...in loop: only traverses the object's own and inherited enumerable properties.
  • Object.keys(): returns the key names of all enumerable properties of the object itself.
  • JSON.stringify(): serializes only enumerable properties of the object itself.
  • Object.assign(): ignore the attribute whose enumerable is false, and only copy the enumerable attributes of the object itself.
    The first three are ES5, and the last object Assign () is new to ES6. Only for In will return the inherited properties, and the other three will ignore the inherited properties.
    In fact, the original purpose of introducing the enumerable attribute is to make some attributes avoid for In operation, otherwise the internal properties and methods will be traversed. For example, the toString() method of the object prototype and the length attribute in the array avoid being used by for In traverses to.
Object.getOwnPropertyDescriptor(Object.prototype, 'toString').enumerable
// false

Object.getOwnPropertyDescriptor([], 'length').enumerable
// false

In the above code, because these two attributes are false, for In does not traverse these two properties inherited from the prototype.
In addition, ES6 stipulates that the prototype methods of all classes are non enumerable.

Object.getOwnPropertyDescriptor(class {foo() {}}.prototype, 'foo').enumerable
// false

In general, the introduction of inherited attributes in operations will complicate the problem. Most of the time, we only care about the attributes of the object itself. So try not to use for In loop instead of object Keys() instead.

Property traversal

  1. for...in
    Loop through the object itself and inherited enumerable properties, without Symbol property.
  2. Object.keys(Obj)
    Returns an array, including all enumerable properties of the object itself without inheritance, and the key name without Symbol property.
  3. Object.getOwnPropertyNames(obj)
    Returns an array containing all the attributes of the object itself, excluding the Symbol attribute, but including the key names of non enumerable attributes.
  4. Object.getOwnPropertySymbols(obj)
    Returns an array containing the key names of all Symbol properties of the object itself.
  5. Reflect.ownKeys(obj)
    Returns an array containing the of the object itself, excluding all inherited key names. Whether Symbol or not, and whether enumerable or not.

The above five methods traverse the key names of objects, and all follow the same order rules for attribute traversal.

  1. Traverse all numeric keys in ascending numerical order.
  2. Traverse all string keys and sort them in ascending order according to the addition time.
  3. Traverse all Symbol keys in ascending order of addition time.
Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
// ['2', '10', 'b', 'a', Symbol()]

Personal PS:
In practical application, if the background returned data is an object (not an array), such as {2021: {id: 1}, 2019: {id: 2}, 2020: {id: 3}, at this time, we need to traverse and display the object. What we expect is to display in return order, but the actual situation is very different. At this time, if you want to display in a certain order, you can only arrange the data at the front end and then display it.

super keyword

We know that this keyword always points to the current object where the function is located. ES6 adds a similar keyword super to point to the prototype object of the current object.

const proto = {
  foo: 'hello'
};

const obj = {
  foo: 'world',
  find() {
    return super.foo;
  }
};
Object.setPrototypeOf(obj, proto);
obj.find() // "hello"

Note that when the super keyword represents a prototype object, it can only be used in the method of the object, and an error will be reported elsewhere.

// An error is reported in the attribute
const obj = {
  foo: super.foo
}

// An error is reported in the function
const obj = {
  foo: () => super.foo
}

// An error is reported in the function
const obj = {
  foo: function () {
    return super.foo
  }
}

Inside the JavaScript engine, super Foo is equivalent to object getPrototypeOf(this). Foo (property) or object getPrototypeOf(this). foo. Call (this) (method).

Object's extension operator

Destructuring assignment

matters needing attention:
1. Deconstruction assignment requires an object to the right of the equal sign, so if the right of the equal sign is undefined or null, an error will be reported because they cannot be converted to objects.
2. Deconstruction assignment must be the last parameter, otherwise an error will be reported.
3. Deconstruction assignment is a shallow copy, that is, if the value of a key is a composite type value (array object function), the deconstruction assignment copies the reference of the value, not the copy of the value.
4. The deconstruction assignment of the extension operator will only copy its own properties, not the properties of the prototype object.

let { ...z } = null; // Runtime error
let { ...z } = undefined; // Runtime error

let { x, ...y, ...z } = someObject; // Syntactic error

let obj = { a: { b: 1 } };
let { ...x } = obj;
obj.a.b = 2;
x.a.b // 2

const o = Object.create({ x: 1, y: 2 });
o.z = 3;

let { x, ...newObj } = o;
let { y, z } = newObj;
x // 1
y // undefined
z // 3

Extension operator

Object (...) It is used to fetch all traversable properties of the parameter object and copy them to the current object.

let foo = { ...['a', 'b', 'c'] };
foo
// {0: "a", 1: "b", 2: "c"}

{...{}, a: 1}
// { a: 1 }

// Equivalent to {... Object(1)}
{...1} // {}

{...'hello'}
// {0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}

The extension operator can merge two objects.

let ab = { ...a, ...b };

In the following code, the x and y attributes of a will be overwritten after being copied to the new object.

let aWithOverrides = { ...a, x: 1, y: 2 };

If the custom attribute is placed before the extension operator, it becomes to set the default attribute value of the new object.

let aWithDefaults = { x: 1, y: 2, ...a };

Like the extension operator of an array, the extension operator of an object can be followed by an expression.

const obj = {
  ...(x > 1 ? {a: 1} : {}),
  b: 2,
};

If there is a value taking function get in the parameter object of the extension operator, this function will be executed directly.

let a = {
  get x() {
    throw new Error('not throw yet');
  }
}

let aWithXGetter = { ...a }; // get is automatically executed, resulting in an error

AggregateError error object

ES12 in order to cooperate with the new promise Any() introduces a new error object, AggregateError.
AggregateError encapsulates multiple errors in an error object. If a single operation causes multiple errors at the same time and needs to throw these errors at the same time, you can throw an AggregateError error object and put all kinds of errors in this object.
AggregateError itself is a constructor used to generate an AggregateError instance object.

AggregateError(errors[, message])
  • errors: array, each member is an error object. This parameter is required.
  • Message: a string indicating the prompt message when thrown. This parameter is optional.
    The instance object of AggregateError has three properties.
    Name: the name of the error. The default is "AggregateError".
    Message: error message.
    errors: array, each member is an error object.

Keywords: Javascript Front-end

Added by jenreb1 on Sat, 22 Jan 2022 11:57:15 +0200