Object function extension

As we all know, JS is particularly dependent on object, whether it is a function or an array, is essentially an object. ES6 optimizes the functionality of existing objects to allow users to better manipulate objects.

Syntax extension

Before ES6, we would return/generate new objects as follows:

function createPerson(name, age) {
    return {
        name: name,
        age: age
    };
}
Copy the code

ES6 simplifies the attribute mapping of an object. If it is a key-value pair with the same name, it can be shortened to one:

function createPerson(name, age) {
    return {
        name,
        age
    };
}
Copy the code

In addition to the property changes, the initialization syntax of methods in objects has also been adjusted. Prior to ES6, the syntax for defining methods in objects was as follows:

var person = {
    name: "pyhhou".sayName: function() {
        console.log(this.name); }};Copy the code

ES6 can omit the function keyword and use super inside methods defined in this way, which is the only difference between this syntax and traditional syntax:

var person = {
    name: "pyhhou".sayName() {
        console.log(this.name); }};Copy the code

In addition, you can use variables to define keys in an object. ES6 also adds this feature. Like value, you can use any expression to define keys, except for [] :

let lastName = "last name";
let person = {
    "first name": "hou",
    [lastName]: "pyh"       / / * *
};

console.log(person["first name"]); // "hou"
console.log(person[lastName]);  // "pyh"
Copy the code

Another change in the syntax is that in ES6 strict mode, an error is reported if an object has a key with the same name, but in ES6 strict mode, the following definition overwrites the previous one:

"use strict";
var person = {
    name: "Nicholas".name: "Greg" // syntax error in ES5 strict mode, no error in ES6 strict mode
};
Copy the code

New built-in methods

In JS you can use == and === to compare elements for equality, a weak comparison, just make sure the values are the same. Another strong comparison, which must have the same value and type, seems perfect, but still has a loophole:

console.log(+0= = -0);    // true
console.log(+0= = = -0);   // true
console.log(NaN= =NaN);  // false
console.log(NaN= = =NaN); // false
Copy the code

The binary representation of +0 and -0 in JS is not the same, but both == and === are considered to be the same. In addition, NaN is considered different from itself. In order to solve the above problem, ES6 introduces the object.is () built-in method to make strong comparisons more accurate:

console.log(Object.is(+0, -0));    // false
console.log(Object.is(NaN.NaN));  // true
console.log(Object.is(5.5));      // true
console.log(Object.is(5."5"));    // false
Copy the code

Another common JS operation is to copy properties and methods from one object to another:

function mixin(receiver, supplier){
    Object.keys(supplier).forEach(function(key) {
        receiver[key] = supplier[key];
    });
    return receiver;
}

function EventTarget() { / *... * / }
EventTarget.prototype = {
    constructor: EventTarget,
    emit: function() { / *... * / },
    on: function() { / *... * /}};var myObject = {};
mixin(myObject, EventTarget.prototype);
myObject.emit("somethingChanged");
Copy the code

It’s inconvenient to have to define a mixin function every time. ES6 introduces the object. assign built-in method to do this:

function EventTarget() { / *... * / }
EventTarget.prototype = {
    constructor: EventTarget,
    emit: function() { / *... * / },
    on: function() { / *... * /}}var myObject = {}
Object.assign(myObject, EventTarget.prototype);
myObject.emit("somethingChanged");
Copy the code

Object.assign can accept as many objects as you want to copy, which is much more convenient than before.

Prototype to strengthen

Before ES6, prototypes were not allowed to change. However, there is no way to change the prototype of an object once it has been initialized, which is inconvenient in some cases. ES6 provides the object.setPrototypeof () method to make changes to the prototype of an Object:

let person = {
    getGreeting() {
        return "Hello"; }};let dog = {
    getGreeting() {
        return "Woof"; }};// prototype is person
let friend = Object.create(person);
console.log(friend.getGreeting());
console.log(Object.getPrototypeOf(friend) === person); // true

// set prototype to dog
Object.setPrototypeOf(friend, dog);
console.log(friend.getGreeting()); // "Woof" 
console.log(Object.getPrototypeOf(friend) === dog); // true
Copy the code

In addition, ES6 provides super for access to prototypes. Before ES6, you need to use object.getProtoTypeof (this) values, and sometimes you need to bind this, which is not intuitive.

let friend = {
    getGreeting() {
        // this is the same as:
        // Object.getPrototypeOf(this).getGreeting.call(this)
        return super.getGreeting() + ", hi!"; }};Copy the code

When using super, the syntax for the method representation must be the concise form of ES6, otherwise an error will be reported:

let friend = {
    getGreeting: function() {
        // syntax error
        return super.getGreeting() + ", hi!"; }};Copy the code

Another important change is that all ES6 object methods have a [[HomeObject]] internal slot. This property covers the object on which the method is located. Normal functions do not have this internal slot. Super is also judged according to [[HomeObject]] and the claim of properties or methods in the prototype.


Deconstruction assignment

Destruct assignment is a unique syntax in ES6 that allows for much cleaner code, especially for objects and arrays.

Object to deconstruct

For example, you can see the difference:

let node = {
    type: "Identifier".name: "foo"
};

/ / ES6 before
// let type = node.type,
// name = node.name;

// ES6
let { type, name } = node;
Copy the code

You may not see a big difference in this example, but remember that in JS, taking values from objects or arrays of objects is a very frequent operation. Without a concise syntax, the code becomes very long and filled with repetitive code that is hard to read. And this seemingly simple syntax actually has many advanced uses, let’s take a look.

Assign to an existing variable:

let node = {
    type: "Identifier".name: "foo"
},
type = "Literal",
name = 5;
// assign different values using destructuring
({ type, name } = node);
console.log(type); // "Identifier"
console.log(name); // "foo"
Copy the code

Default parameters:

let node = {
    type: "Identifier".name: "foo"
};
let { type, name, value = true } = node;
console.log(type); // "Identifier"
console.log(name); // "foo"
console.log(value); // true
Copy the code

Declare variables with different names:

let node = {
    type: "Identifier".name: "foo"
};

let { type: localType, name: localName } = node;
console.log(localType); // "Identifier"
console.log(localName); // "foo"
Copy the code

Nested object deconstruction:

let node = {
    type: "Identifier".name: "foo".loc: {
        start: {
            line: 1.column: 1
        },
        end: {
            line: 1.column: 4}}};// extract node.loc.start
let { loc: { start: localStart }} = node;
console.log(localStart.line); / / 1
console.log(localStart.column); / / 1
Copy the code

In short, the rule of object deconstruction is that the semicolon: the thing to the left represents the object to be searched at the current layer of the object, the thing to the right of the semicolon is optional and represents an identifier to be used to receive an assignment, and if not, the content to the left is assumed to be the identifier. A curly brace to the right indicates a search to the next level.

An array of deconstruction

Array deconstruction is very similar to object deconstruction. Except in arrays, where there is no key to consider, the situation is relatively simple:

let colors = [ "red"."green"."blue" ];
let [ firstColor, secondColor ] = colors;
let [ , , thirdColor ] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
console.log(thirdColor); // "blue"
Copy the code

Using the destruct nature of arrays, it is easy to swap two elements:

// swapping variables in ECMAScript 6
let a = 1,
    b = 2;
[ a, b ] = [ b, a ];
console.log(a); / / 2
console.log(b); / / 1
Copy the code

Similar to object deconstruction, array deconstruction also supports default arguments, as well as nested deconstruction:

let colors = [ "red" ];
let [ firstColor, secondColor = "green" ] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
Copy the code
let colors = [ "red"["green"."lightgreen"]."blue" ];
// later
let [ firstColor, [ secondColor ] ] = colors;
console.log(firstColor);  // "red"
console.log(secondColor); // "green"
Copy the code

In addition, array deconstruction also supports residual arguments (similar to function argument acceptance) :

let colors = [ "red"."green"."blue" ];
let [ firstColor, ...restColors] = colors;
console.log(firstColor); // "red"
console.log(restColors.length); / / 2
console.log(restColors[0]); // "green"
console.log(restColors[1]); // "blue"
Copy the code

Array deconstruction and the previously mentioned object deconstruction can also be used together:

let node = {
    type: "Identifier".name: "foo".loc: {
        start: {
            line: 1.column: 1
        },
        end: {
            line: 1.column: 4}},range: [0.3]};let {
    loc: { start },
    range: [ startIndex ]
} = node;
console.log(start.line); / / 1
console.log(start.column); / / 1
console.log(startIndex); / / 0
Copy the code

Parameters of deconstruction

Destruct can be used not only for variable declarations and assignments, but also for function argument reception. Before ES6, destruct of function arguments was a troublesome matter:

// properties on options represent additional parameters
function setCookie(name, value, options) {
    options = options || {};
    let secure = options.secure,
        path = options.path,
        domain = options.domain,
        expires = options.expires;
        // code to set the cookie
}
// third argument maps to options
setCookie("type"."js", {
    secure: true.expires: 60000
});
Copy the code

The introduction of parameter deconstruction makes code much simpler:

function setCookie(name, value, { secure, path, domain, expires }) {
    // code to set the cookie
}
setCookie("type"."js", {
    secure: true.expires: 60000
});
Copy the code

The destruct parameter must exist, otherwise an error will be reported. For example:

function setCookie(name, value, { secure, path, domain, expires }) {
    // code to set the cookie
}
// error!
setCookie("type"."js");
Copy the code

Of course, you can also use default parameters to solve this problem:

function setCookie(name, value, { secure, path, domain, expires } = {}) {
    // code to set the cookie
}
Copy the code


symbol

Before ES6, there were five basic data types in JS: String, Number, Boolean, NULL, undefined. ES6 introduces a new base data type, Symbol. This data type is used to solve the problem of object property names. Previously, object property names can only be strings and have no access permissions. Symbol allows developers to define non-character attribute names, and these attributes are set to be private and undetected by normal statements, adding to the attribute’s concealability. This ensures that the special properties of these symbols are not traversed when traversing objects.

Symbol is also very simple to create and use:

let firstName = Symbol("first name"); 
let person = {};
person[firstName] = "pyhhou";
console.log("first name" in person); // false
console.log(person[firstName]); // "pyhhou"
console.log(firstName); // "Symbol(first name)"
Copy the code

Note that since Symbol is the base data type, you cannot add new before creation, otherwise an error will be reported. The Symbol’s Description is stored in an inner slot called [[Description]]. When the Symbol’s toString method is called, the inner slot is accessed to retrieve the Symbol’s Description.

Because Symbol is the base type, you typeof can recognize variables of type Symbol:

let symbol = Symbol("test symbol");
console.log(typeof symbol); // "symbol"
Copy the code

ES6 has a globally shared Symbol, which is stored in a place similar to global/window. If the Symbol is defined again, it returns:

let uid = Symbol.for("uid");
let object = {
    [uid]: "12345"
};
console.log(object[uid]);
console.log(uid);
let uid2 = Symbol.for("uid");

console.log(uid === uid2); // true
console.log(object[uid2]); / / "12345"
console.log(uid2); // "Symbol(uid)"
Copy the code

As for Symbol, it may be seldom used in general. This is because users generally do not use it directly in business code. However, Symbol is frequently used to implement some common functions in JS. Like the following:

Symbol. HasInstance method

ES6 implements obj instanceof Array through Arra[symbol.hasInstance](obj). Of course you can use the Object.defineProperty method to change what the Symbol represents:

function MyObject() {
    // empty
}
Object.defineProperty(MyObject, Symbol.hasInstance, {
    value: function(v) {
        return false; }});let obj = new MyObject();
console.log(obj instanceof MyObject); // false
Copy the code

Symbol. IsConcatSpreadable method

An Array in JS can be combined with other arrays or other types of variables:

let colors1 = [ "red"."green" ],
    colors2 = colors1.concat([ "blue"."black"]."brown");

console.log(colors2.length); / / 5
console.log(colors2); // ["red","green","blue","black","brown"]
Copy the code

Only Array variables are allowed to do this because JS internally treats arrays differently. So how do you make general objects have the same properties? We can use the Symbol. IsConcatSpreadable symbols. Note that this symbol does not appear by default in any standard object. This is a Boolean property. True means the object has a length property and a numeric Key, which is the basic property of an Array object.

let collection = {
    0: "Hello".1: "world".length: 2[Symbol.isConcatSpreadable]: true
};
let messages = [ "Hi" ].concat(collection); 
console.log(messages); // ["hi","Hello","world"]
console.log(messages.length); / / 3
Copy the code

Symbol. ToPrimitive method

When you weakly compare a string variable with an object variable, STR == obj. The obJ object needs to do a cast first, but the problem is that obJ needs to know what type to cast to. This hidden conversion process is controlled by the symbol.toprimitive method.

function Temperature(degrees) {
    this.degrees = degrees;
}
Temperature.prototype[Symbol.toPrimitive] = function(hint) {
    switch (hint) {
        case "string":
            return this.degrees + "\u00b0"; // degrees symbol
        case "number":
            return this.degrees;
        case "default":
            return this.degrees + " degrees"; }};var freezing = new Temperature(32);
console.log(freezing + "!"); // "32 degrees!"
console.log(freezing / 2); / / 16
console.log(String(freezing)); 32 ° "/ /"
Copy the code