当前位置:网站首页>Deep understanding of ES6 (1)

Deep understanding of ES6 (1)

2021-06-22 00:57:53 P.yh

Why learn ES6

here ES6 It's actually JavaScript stay 2015 A version of the release , The full name is ECMAScript6. This version is right JavaScript Great changes have taken place in all aspects of the language . Well known ,JavaScript Since its birth, the language has suffered countless criticisms . At the beginning of the design of this language , The designer just positioned it as a simple scripting language , No very advanced design principles have been introduced . But what I didn't expect was the emergence of browsers , And subsequently NodeJS It's all driven JavaScript The development of . It's used in a wide range of scenarios , More users , Naturally, the potential problems of the language emerge one by one .ES6 To solve these problems , And keep it down compatible .

Study ES6 In fact, we can have a good understanding of JavaScript This language . Know what problems the language has had before , Why are these problems called problems ?ES6 And how to solve these problems .

On the other ES6 This knowledge of , I mainly saw Understanding ECMAScript 6 This book . The author's narration is simple and clear , And the examples are easy to understand . This article mainly extracts some important contents from the book .


Block binding

stay ES6 Before ,JS Variables in have only two scopes , Namely overall situation and function . Before we talk about scope , We need to know JS Before running, there is an operation similar to precompile , This operation will raise the declaration of all variables or functions . Note that this is just a statement of variables , Does not include value binding of variables , Consider the following code :

function getValue(condition) {
    if (condition) {
        var value = "blue";
        // other code
        return value;
    } else {
        // value exists here with a value of undefined
        return null;
    }
    // value exists here with a value of undefined
}
 Copy code 

If you are right about JS If you don't understand , You might simply think of variables as value The statement of only takes place in condition For the truth , But because the variable declaration says , The code above is equivalent to the code below :

function getValue(condition) {
    var value;
    if (condition) { 
        value = "blue";
        // other code
        return value;
    } else {
        return null;
    }
}
 Copy code 

Based on these ,ES6 Before , There are several problems with variable declaration and value binding :

  • There is no block scope , Confusion of variable names can easily cause problems .
  • Because the variable is up , Causes variables to be allowed to be used before they appear . It's confusing for users

In order to solve the above two problems ,ES6 Introduced block scope (Block Scopes), It can also be called Lexical scope (lexical Scopes). At the same time, in order to downward compatible with the old version , Declare variables with lexical scope , We need to adopt let and const. There is not much to say about the use of these two keywords , They also solve the two problems mentioned above .

Here's an explanation TDZ(temporal dead zone), For using let as well as const Declared variables , In scope , But the area before the variable declaration is called TDZ, Accessing variables in this area will result in an access error ( This is not to say that variables have not been declared yet , It's about in a lexical environment , Variable in TDZ Is not accessible in ).

Based on the above discussion ,ES6 Then the recommended practice is , In general const Declare variables or functions , If you want to change this variable, use let. This can avoid some potential problems .


character string

Character access

stay ES6 Before ,JS The string will default to a 16 bits Sequence , Each character will be 16 bits (code unit) To express . And all string related built-in methods are based on this . however Unicode The emergence of the Internet makes this a problem , because Unicode It's more than 16 bits. Here's an example :

let text = "𠮷";
console.log(text.length);  // 2
console.log(text.charCodeAt(0));  // 55362
console.log(text.charCodeAt(1));  // 57271
 Copy code 

The single character here is in JS It looks like it's actually two characters , Because this character is actually written by two 16 bits Unit representation of .

ES6 Some methods are provided for character access in strings , such as codePointAt,codePointLength, as well as fromCodePoint

let text = "𠮷a";
console.log(text.charCodeAt(0)); // 55362
console.log(text.charCodeAt(1)); // 57271
console.log(text.charCodeAt(2)); // 97

console.log(text.codePointAt(0)); // 134071
console.log(text.codePointAt(1)); // 57271
console.log(text.codePointAt(2)); // 97

console.log(codePointLength("abc")); // 3
console.log(codePointLength("𠮷bc")); // 3

console.log(String.fromCodePoint(134071)); // "𠮷"
 Copy code 

meanwhile , We can also pass codePointAt To simply judge whether you can use a 16 bits The sequence representation of

function is32Bit(c) {
    return c.codePointAt(0) > 0xFFFF;
}
console.log(is32Bit("𠮷")); // true
console.log(is32Bit("a")); // false
 Copy code 

Built in functions

stay ES6 Before checking whether a string contains a substring , Only through indexOf, It sometimes seems unnatural .ES6 Provides includesstartsWithendsWith To do this , Their usage is known from the method name , It's going to be simpler :

let msg = "Hello world!";
console.log(msg.startsWith("Hello")); // true
console.log(msg.endsWith("!")); // true
console.log(msg.includes("o")); // true

console.log(msg.startsWith("o")); // false
console.log(msg.endsWith("world!")); // true
console.log(msg.includes("x")); // false

console.log(msg.startsWith("o", 4)); // true
console.log(msg.endsWith("o", 8)); // true
console.log(msg.includes("o", 8)); // false
 Copy code 

Another practical built-in approach is repeat, As the name suggests, it is to repeat a string to form a new string :

console.log("x".repeat(3)); // "xxx"
console.log("hello".repeat(2)); // "hellohello"
console.log("abc".repeat(4)); // "abcabcabcabc"
 Copy code 

The main purpose of this built-in method is to format the output of some text .

Template literal amount

Templates literally provide DSL, A language or grammar produced for a particular task , Compared to general programming languages , Its purpose is clearer . Template literal quantity is mainly to solve ES6 Some of the previous string representation problems :

  • Strings are not well represented in multiple lines
  • The problem of string formatting
  • HTML Translation question

The usage of template literal quantity is also very simple , In fact, it is to use ` Instead of ordinary quotation marks :

let message = `Hello world!`;
console.log(message); // "Hello world!"
console.log(typeof message); // "string"
console.log(message.length); // 12
 Copy code 

Again , Template literal size can also easily handle string formatting :

let count = 10,
    price = 0.25,
    message = `${count} items cost $${(count * price).toFixed(2)}.`; 
console.log(message); // "10 items cost $2.50."
 Copy code 

in addition , The important thing is , Template literal amount also belongs to JS The expression of , So you can nest each other :

let name = "pyhhou",
    message = `Hello, ${
        `my name is ${ pyhhou }`
    }.`;
console.log(message); // "Hello, my name is pyhhou."
 Copy code 

Template literal quantity also has a custom usage , It's the label template . Allows the user to use information based on the literal amount of the template , Customize the content and form of the last string :

function passthru(literals, ...substitutions) {
    let result = "";
    // run the loop only for the substitution count
    for (let i = 0; i < substitutions.length; i++) {
        result += literals[i];
        result += substitutions[i];
    }
    // add the last literal
    result += literals[literals.length - 1];
    return result;
}

let count = 10,
    price = 0.25,
    message = passthru`${count} items cost $${(count * price).toFixed(2)}.`; 
console.log(message); // "10 items cost $2.50."
 Copy code 

There are some built-in template functions , such as Shring.raw You can make strings not translate special symbols :

let message1 = `Multiline\nstring`,
    message2 = String.raw`Multiline\nstring`;

console.log(message1); // "Multiline
                       // string"
console.log(message2); // "Multiline\\nstring"
 Copy code 

function

Default parameters

stay ES6 Before ,JS All functions do not support default parameters . This brings a lot of inconvenience to parameter transfer and parameter judgment of function .ES6 Start supporting default parameters . The default parameter is JS Some of the original mechanisms have brought some changes , Let's see .

In the original version ,arguments Object holds all the parameters of the current function . And in non strict mode , Parameters and arguments Objects are also related :

function mixArgs(first, second) {
    console.log(first === arguments[0]); // true
    console.log(second === arguments[1]); // true
    first = "c";
    second = "d";
    console.log(first === arguments[0]); // true
    console.log(second === arguments[1]); // true
}
mixArgs("a", "b");
 Copy code 

however ,ES6 The default parameters and arguments The relationship of objects will behave as it did in the previous strict mode :

// not in strict mode
function mixArgs(first, second = "b") {
    console.log(arguments.length); // 1
    console.log(first === arguments[0]); // true
    console.log(second === arguments[1]); // false
    first = "c";
    second = "d"
    console.log(first === arguments[0]); // false
    console.log(second === arguments[1]); // false
}
mixArgs("a");
 Copy code 

Actually , Function parameter passing is similar to variable declaration and value binding , For example, the following situations are allowed :

function add(first, second = first) {
    return first + second;
}
console.log(add(1, 1)); // 2
console.log(add(1)); // 2
 Copy code 
function getValue(value) {
    return value + 5;
}

function add(first, second = getValue(first)) {
    return first + second;
}

console.log(add(1, 1)); // 2
console.log(add(1)); // 7
 Copy code 

That's why , Parameters also exist as we mentioned before TDZ The phenomenon , For example, the following codes will report errors :

function add(first = second, second) {
    return first + second;
}

console.log(add(1, 1)); // 2
console.log(add(undefined, 1)); // throws an error
 Copy code 

Unknown parameter

In actual development , We tend to pass in multiple arguments to a function , And the number of these parameters is uncertain . Although in ES6 Before ,arguments It seems that this kind of problem can be solved . But this approach does not guarantee the simplicity of the code , And this kind of code is not easy to maintain and expand .ES6 Residual parameters are introduced to solve this problem :

function pick(object, ...keys) {
    let result = Object.create(null);
    for (let i = 0, len = keys.length; i < len; i++) {
        result[keys[i]] = object[keys[i]];
    }
    return result;
}
 Copy code 

About the residual parameter , There are two points to pay attention to :

  • The remaining arguments must exist at the end of the function argument list , Otherwise you will report a syntax error
  • In the literal assignment of an object , The remaining parameters cannot be used in setter Function

Clarify the dual role of functions

stay ES6 Before , Functions can be called in normal ways , It can also be used as object generation . When used to generate objects , You can add... Before the function new. The reason why there is such a dual characteristic , It's because in general functions there are 2 There are two internal attributes ( Internal slot ), Namely [[Call]] and [[Constructor]].[[Call]] Responsible for function call and execution . When the function call is preceded by new, that [[Constructor]] It will be called , This internal slot is mainly responsible for generating new objects , And execute the body of the function , And then this Bind to a newly generated object .

stay ES6 Before , We need to pass instanceof To determine whether the function is used new

function Person(name) {
    if (this instanceof Person) {
        this.name = name; // using new
    } else {
        throw new Error("You must use new with Person.");
    }
}
var person = new Person("Nicholas");
var notAPerson = Person("Nicholas"); // throws an error
 Copy code 

But it's not rigorous , There are other ways to avoid this kind of judgment :

function Person(name) {
   if (this instanceof Person) {
       this.name = name; }
   else {
       throw new Error("You must use new with Person.");
   }
}
var person = new Person("Nicholas");
var notAPerson = Person.call(person, "Michael"); // works!
 Copy code 

ES6 Added new.target This meta property , This property is only used in functions new And then there are values :

function Person(name) {
    if (typeof new.target !== "undefined") {
        this.name = name;
    } else {
        throw new Error("You must use new with Person.");
    }
}
var person = new Person("Nicholas");
var notAPerson = Person.call(person, "Michael"); // error!
 Copy code 

It should be noted that , Use... Outside of functions new.target If you do, you will make grammatical mistakes .

Arrow function

This can be regarded as ES6 One of the biggest innovations in function . The arrow function can actually be seen as JS Is an anonymous function of , And it has a simple grammar . in addition , Arrow function abandons many confusing designs in traditional functions , For example, the arrow function doesn't have arguments object , The arrow function doesn't change either this The binding of , Of course, it can't be used as a constructor to generate new objects .

Let's take a look at the properties of arrow functions :

  • non-existent thissuperarguments、 as well as new.target The binding of

    The value of the above attributes in the arrow function is the value of the outer function

  • Cannot add... When called new

    The arrow function doesn't [[Constructor]] Internal slot

  • There is no prototype

The expression of arrow function is much simpler than traditional function :

let getTempItem = id => ({ id: id, name: "Temp" });

// effectively equivalent to:
let getTempItem = function(id) {
    return {
        id: id,
        name: "Temp"
    };
};
 Copy code 

Arrow function is also to solve the problem of traditional function this The binding problem of , For example, the following code is bound to itself by default because of the inner function this And report wrong :

let PageHandler = {
    id: "123456",
    init: function() {
        document.addEventListener("click", function(event) {
            this.doSomething(event.type); // error
        }, false);
    },
    doSomething: function(type) {
        console.log("Handling " + type + " for " + this.id);
    }
};
 Copy code 

Replacing the inner function with the arrow function can solve this problem :

let PageHandler = {
    id: "123456",
    init: function() {
        document.addEventListener("click",
            event => this.doSomething(event.type),
            false
        );
    },
    doSomething: function(type) {
        console.log("Handling " + type + " for " + this.id);
    }
};
 Copy code 

Of course , Although arrow functions are different from ordinary functions , You can still use something like call()apply()bind() These operations , Just say this Will not be bound :

var sum = (num1, num2) => num1 + num2;
console.log(sum.call(null, 1, 2)); // 3
console.log(sum.apply(null, [1, 2])); // 3

var boundSum = sum.bind(null, 1, 2);
console.log(boundSum()); // 3
 Copy code 

so , Compared with traditional functions , The arrow function is designed as a lightweight structure , The usage is simple and does not take up too much resources .

Function call optimization

When other functions are called at the end of a function , Here you can move the current function out of the function call stack , This saves stack space , Here's an example :

function doSomething() {
    return doSomethingElse();   // tail call
}
 Copy code 

ES6 In strict mode , Provides such optimizations :

"use strict";
function doSomething() {
    // optimized
    return doSomethingElse();
}
 Copy code 

Be careful , Here we need to pay attention to “ Tail call ” The concept , Some calls exist at the end of the function , But it's not a tail call :

"use strict";
function doSomething() {
    // not optimized - no return
    doSomethingElse();
}
 Copy code 
"use strict";
function doSomething() {
    // not optimized - must add after returning
    return 1 + doSomethingElse();
}
 Copy code 
"use strict";
function doSomething() {
    var num = 1,
    func = () => num;
    // not optimized - function is a closure
    return func();
}
 Copy code 

Tail function optimization in general function call effect is not obvious , But if it's a recursive call , The difference will be very big , Here's an example :

function factorial(n, p = 1) {
    if (n <= 1) {
        return 1 * p;
    } else {
        let result = n * p;

        // optimized
        return factorial(n - 1, result);
    }
}
 Copy code 

If there is a tail call optimization blessing , Even if the input parameters are particularly large , The above functions will not cause stack overflow .

版权声明
本文为[P.yh]所创,转载请带上原文链接,感谢
https://chowdera.com/2021/06/20210602181439747t.html