ES6 grammar is one of the most commonly asked questions in an interview, so let’s talk about using ES6 grammar in the workplace

Template string

The new template string in ES6 can use ${} to execute JS expressions. You can put variables, arithmetic calculations, ternary expressions, object properties, creating objects, calling functions, accessing array elements, and expressions in parentheses. Do not put branch/judge, loop, etc program structure.

let count = 1
let str = 'I am a string, followed by \${} can put an expression${ count }`
Copy the code

Let and const

I’ll take a little space here to explain variable promotion to help understand what the problem is with var and what the problem is solved by let and const.

Variable ascension

As of July 1, 1997, let and const have solved the following problems:

say()
console.log(myname)
var myname = 'good sauce

function say() {
    console.log('say hello')}Copy the code

As we all know, JS code is executed from the top down, the above code should be executed according to this logic:

  • When I get to line 1, because of the functionsayIt is not defined yet, so execution should report an error;
  • If I can get to line 2, because of the variablemynameIt is also undefined, so an error will be reported as well.

But when we actually put it into the browser, we get something like this:

'say hello' // say is running properly
undefined / / myname is undefined
Copy the code

Var name = ‘xiaoming’; var name = ‘xiaoming’

'say hello'
Uncaught ReferenceError: name is not defined // execute to myname error
Copy the code

We have the following conclusions:

  1. If an undeclared variable is used during JS execution, an error will be reported during JS execution
  2. No error is reported when used before a variable definition, and its value isundefined
  3. In afunctionThe function can run normally before it is defined

The so-called variable promotion refers to the “behavior” in which the JavaScript engine promotes the declaration part of variables and functions to the beginning of the code during the execution of JavaScript code. When a variable is promoted, it is given a default value, which is known as undefined. Let’s use code to describe how the above example is executed in the JS engine

// Compile phase
var myname = undefined
function say() {
    console.log('say hello')}// Execution phase
say()
console.log(myname) // undefined
myname = "Handsome sauce"
Copy the code

Now that we have a simple understanding of JS variable promotion, let, var, and const are discussed

The problem of var

Let’s start with var:

  • There is a variable promotion problem, which will disrupt the normal execution order of the program
  • Without block-level scope, variables within a code block can go beyond the scope of the code block, affect variables outside the code block and easily be overwritten without being noticed. You can read about scopes in πŸ‘‰
  • Variables declared in the global scope are mounted to the window
// Declare ahead with no block-level scope
console.log(a) // undefined can be accessed
console.log(b) // ❌ error: b is not defined

if (false) {
    var a = 1
}

// Easily overwritten or modified
var i = 10
for(var i = 0; i < 100; i++) {
    console.log(i) / / 0 ~ 99
}
console.log(i) / / 100

// It has been changed
var count = 10
changeCount()
console.log(count) / / 100

function changeCount() {
    count = 100
}

// var Variables declared in the global scope are mounted to the window
console.log(window.count) / / 100
Copy the code

Let and const resolve var problems

The new let and const ways of defining variables in ES6 address the above var problem. Let features:

  1. You cannot use this variable before declaring it
  2. You cannot declare two variables with the same name in the same scope
  3. Variables declared in the global scope are not mounted to the window
// Select * from *; // select *
console.log(b) // ❌ Cannot access 'b' before initialization
let b = 10

// Variables of the same name cannot be declared in the same scope
let b = 100 // ❌ cast error Identifier 'b' has already been declared

// Have block-level scope
console.log(a) // ❌ throw error A is not defined
if (false) {
    let a = 1
}

// Variables declared in the global scope are not mounted to the window
let num = 200
window.num // undefined
Copy the code

In addition to the let characteristics mentioned above, const has the following characteristics:

  1. You must have an assignment when you define it
  2. You cannot change the value of an assignment, but you can change the attributes inside the value if the assignment is of a reference type.
const x // ❌ error Missing initializer in const declaration

const num = 1
num = 2 // ❌ error Assignment to constant variable

const xiaoming = {
    name: 'Ming'.age: 18
}

xiaoming = null // ❌ error Assignment to constant variable

xiaoming.age = 19 // βœ… can be modified for internal attributes of reference types
Copy the code

Var, let, and const

The difference between var let const
Whether “block-level scope” is generated ❌ βœ… βœ…
Is there a variable lift βœ… ❌ ❌
Whether to save to window βœ… ❌ ❌
Can a variable be declared repeatedly in the same scope βœ… ❌ ❌
Whether it can be used in advance βœ… ❌ ❌
Whether the initial value must be set ❌ ❌ βœ…
Can modify the original type value or reference type address that is actually stored in a variable βœ… βœ… ❌

Arrow function

  1. This inside the arrow function is the same as this outside the function scope. (πŸ‘‰Click on theUnderstand whygetMynameCan point toxiaomingIn this objectnameAttributes)
const xiaoming = {
    name: 'Ming'.getName: () = > {
        console.log(this.name)
    },
    getMyname: function() {
        console.log(this.name)
    },
}

xiaoming.getName() // undefined

xiaoming.getMyname() // 'xiao Ming'
Copy the code
  1. Arrow functions are not boundarguments(It is better to use the remaining parameters).
const foo = () = > {
    console.log(arguments)
}
foo(1.2) // ❌ error arguments are not defined

const foo1 = (. args) = > {
    console.log(args)
}
foo1(1.2) / / βœ… [1, 2]
Copy the code
  1. There is nosuper,new.targetSo it can’t be used as a constructor, and a call to new will throw an error.
const Foo = (name) = > {
    this.name = name
}

const f = new Foo('Ming') // ❌ error Foo is not a constructor
Copy the code
  1. Since the arrow function does not have its own this pointer (or is permanently bound to an external scope), it passescall,applyThe first argument is ignored when the method is called, and subsequent arguments are valid.
const foo = (p, q) = > {
    console.log(this, p, q)
}

const xiaoming = { name: 'Ming' }
foo.call(xiaoming, 1.2) // window, 1, 2

foo.apply(xiaoming, [1.2]) // window, 1, 2

const temp = foo.bind(xiaoming, 1.2)
temp() // window, 1, 2
Copy the code

Fourth, the for of

To get the values of the elements inside the array, use for of. And arrays of classes that cannot use forEach also support for of.

const arr = [Ice Dadden Dadden."Xiao Ming"."Snow melt"]

for (let item of arr) {
    console.log(item) // "Bing Dwen Dwen" "Xiao Ming" "Xue Rong Rong"
}

function foo() {
    for (let item of arguments) {
        console.log(item) // "Bing Dwen Dwen" "Xiao Ming" "Xue Rong Rong"
    }
}

foo(Ice Dadden Dadden."Xiao Ming"."Snow melt")
Copy the code

For of does not care about the subscript position, only the element value, which has the advantage of handling arrays (or arrays like) of numeric subscripts. Disadvantages of for of:

  • Cannot get the index of the element, only the element value
  • You can’t control the order of traversal, you can only traverse from beginning to end
  • Cannot iterate over objects and associative arrays with subscripts named custom subscripts

Here is a table comparing several commonly used for:

for forEach for of for in
Digital subscript The index array βœ… βœ… βœ… ❌
Array like object βœ… ❌ βœ… ❌
Custom subscripts An associative array ❌ ❌ ❌ βœ…
object ❌ ❌ ❌ βœ…
  • The subscript is a number and select for of
  • Select for in if subscript is a custom string

5. Remaining parameters

For example, 🌰, we have an add function that adds several arguments passed in. This is easy in normal function functions and can be done by using arguments inside the function, but if we use arrow functions and can’t use arguments, then the remaining arguments come into play. On function parameters, use… Custom variable names are the syntax for the remaining parameters.

// Args can be named by itself
const add = (. args) = > {
    // args can get all the parameters passed in
    return args.reduce((a, b) = > a + b)
}
console.log(add([1.2.3.4.5.6])) / / 21

// Or use it this way
// You can fix N parameters of your own name, and the rest of the parameters are handled by the rest of the parameters
const add1 = (n, m, ... args) = > {
    return n + m + args.reduce((a, b) = > a + b)
}
console.log(add1([1.2.3.4.5.6])) / / 21
Copy the code

Residual arguments have three advantages over Arguments

  1. Support for arrow functions
  2. The resulting array is a pure array type
  3. Custom naming

Expansion operators

The syntax of the expansion operator is exactly the same as that of the remaining parameters, but it is used as an expansion operator when used on non-function parameters. The use of the expansion operator is very broad, let’s briefly mention two 🌰

  1. Taking the maximum
Math.max(... [1.2.36.21.3]) / / 36
Copy the code
  1. Shallow copy
const arr = [1.2.3.4.5]
const arr1 = [...arr]
console.log(arr === arr1) // false

const obj = {
	name: 'Ming'.age: 16
}
constobj1 = { ... obj }Copy the code
  1. Data consolidation
const arr = [1.2.3.4]
const arr1 = [7.10.0.9]
const arr2 = [100. arr,200. arr1]const obj = {
    name: 'Ming'.age: 16
}

const obj1 = {
    name: 'light'. obj }// obj1 = { name: '小明', age: 16 }
Copy the code

Seven, deconstruction

Before ES6, if you wanted to use an object member, an element in an array, you had to have the form “object name.”” or array name [subscript]”. This approach might be written as obj.p.r…….. if it is deeply nested Etc.

But deconstruction has been added in ES6 and comes in three forms:

  • An array of deconstruction

The array structure returns a value based on the array subscript, which starts at 0 and cannot be jumped in between

const [name, age] = ["Xiao Ming".18]
console.log(name, age) // "xiao Ming

// React useState uses arrays
const [state, dispatch] = useState(0)
Copy the code
  • Object to deconstruct

Object deconstruction is powerful, with renaming, key-value abbreviations, default Settings, and multi-level deconstruction

const obj = {
    name: "Xiao Ming".age: 18
}
// Before ES6, I want to get name and age in obj
console.log(obj.name, obj.age)

// className does not exist. You can set the default value to prevent deconstruction failure
const { name, age, className = "Class two, Three" } = obj

// Objects for multiple layers can also be deconstructed at once
const obj1 = {
    p: {
        k: {
            j: 1}}}const { p: { k: { j } } } = obj1
console.log(p, k , j)
Copy the code
  • Parameters of deconstruction

Parameter deconstruction is basically consistent with object deconstruction

function foo(params) {
    const { name = "Unknown", age = 16 } = params
    console.log(name, age)
}

foo({ name: "Xiao Ming" }) // "xiao Ming
Copy the code

Class and Inheritance

Two articles about object orientation πŸ‘‰ Features of object orientation and how to create objects and implement inheritance

The basic use

Class is a new way of creating objects in ES6 that is essentially the same as the function constructor, just a syntactic sugar.

class Person {
    constructor(name, age, className) {
        this.name = name
        this.age = age
        if (className) {
            this.className = className
        }
    }
  
    static className = Class 2 one Year // Static property (not this, only Person)
  
    say(msg) { // Static attributes
        console.log(`The ${this.name}He said these words:${msg}`)}}const xiaoming = new Person('Ming'.18.'Third year')
console.log(xiaoming.className) // 'grade 3'

const xiaoguang = new Person('light'.16)
console.log(xiaoguang.className)

Copy the code

This is equivalent to using the constructor as follows:

function Person(name, age) {
    this.name = name
    this.age = age
}

Person.className = Class 2 one Year // Static attributes

Person.prototype.say = function(msg) { // Prototype method
    console.log(`The ${this.name}He said these words:${msg}`)}Copy the code

inheritance

Let’s take a simple airplane fighting game as an example

There are two types of objects that move down from the screen in aircraft battles: enemy planes and parachutes. They share attributes x, y for the current position, and the fly method, as well as their own specific attributes or methods

class EnemyPlane { / / the enemy
  constructor(x, y, score) {
    this.x = x
    this.y = y
    this.score = score
  }
  fly() {
    console.log('After judging the current position, select a movable position at random')}payScore() {
    console.log('Shot down, return score'.this.score)
    return this.score
  }
}

class Parachute { / / the parachute
  constructor(x, y, award) {
    this.x = x
    this.y = y
    this.award = award
  }
  fly() {
    console.log('After judging the current position, select a movable position at random')}payAward() {
    console.log("Shot down, return to prize.".this.award)
    return this.award
  }
}
Copy the code

We found that there was a lot in common between the two, so we optimized the code for that — through inheritance

class Enemy {
  constructor(x, y) {
    this.x = x
    this.y = y
  }
  fly() {
    console.log('After judging the current position, select a movable position at random')}}class EnemyPlane extends Enemy {
  constructor(x, y, score) {
    
    this.score = score
  }
  payScore() {
    console.log('Shot down, return score'.this.score)
    return this.score
  }
}

class Parachute extends Enemy {
  constructor(x, y, award) {
  
    this.award = award
  }
  payAward() {
    console.log("Shot down, return to prize.".this.award)
    return this.award
  }
}
Copy the code

The above code actually doesn’t work, but let’s ignore that for now. Let’s see if we inherit fly from our parent class.

The fly method was found in the subclass by a prototype chain lookup, indicating that the inheritance was successful. Let’s try one more enemy plane

“This” EnemyPlane “EnemyPlane” EnemyPlane “EnemyPlane” EnemyPlane “EnemyPlane” EnemyPlane “EnemyPlane” EnemyPlane “EnemyPlane” EnemyPlane


class EnemyPlane extends Enemy {
  constructor(x, y, score) {
    super(x, y) // By calling super and passing x, y
    this.score = score
  }
  payScore() {
    console.log('Shot down, return score'.this.score)
    return this.score
  }
}

class Parachute extends Enemy {
  constructor(x, y, award) {
    super(x, y)
    this.award = award
  }
  payAward() {
    console.log("Shot down, return to prize.".this.award)
    return this.award
  }
}
Copy the code

When we call super, we implement the whole inheritance process.

Nine, Promise

In actual development, multiple asynchronous tasks are often needed to be executed in sequence, and the later asynchronous tasks depend on the results of the previous asynchronous tasks. We give some example codes, in which there are some discrepancies between the log information and the actual execution, which is mainly convenient to understand. Specific event execution timing and content can be viewed at πŸ‘‰ JavaScript’s event execution visualization site.

Tips: We use setTimeout to simulate asynchronous requests

function task1() {
  console.log('task1 enters the queue ')
  // The task1 function is not queued, but the setTimeout function
  // The implementation visualization website mentioned above can be viewed without too much explanation
  setTimeout(function(){
    console.log('Task1 completed')},6000)}function task2() {
  console.log('task2 enters the queue ')
  setTimeout(function(){
    console.log('Task2 completed')},5000)}function task3() {
  console.log('task3 enters the queue ')
  setTimeout(function(){
    console.log('Task3 execution completed')},4000)
}

task1()
task2()
task3()
Copy the code

If we follow the above simultaneous execution, then the execution flow of the three tasks is as shown in the figure below, which cannot be executed in the order we want and cannot pass parameters to the tasks to be executed later

Before promises came out, we used callbacks, passing a subsequent function (such as task2) as an argument to the previous function (task1), and then executing the argument after the asynchronous task in the previous function (the callback) had completed.

function task1(callback) {
  console.log('task1 enters the queue ')
  setTimeout(function(){
    console.log('Task1 completed')
    callback(1)},6000)}function task2(res, callback) {
  console.log('task2 enters the queue ')
  setTimeout(function(){
    console.log('Task2 completed', res)
    callback(res + 1)},5000)}function task3(res) {
  console.log('task3 enters the queue ')
  setTimeout(function(){
    console.log('Task3 execution completed', res)
  }, 4000)
}

task1(
  function(res) { 
    task2(res, task3)
  }
)
// 'task1 enters the queue '
  // 'task1 completes execution '// 6s later
  // 'task2 enters the queue '
    // 'task2 has completed ', 1 // 6s + 5s later
    // 'task3 enters the queue '
      // 'task3 has completed ', 2 // after 6s + 5s + 4s
Copy the code

Too many callback functions can result in a deeply nested structure called callback hell, which is extremely inelegant and difficult to develop and maintain.

Let’s use promise to see (note that setTimeout is still the time it takes to simulate an asynchronous request)

function task1() {
  console.log('task1 enters the queue ')
  return new Promise((resolve, reject) = > {
    setTimeout(function(){
      console.log('Task1 completed')
      resolve(1)},6000)})}function task2(res) {
    console.log('task2 enters the queue ')
    return new Promise((resolve, reject) = > {
        setTimeout(() = > {
            console.log('Task2 completed')
            resolve(res + 1)},5000)
    })
}

task1().then(res= > task2(res)) / / short task1 (). Then (task2)
Copy the code

The arguments passed to resolve in Task1 are passed to the functions accepted in THEN, and in the example above res in THEN is the 1 passed in resolve.

In interviews, interviewers often ask, What problem did Promise solve? Some of the younger candidates might answer, solving the callback hell problem. But in addition to solving the problem of callback hell, I think Promise provides a way to do asynchronous programming in JavaScript and good exception handling

function task1(val) {
  console.log('task1 enters Task Queue')
  return new Promise((resolve, reject) = > {
    setTimeout(function() {
      console.log('Task1 completed')
      if (val) {
        resolve('Task1 executed successfully')}else {
        reject('Oops, Task1 is wrong') // Use reject when exceptions occur}},6000)})}function task2(val) {
    console.log('Task2 enters Task Queue')
    return new Promise((resolve, reject) = > {
        setTimeout(() = > {
            console.log('Task2 completed')
            if (val) {
                resolve('Task2 executed successfully')}else {
                reject('Oops, Task2 is wrong')}},5000)})}function task3(val) {
    console.log('task3 enters Task Queue')
    return new Promise((resolve, reject) = > {
        setTimeout(() = > {
            console.log('Task3 execution completed')
            if (val) {
                resolve('Task3 executed successfully')}else {
                reject('Oops, Task3 is wrong')}},4000)
    })
}


task1(false)
  .then(task2)
  .then(task3)
  .catch(err= > {
    console.log('Error handling', err)
  })
  .finally(() = > {
    console.log('Execute anyway, regardless of previous execution state')})// 'task1 starts executing '
  // 'task1 completed '6s
  // 'error handling ',' Oops, task1 is wrong '
  // 'execute anyway, regardless of previous execution state'
Copy the code

In addition to addressing callback hell, I think Promise offers asynchronous programming in JavaScript and good exception handling.

Again, I recommend this visual site that describes JavaScript event loops online, which is very useful for understanding event loops

Ten, async/awiat

Promise solves callback hell, but the nesting problem is still there and bad for understanding, and async/await solves it. Async /await provides a form of converting asynchrony to synchronous programming

async function task1() { // Asynchronous request
  return new Promise((resolve) = > {
    setTimeout(() = > {
      resolve(1)},1000)})}const res1 = await task1()
console.log(res1) / / 1
const res2 = await task1()
console.log(res2) / / 1
Copy the code

When calling an asynchronous function, simply prefix the call with await to make the code more understandable as writing synchronous code. Async /await has two characteristics when used:

  1. But as long as it’s writtenasyncKeyword function, when called, will return onepromise, which makes it necessary to prefix the call regardless of whether our function is an asynchronous process internallyawait.
  2. In the use ofawaitWhen,awaitThe function must beasyncCan be used, otherwise reported syntax error. This also causes async syntax contamination.
// Specifically for syntax pollution demo
async function task1() { // Asynchronous request
  return new Promise((resolve) = > {
    setTimeout(() = > {
      resolve(1)},1000)})}async function getRes() {
  const res = await task1() // Let's say we make an asynchronous request here
  return res
}


function foo() {
  // Since getRes is async, we must use await here
  // But to use await, foo must add async
  // So if there are multiple calls, there will be a pollution
  const n = getRes()
  console.log(n) // Get a Promise
}

const m = await getRes()
console.log(m)
foo()
Copy the code

We sum up Promise with async/await: Promise:

  1. Fixed callback hell in asynchronous requests.
  2. Provides a way to program asynchronously in JavaScript.

Async/Await:

  1. Solved the nesting problem in promises.
  2. Change asynchronous code to synchronous code writing.
  3. Async /await must be used together and may cause syntax contamination.

This article has been written on and off for a long time πŸ˜‚ a little sorry myself, continue to refueling!