FreeCodeCamp (Ladder required)

The first translation ~ if there is a mistake, please give advice ~

Complete JavaScript Manual

JavaScript is one of the most popular programming languages in the world and is now widely used outside of the browser. In recent years, the rise of Node.js has unlocked the realm of back-end development that has long been dominated by traditional server-side languages such as Java, Ruby, Python, and PHP.

This complete JavaScript manual follows the 80/20 rule: Learn 80% of JavaScript 20% of the time.

Learn all you need to know about JavaScript!

Note: You can get the Markdown, ePub, Mobi (extract code: R4i3) versions of this complete JavaScript manual for better reading.

introduce

JavaScript is one of the most popular programming languages in the world. It has come a long way since it was born 20 years ago. As the first and only scripting language to be natively supported by web browsers, it’s dead on its head.

In the beginning, it was not as powerful as it is today, and was mainly used in all kinds of fancy animation 😆 and in dynamic HTML (DHTML), which was widely regarded as a marvel at the time. As web platforms evolve, JavaScript also has a responsibility to get better to meet the needs of one of the most widely used ecosystems in the world. The platform itself has introduced a lot of things, including browser apis, and language features have grown a lot.

Now JavaScript can also operate databases and develop applications, even embedded applications, mobile apps, TV applications, and so on. What was once a small language in the browser is now the most popular language in the world

Basic JavaScript Definitions

JavaScript is a programming language like this:

  • Advanced: This allows you to focus more on your own logic and ignore the details of the machine it is currently running on. JavaScript automatically manages memory through the garbage collector, allowing you to focus on code rather than managing memory, and it also provides a number of constructors that allow you to handle powerful variables and objects.
  • Dynamic: In contrast to static languages that execute at compile time, dynamic languages execute at run time. This has its pros and cons. JavaScript gives us great functionality like dynamic typing, delayed binding, reflection, functional programming, Object Runtime variations, closures, and more.
  • Dynamic typing: Variables are not typed. You can rebind variables of any type, such as an integer value to a declared string variable.
  • Weak typing: In contrast to strong typing, weakly typed languages do not enforce the type of an object. This makes operations more flexible, but there is no type checking to ensure type safety (TypeScript and Flow aim to improve this).
  • Interpreted: JavaScript is often thought of as an interpreted language, meaning that no program needs to be compiled before it can run, as opposed to C, Java, or Go. In fact, browsers compile before JavaScript is executed for performance reasons, but this happens naturally and requires no additional action.
  • Multiparadigm: JavaScript does not enforce any fixed programming paradigm, unlike Java’s object-oriented programming or C’s imperative programming. In JavaScript, you can write object-oriented programming using the prototype and classes syntax provided in ES6. You can also write functional programming style code through its first-class functions, and even write programs in imperative (c-like) style.

Just to be clear, JavaScript has nothing to do with Java, and this was an unfortunate naming choice, but we have to live with it.

The JavaScript version

Let me introduce ECMAScript. We have a complete guide dedicated to ECMAScript, which you can delve into, but for now, all you need to know is that ECMAScript (also known as ES) is the name of the JavaScript standard. JavaScript is an implementation of the ECMAScript standard, which is why you hear about ES6, ES2015, ES2016, ES2017, ES2018, etc.

A long time ago, the version of JavaScript running in all browsers was based on ECMAScipt 3. Version 4 was cancelled due to feature creep (they tried to add many features at once). While ES5 is a huge version of JavaScript, ES2015 (also known as ES6) is also a major update to JavaScript.

Since then, the standards committee has decided to update the version annually to avoid long iterations between versions and to speed up feedback.

Currently, the latest approved version of JavaScript is ES2017.

ECMASCRIPT

Whenever you read about JavaScript, you will inevitably see the following terms:

  • ES3
  • ES5
  • ES6
  • ES7
  • ES8
  • ES2015
  • ES2016
  • ES2017
  • ECMAScript 2017
  • ECMAScript 2016
  • ECMAScript 2015

What are these? They both refer to the JavaScript standard, called ECMAScript.

ECMAScript is the standard on which JavaScript is based and is often referred to simply as ES.

In addition to JavaScript, ECMAScript standards include:

  • ActionScript(Flash scripting language), has fallen out of fashion since the official decision not to maintain Flash from 2020.
  • JScript(Microsoft), at first only Netscape supported JavaScript, but as the browser wars intensified, Microsoft implemented a version only supported by Internet Explorer.

But there is no doubt that JavaScript is still the most common ES implementation.

Why the strange name? Ecma International is the Swiss standards Association responsible for setting International standards.

When JavaScript is created, it is committed to Ecma by Netscape and Sun Microsystems as ECMA-262, also known as ECMAScript. According to Wikipedia, the joint press release issued by Netscapte and Sun Microsystems, the Java manufacturer, may shed some light on the reasons for the naming, which may include Microsoft’s legal and branding issues with the commission.

After IE9, Microsoft stopped calling the ES implementation JScript in browsers and started calling it JavaScript (at least I can’t quote any).

So, as of 201x, the only popular language that supports the ECMAScript standard is JavaScript.

The latest version of ECMAScript

The current version of ECMAScript is ES2018, or ES9, which will be released in June 2018.

When will the next version be released?

Normally, JavaScript releases a standard version every summer, so we could see ECMAScript 2019 (ES2019 or ES10) in the summer of 2019, but that’s just speculation.

What is TC39

TC39 is the JavaScript Development Committee.

TC39 members include JavaScript and browser vendors, including Firefox, Google, Facebook, Apple, Microsoft, Intel, PayPal, SalesForce, and more.

Each release of a standard version must go through a different phase of the proposal.

ES version

I find that the ES version is sometimes referred to by the version number, and sometimes by the year, which can be confusing.

Prior to ES2015, the ECMAScript standard was usually named by its version number, so ES5 is the official name of the ECMAScript standard updated in 2009.

Why is that? When ES2015 was released, the name changed from ES6 to ES2015, but it was too late, people still habitually called ES6, and the community did not abandon the name – the world still refers to the ES version by version number.

This chart can help clarify your thinking:

version The official name The release date
ES9 ES2018 In June 2018
ES8 ES2017 In June 2017
ES7 ES2016 In June 2016
ES6 ES2015 In June 2015
ES5.1 ES5.1 In June 2011
ES5 ES5 In December 2009
ES4 ES4 abandoned
ES3 ES3 In December 1999
ES2 ES2 In June 1998
ES1 ES1 In June 1997

ES.Next always refers to future versions of JavaScript. As of this writing, ES9 has been released and ES.next is ES10.

ES6 improvement

ECMAScript 2015, also known as ES6, is the foundation version of the ECMAScript standard, four years after the release of the previous version, ECMAScript 5.1, which was renamed after years. So it shouldn’t be called ES6 (although everyone does), but ES2015.

ES5 took ten years to perfect, from 1999 to 2009, and while it is an important release for the language, it has passed too long to be worth discussing how the code before ES5 worked.

From ES5.1 to ES6, the JavaScript language has significant new features and key updates for better practice. To understand the basic functionality of ES2015, refer to pages 250 to 600 of the specification document.

Important updates in ES2015 include:

  • Arrow function
  • Promises
  • Generators
  • letconst
  • Classes
  • Multiline string
  • Template string
  • Parameter Default Value
  • Extended operator
  • Deconstruction assignment
  • Enhanced object literals
  • for.. Of circulation
  • The Map and Set

In this guide, I will dedicate each chapter to them. Let’s get started!

Arrow function

The arrow function changes the way most JavaScript code is written and works.

Visually, it’s simpler, and welcome changes like:

const foo = function foo() {
  // ...
}
Copy the code

To:

const foo = (a)= > {
  // ...
}
Copy the code

If the function body had only one line, it could look like this:

const foo = (a)= > doSomething()
Copy the code

If there is only one argument, it could look like this:

const foo = param= > doSomething(param)
Copy the code

It does not introduce any incompatible changes compared to regular functions and works just as before.

New this scope

The this in the arrow function inherits from the execution context.

In previous regular functions, this usually referred to the nearest function. In the arrow function, however, this problem is gone, so you don’t have to rewrite var that = this again.

Promises

Promises helps us solve the famous “callback hell” problem, although it introduces a more complex problem that can be solved with the more advanced async constructor in ES2017.

Before ES2015, JavaScript developers can use different libraries (jQuery, Q, deferred.js, Vow…). Make Promises look like Promises. The standard sets out a more general approach.

By using Promises, you can refactor this code:

setTimeout(function() {
  console.log('I promised to run after 1s')
  setTimeout(function() {
    console.log('I promised to run after 2s')},1000)},1000)
Copy the code

Is equal to:

const wait = (a)= > new Promise((resolve, reject) = > {
  setTimeout(resolve, 1000)
})
wait().then((a)= > {
  console.log('I promised to run after 1s')
  return wait()
})
.then((a)= > console.log('I promised to run after 2s'))
Copy the code

Generators

A generator is a special function that pauses output, resumes it later, and allows other code to run in the meantime.

The code itself determines that it must wait to let other code run “in queue” order, and reserves the right to resume operations “when waiting” is complete. All of this is done with a simple keyword, yield. When a generator contains this keyword, the code is paused. Generators can contain many yield keywords, so they can be paused many times, and are identified by the *function keyword, not to be confused with pointer backreference operators in low-level languages such as C, C++, or Go.

In JavaScript, generators enable entirely new programming paradigms such as:

  • Bidirectional communication in generator operation
  • A persistent while loop that does not freeze the program

Here’s an example of how generators work:

function *calculator(input) {
    var doubleThat = 2 * (yield (input / 2))
    var another = yield (doubleThat)
    return (input * doubleThat * another)
}
Copy the code

Initialization:

const calc = calculator(10)
Copy the code

Then start iterator:

calc.next()
Copy the code

For the first iteration, the code returns this:

{
  done: false
  value: 5
}
Copy the code

What happens: When the function starts running, input = 10 is passed as an argument to the generator’s constructor until yield returns the contents of Yeild: input / 2 = 5. So we get a value of 5 that tells us that the iteration is not complete (just that the function is paused).

In the second iteration, we pass in 7:

calc.next(7)
Copy the code

Then we get:

{
  done: false
  value: 14
}
Copy the code

7 is the doubleThat value.

Note: You might think of input / 2 as this parameter, but it is only the return value of the first iteration. This time we skip this step and use the new input value 7 multiplied by 2.

We continue with the second iteration, which returns doubleThat with a value of 14.

Next, the last iteration, we pass in 100:

calc.next(100)
Copy the code

Returns:

{
  done: true
  value: 14000
}
Copy the code

At the end of the iteration (without the yeild keyword), we get the value of (input * doubleThat * another) : 10 * 14 * 100.

Let and const

Var is the traditional function scope.

Let is the new method of declaring variables with block-level scope. This means that in a for loop, variables that use let declarations within an if statement or plain block do not “escape” from the block, while var variables are promoted to function definitions.

Const is similar to like, but cannot be changed.

Looking ahead to JavaScript, the VAR declaration will disappear, leaving only lets and const.

More specifically, const has become surprisingly popular today because of its immutable nature.

Classes

Traditionally, JavaScript has been the only language based on archetypal inheritance. Switching from a language based on class inheritance to JavaScript can be confusing for programmers, but ES2015’s introduction of classes as syntactic sugar for implementing inheritance within JavaScript has changed the way we write JavaScript programs.

Inheritance is now very simple, similar to other object-oriented programming languages:

class Person {
  constructor(name) {
    this.name = name
  }
  hello() {
    return 'Hello, I am ' + this.name + '. '}}class Actor extends Person {
  hello() {
    return super.hello() + ' I am an actor.'}}var tomCruise = new Actor('Tom Cruise')
tomCruise.hello()
Copy the code

The code above will print: “Hello, I am Tom Cruise. I am an actor.”

Classes does not explicitly declare class variables; you must initialize them in the constructor.

Constructor

Classes has a special method called constructor, which is called when a class is instantiated using new.

Getters and Setters

You can declare a getter property like this:

class Person {
  get fullName() {
    return `The ${this.firstName} The ${this.lastName}`}}Copy the code

Declare setter properties in the same way:

class Person {
  set age(years) {
    this.theAge = years
  }
}
Copy the code

modular

Before ES2015, there were at least three major modularity standards that split the community:

  • AMD
  • RequireJS
  • CommonJS

ES2015 has developed a unified modular standard.

The import module

Through the import… from … Import module:

import * from 'mymodule'
import React from 'react'
import { React, Component } from 'react'
import React as MyLibrary from 'react'
Copy the code

Export module

You can use the keyword export to export the contents of a written module to another module:

export var foo = 2
export function bar() { / *... * / }
Copy the code

Template string

Template strings are new ways to create strings:

const aString = `A string`
Copy the code

The ${a_variable} syntax makes it easy to insert the value of an expression into a string:

const var = 'test'
const string = `something The ${var}` //something test
Copy the code

You can also execute more complex expressions like this:

const string = `something The ${1 + 2 + 3}`
const string2 = `something ${foo() ? 'x' : 'y' }`
Copy the code

Strings can be multiple lines:

const string3 = `Hey this string is awesome! `
Copy the code

Compare this to multi-line strings written prior to ES2015:

var str = 'One\n' +
'Two\n' +
'Three'
Copy the code

Parameter Default Value

The function now supports default parameter values:

const foo = function(index = 0, testing = true) { / *... * / }
foo()
Copy the code

Extended operator

You can extend the operators… Extend arrays, objects, or strings.

Let’s use arrays as an example:

const a = [1.2.3]
Copy the code

You can create a new array like this:

const b = [...a, 4.5.6]
Copy the code

You can copy an array:

const c = [...a]
Copy the code

This works for objects as well, copying an object like this:

constnewObj = { ... oldObj }Copy the code

For strings, the extension operator generates an array for each character:

const hey = 'hey'
const arrayized = [...hey] // ['h', 'e', 'y']
Copy the code

This operator is very useful. The most important thing is that you can pass an array of arguments to a function in a very simple way:

const f = (foo, bar) = > {}
const a = [1.2] f(... a)Copy the code

Previously you could have used f.ply (null, a) to achieve the same effect, but it wasn’t very readable.

Deconstruction assignment

Given an object, you can extract values and assign them to other variables:

const person = {
  firstName: 'Tom'.lastName: 'Cruise'.actor: true.age: 54.//made up
}
const {firstName: name, age} = person
Copy the code

Name and age contain these values.

This syntax can also be used with arrays:

const a = [1.2.3.4.5]
[first, second, , , fifth] = a
Copy the code

Enhanced function literals

Function literals are more powerful in ES2015.

Simple syntax for declaring variables

Before:

const something = 'y'
const x = {
  something: something
}
Copy the code

Now you can:

const something = 'y'
const x = {
  something
}
Copy the code

The prototype

You can specify a prototype for a variable like this:

const anObject = { y: 'y' }
const x = {
  __proto__: anObject
}
Copy the code

super()

const anObject = { y: 'y'.test: (a)= > 'zoo' }
const x = {
  __proto__: anObject,
  test() {
    return super.test() + 'x'
  }
}
x.test() //zoox
Copy the code

Dynamic properties

const x = {
  ['a' + '_' + 'b'] :'z'
}
x.a_b //z
Copy the code

For – loops

ES5 2009 introduced the forEach() loop. While good, you can’t break out of a loop like for.

ES2015 introduced for-of loops, combining the simplicity of forEach with the ability to jump out of loops.

//iterate over the value
for (const v of ['a'.'b'.'c']) {
  console.log(v);
}
//get the index as well, using `entries()`
for (const [i, v] of ['a'.'b'.'c'].entries()) {
  console.log(i, v);
}
Copy the code

The Map and Set

Map and Set (and their respective weak-reference types, WeakMap and WeakSet) are two very popular data structures that are officially implemented (described later).

ES2016 improvement

ES7, officially known as ECMAScript 2016, was released in June 2016.

Compared to ES6, ES7 is a minor update to JavaScript and includes two features:

  • Array.prototype.includes
  • Exponential operator

Array.prototype.includes()

This feature introduces a more readable syntax for checking whether an array contains an element.

In ES6 and earlier, to check if an element is in an array you have to use indexOf to check the indexOf the array, and if the element returns -1 it is not in the array.

Since -1 is considered a true value, you can’t tell the following example:

if(! [1.2].indexOf(3)) {
  console.log('Not found')}Copy the code

Using ES7’s new syntax we get the expected results:

if(! [1.2].includes(3)) {
  console.log('Not found')}Copy the code

Exponential operator

The exponential operator ** is the same as math.pow (), but introduces a language feature rather than a library function.

Math.pow(4.2) = = =4台湾国2
Copy the code

This feature is a nice complement to maths-intensive JavaScript applications. The ** operator has been standardized in many languages, such as Python, Ruby, MATLAB, Lua, Perl, and so on.

ES2017 improvement

ECMAScript 2017, the eighth edition of the ECMA-262 standard (called ES2017 or ES8), is released in June 2017.

Compared to ES6, ES8 still brings a number of useful features:

  • String padding
  • Object.values
  • Object.entries
  • Object.getOwnPropertyDescriptors()
  • Function arguments end with commas
  • An asynchronous function
  • Shared memory and atomics

String padding

The purpose of String padding is to add characters to a String so that it matches the declared length.

ES2017 introduces two methods on strings: padStart() and padEnd().

padStart(targetLength [, padString])
padEnd(targetLength [, padString])
Copy the code

Example:

padStart() The output
‘test’.padStart(4) ‘test’
‘test’.padStart(5) ‘ test’
‘test’.padStart(8) ‘    test’
‘test’.padStart(8, ‘abcd’) ‘abcdtest’
padEnd() The output
‘test’.padEnd(4) ‘test’
‘test’.padEnd(5) ‘test ‘
‘test’.padEnd(8) ‘test    ‘
‘test’.padEnd(8, ‘abcd’) ‘testabcd’

Object.values()

This method returns an array containing the values of the object’s own properties. Usage:

const person = { name: 'Fred'.age: 87 }
Object.values(person) // ['Fred', 87]
Copy the code

Object.values() can also be used with arrays:

const people = ['Fred'.'Tony']
Object.values(people) // ['Fred', 'Tony']
Copy the code

Object.entries()

This method returns an array of the form [key, value] containing all the attributes and values of the object itself. Usage:

const person = { name: 'Fred'.age: 87 }
Object.entries(person) // [['name', 'Fred'], ['age', 87]]
Copy the code

Object.entries() can also be used for arrays:

const people = ['Fred'.'Tony']
Object.entries(people) // [['0', 'Fred'], ['1', 'Tony']]
Copy the code

getOwnPropertyDescriptors()

This method returns all (non-inherited) descriptors of the object itself.

All objects in JavaScript have a collection of properties, and each property has a descriptor.

Descriptors are attribute collections of attributes, which include the following subsets:

  • Value: Indicates the value of the attribute
  • Writable: Property can be overridden if it is true
  • Get: The getter function for the property, called when the property is read
  • Set: the setter function for the property, called when the property value is set
  • If the configured information is false, the attribute cannot be deleted, and all attribute values, excluding the attribute’s own value, cannot be changed
  • Enumerable: An attribute can be enumerable if it is true

Object. GetOwnPropertyDescriptors (obj) accept the Object as a parameter, return a contains descriptor Object.

What’s the use of that?

ES2015 brings object.assign (), which makes it easy to copy one or more objects’ own enumerable properties and return a new Object.

There is a problem with doing this, however, because it does not correctly copy attributes that do not have default Attributes. For example, if an Object has only one setter, it cannot be correctly copied with object.assign ().

const person1 = {
    set name(newName) {
        console.log(newName)
    }
}
Copy the code

This won’t work:

const person2 = {}
Object.assign(person2, person1)
Copy the code

But it does:

const person3 = {}
Object.defineProperties(person3,
Object.getOwnPropertyDescriptors(person1))
Copy the code

You can simply test this on the console:

person1.name = 'x'
"x"
person2.name = 'x'

person3.name = 'x'
"x"
Copy the code

Person2 has no setter, and it cannot be copied correctly.

Using object.create () has the same limitation for shallow Object cloning.

After the comma

This feature allows the use of trailing commas in function declarations and function calls:

const doSomething = (var1, var2,) = > {
  / /...
}
doSomething('test2'.'test2'.)Copy the code

This change will encourage developers to stop using the ugly “first line comma” habit.

An asynchronous function

ES2017 introduces the concept of asynchronous functions, which is the most important change in this ECMAScript version.

Asynchronous functions combine Promises and generators to reduce the boilerbox style of Promises and the “don’t break the call chain” limitation of Promises chains.

Why are they useful

It is a high-level abstraction of promises functions.

Promises when Promises was introduced in ES2015, this feature was designed to solve the problem of asynchronous code. But in the two years between THE release of ES2015 and ES2017, it has become clear that this is not the ultimate solution.

Promises were introduced to solve the famous problem of callback hell, but they also brought their own complexity and increased the complexity of grammar. They are a good place to start, on which developers can use a better syntax: asynchronous functions.

A quick example

Code for asynchronous functions could look like this:

function doSomethingAsync() {
    return new Promise((resolve) = > {
        setTimeout((a)= > resolve('I did something'), 3000)})}async function doSomething() {
    console.log(await doSomethingAsync())
}
console.log('Before')
doSomething()
console.log('After')
Copy the code

The code above will be printed in the browser console:

Before
After
I did something //after 3s
Copy the code

Concatenation of multiple asynchronous functions

Chain calls to asynchronous functions are easy and much easier to read than the original Promises:

function promiseToDoSomething() {
    return new Promise((resolve) = >{
        setTimeout((a)= > resolve('I did something'), 10000)})}async function watchOverSomeoneDoingSomething() {
    const something = await promiseToDoSomething()
    return something + ' and I watched'
}
async function watchOverSomeoneWatchingSomeoneDoingSomething() {
    const something = await watchOverSomeoneDoingSomething()
    return something + ' and I watched as well'
}
watchOverSomeoneWatchingSomeoneDoingSomething().then((res) = > {
    console.log(res)
})
Copy the code

Shared memory and atoms

WebWorkers are used to build multithreaded applications in the browser.

They provide a communication protocol through events. Starting with ES2017, you can create a shared memory array between Web workers and their builders using SharedArrayBuffer.

Since we don’t know how many events it takes to write the pass part of a shared memory, Atomics is a way of performing operations while reading values, and doing all kinds of writes.

More can be found in this proposal, which has been implemented.

ES2018 improvement

ES2018 is the latest ECMAScript standard.

What new things does it introduce?

Rest/Spread properties

ES6 introduces the remaining elements for array deconstruction:

const numbers = [1.2.3.4.5]
[first, second, ...others] = numbers
Copy the code

And extension elements:

const numbers = [1.2.3.4.5]
const sum = (a, b, c, d, e) = > a + b + c + d + e
constsum = sum(... numbers)/ / corrections: here can't sum assignment again, thanks to [sen lanqing,] (https://juejin.cn/user/3773179636225895)
Copy the code

ES2018 brings the same functionality to objects.

Residual attributes:

const{ first, second, ... others } = {first: 1.second: 2.third: 3.fourth: 4.fifth: 5 }
first / / 1
second / / 2
others // { third: 3, fourth: 4, fifth: 5 }
Copy the code

Extended properties allow new objects to be created by combining the properties of objects passed after the spread operator:

constitems = { first, second, ... others } items//{ first: 1, second: 2, third: 3, fourth: 4, fifth: 5 }
Copy the code

Asynchronous iterative

The new for-await-of constructor allows you to iterate with asynchronous iterables as loops:

for await (const line of readLines(filePath)) {
  console.log(line)
}
Copy the code

Since we are using await here, you can only use it inside async functions, like a normal await (see async/await).

Promise.prototype.finally()

This is a big pity. When a promise is fulfilled, it will invoke the then() method one by one. If anything goes wrong in this process, the then() method is skipped and the catch() method is executed.

Finally () runs the code whether the promise succeeds or fails:

fetch('file.json')
  .then(data= > data.json())
  .catch(error= > console.error(error))
  .finally((a)= > console.log('finished'))
Copy the code

Regular expression improvement

The following assertion of RegExp: Matches the string based on the previous content.

This is an antecedent assertion: use? = matches a specific substring:

/Roger(? =Waters)//Roger(? = Waters)/.test('Roger is my dog') //false/Roger(? = Waters)/.test('Roger is my dog and Roger Waters is a famous musician') //true
Copy the code

? ! To do the opposite, match the string without a specific substring following it:

/Roger(? ! Waters)//Roger(? ! Waters)/.test('Roger is my dog') //true/Roger(? ! Waters)/.test('Roger Waters is a famous musician') //false
Copy the code

Predicate use? = identifier, which is already available.

After assertion, new functionality, use? < =.

/ (?<=Roger) Waters/ / (? < =Roger) Waters/.test('Pink Waters is my dog/ / ')false/ (? < =Roger) Waters/.test('Roger is my dog and Roger Waters is a famous musician/ / ')true
Copy the code

Use?

/ (?<! Roger) Waters/ / (? <!Roger) Waters/.test('Pink Waters is my dog/ / ')true/ (? <!Roger) Waters/.test('Roger is my dog and Roger Waters is a famous musician/ / ')false
Copy the code

Unicode attribute escape\ p {... }\ P {... }

In a regular expression, you can use \d to match any number, \s to match any character except Spaces, \w to match any alphanumeric character, and so on.

This new feature extends this concept to all Unicode characters by introducing \p{} and \p{}.

Any Unicode character has a specific set of attributes. For example, Script determines the language family, ASCII is the Boolean value of the ASCII string true, and so on. You can put this feature in braces and the regular expression will check if it is true:

/^\p{ASCII}+$/u.test('abc')   / / ✅
/^\p{ASCII}+$/u.test('ABC@')  / / ✅
/^\p{ASCII}+$/u.test('ABC 🙃') / / ❌
Copy the code

ASCII_Hex_Digit is another Boolean property that checks if the string contains a valid hexadecimal number:

/^\p{ASCII_Hex_Digit}+$/u.test('0123456789ABCDEF') / / ✅
/^\p{ASCII_Hex_Digit}+$/u.test('h')                / / ❌
Copy the code

There are many other Boolean attributes that you can use just by enclosing their names in curly brackets: Uppercase, Lowercase, White_Space, Alphabatic, Emoji, and so on:

/^\p{Lowercase}$/u.test('h') / / ✅
/^\p{Uppercase}$/u.test('H') / / ✅
/^\p{Emoji}+$/u.test('H')   / / ❌
/^\p{Emoji}+$/u.test('🙃 🙃') / / ✅
Copy the code

In addition to these binary properties, you can also check whether any Unicode character matches a particular value. In this example, I check whether the string is in Greek or Latin letters:

/^\p{Script=Greek}+$/u.test('epsilon lambda lambda eta argument ι kappa ά predominate') / / ✅
/^\p{Script=Latin}+$/u.test('hey') / / ✅
Copy the code

To learn more, read the proposal.

Named capture group

Groups matched in ES2018 can be bound to a name, rather than just a slot in the result array:

const re = / (? 
      
       \d{4})-(? 
       
        \d{2})-(? 
        
         \d{2})/
        
       
      
const result = re.exec('2015-01-02')
// result.groups.year === '2015';
// result.groups.month === '01';
// result.groups.day === '02';
Copy the code

Regular expression ‘s’

S is short for single line and can match a new line with. Without it, the dot identifier will not match the new line:

/hi.welcome/.test('hi\nwelcome') // false
/hi.welcome/s.test('hi\nwelcome') // true
Copy the code

Programming style

JavaScript programming styles are a set of specifications for writing JavaScript.

Programming style is an agreement between you and your team to ensure consistency on the project. If you don’t have a team, it’s your own protocol and should always ensure that your code meets your standards.

Having fixed rules about how your code should be written is a great benefit because it makes your code more readable and manageable.

A popular style guide

There are many style guides in JavaScript, but two of the most common are:

  • The Google JavaScript Style Guide
  • The AirBnb JavaScript Style Guide

You can choose to use one or develop your own code style.

Keep your style consistent with your project

Even if you have a preferred code style, you should adhere to the team project style in teamwork.

Every open source project on Github may have a set of rules, and another project you work on May have a completely different set of rules.

Prettier is a powerful tool for formatting code. You should try it.

The rules used in this guide

We’ve been using the latest ES version and using Babel in older browsers.

  • Indent: Replace ‘tabs’ with a space by two Spaces.

  • Semicolons: Do not use semicolons.

  • Line length: If possible, try not to exceed 80 characters per line.

  • In-line comments: Use in-line comments in code, block level comments only in files.

  • Don’t have useless code: Don’t keep old code, “just in case” it’s useful in the future. Make sure you only have the code you need right now, version control or your note-taking app for that purpose.

  • Comment only when needed: Don’t add comments that don’t help you understand. If the code itself has good variable names, function names, and JSDoc function comments, don’t add comments.

  • Variable declarations: Avoid contaminating global variables and never use var. The default is const, and let is used only when variables need to be rebound.

  • Constants: Declare all constants with uppercase letters and split VARIABLE_NAME with _.

  • Functions: Use arrow functions unless you have a reason to use regular functions, such as object methods or constructors, where you need to specify what this points to. Declare functions as const, and use implicit returns where possible. Using nested functions to hide the excess code of helper functions can feel liberating.

    const test = (a, b) = > a + b
    const another = a= > a + 2
    Copy the code
  • Names: Function lives, variable names, and method names start with a lower case (unless they are private and read-only) camel name, only constructors and class names should start with an upper case. If you use a framework with specific specifications, change your habits as required. File names should be all lowercase, separated by -.

  • Specific statement formats and rules:

    if

    if (condition) {
      statements
    }
    if (condition) {
      statements
    } else {
      statements
    }
    if (condition) {
      statements
    } else if (condition) {
      statements
    } else {
      statements
    }
    Copy the code

    For: Always cache the length of the traversal object at initialization, and do not insert it into conditional statements. Avoid for in expressions other than with.hasownProperty (), preferring for of:

    for (initialization; condition; update) {
      statements
    }
    Copy the code

    while

    while (condition) {
      statements
    }
    Copy the code

    do

    do {
      statements
    } while (condition);
    Copy the code

    switch

    switch (expression) {
      case expression:
        statements
      default:
        statements
    }
    Copy the code

    try

    try {
      statements
    } catch (variable) {
      statements
    }
    try {
      statements
    } catch (variable) {
      statements
    } finally {
      statements
    }
    Copy the code
  • Whitespace: Cleverly improves code readability with whitespace: inserts a space in keywords and (; In binary operators (+, -, /,*, &&…) Insert a space before and after; Within a statement, each; Insert a space to separate each statement; Insert a space after each.

  • Newline: Blocks of code that are separated by new lines to perform logic-related operations.

  • Quotation marks: Use single quotation marks’ instead of double quotation marks. Double quotes are a standard attribute of HTML, so using single quotes avoids the problems you might encounter when working with HTML strings. Use template strings instead of variable interpolation where appropriate.

Lexical structures

Now we’ll dive into JavaScript’s building blocks: Unicode, semicolons, Spaces, case sensitivity, comments, literals, identifiers, and reserved words.

Unicode

JavaScript is written in Unicode. This means you can use Emojis as variable names. 😃 😧 😲 More importantly, you can write the rules for identifiers in any language, such as Japanese or Chinese.

A semicolon

JavaScript syntax is similar to C, and you’ll probably see a semicolon at the end of each line of code in many examples.

Semicolons are not mandatory, and there is no problem with JavaScript not using them. Many developers are now avoiding semicolons, especially in languages that don’t require them.

You just need to avoid weird practices like splitting statements into multiple lines:

return
variable
Copy the code

Or start a line with ([or (). You’ll be safe 99.9% of the time (and your linter will warn you).

It’s a matter of personal preference, but RECENTLY I decided not to use any semicolons anymore, so you won’t see any semicolons in this article.

The blank space

JavaScript does not consider whitespace to be meaningful. Spaces and line breaks can be added as much as you like, even if this is theoretically possible.

In practice, you might force a style tool like Linter or Prettier by following the rules of good style and common sense.

For example, I like to indent two characters.

Case sensitivity

JavaScript is case sensitive. The variable something and Somethin are different. This is consistent in any identifier.

annotation

In JavaScript, there are two ways to comment:

/ * * /
//
Copy the code

The first one can be multi-line commented and needs to be closed.

The second comments out everything to the right of the current line.

Literals and identifiers

We define values in the source code as literals, such as numbers, strings, bools, or higher-level constructs like object literals or array literals:

5
'Test'
true
['a'.'b']
{color: 'red'.shape: 'Rectangle'}
Copy the code

An identifier can be used to identify a variable, a function, an object. It can start with a dollar sign $or an underscore _. It can also contain numbers. Using Unicode, letters can be any allowed character, such as emoji :smile:.

Test
test
TEST
_test
Test1
$test
Copy the code

Dollar signs are often used to distinguish DOM elements.

Reserved words

You cannot use the following identifiers because they are language reserved words.

break
do
instanceof
typeof
case
else
new
var
catch
finally
return
void
continue
for
switch
while
debugger
function
this
with
default
if
throw
delete
in
try
class
enum
extends
super
const
export
import
implements
let
private
public
interface
package
protected
static
yield
Copy the code

variable

A variable is an identifier bound to a literal, so you can reference and use it in subsequent code. We’ll learn how to declare a variable in JavaScript.

Introduction to JavaScript variables

A variable is an identifier bound to a literal, so you can reference and use it in subsequent code. In JavaScript, variables are not bound to any type. Even if you bind a variable to a specific type, you can rebind any other type later without causing type errors or other problems.

This is why references to JavaScript are sometimes referred to as “untyped”.

Variables must be declared before you use them. There are three declarations: var, let, or const. The difference between these three approaches is how you interact with this variable later on.

The use of var

Until ES2015, var was the only way to define variables.

var a = 0
Copy the code

If you forget to add var, you will bind the value to an undeclared variable, and the result may be different: in modern environments, turning on strict mode will result in an error. In the old world (or with strict mode off), this would initialize a variable and bind it to a global object.

If you declare a variable without initializing it, it will get a undefined value until you bind a value to it.

var a //typeof a === 'undefined'
Copy the code

You can declare the same variable repeatedly, overwriting its value:

var a = 1
var a = 2
Copy the code

You can declare multiple variables at once:

var a = 1, b = 2
Copy the code

The scope of the code is the scope within which variables are visible.

Variables initialized by var outside any function are bound to global objects, have global scope, and are available anywhere. Variables initialized by var inside a function are bound to the function and are only available inside the function, just like function parameters.

It is important to understand that a block (distinguished by a pair of curly braces) does not define a new scope. New scopes are created only with function creation, because var has no block-level scope, only function scope.

Inside a function, any variables defined in it are visible in the function code, and even variables defined at the end are introduced in the function header, because JavaScript automatically moves all variables to the top (variable promotion). To avoid confusion, always declare variables in the function header.

Use the let

Let is a new feature introduced in ES2015 and is essentially a VAR with block-level scope. Its scope is limited to the block, statement, or expression that defines it, and any contained internal blocks.

Modern JavaScript developers would probably use let alone var entirely.

If let seems to be a vague term, it is much easier to understand by looking at let color = ‘red’ to make the color red.

Using let outside a function, as opposed to var, does not create a global variable.

Use the const

Variables declared with var or let can be changed and rebound later. Once a const is initialized, its value cannot be modified or bound to other values.

const a = 'test'
Copy the code

We can’t bind a to a different value. However, if A is an object that provides methods to change its content, we can still change it.

Const does not provide immutability and only ensures that references are not changed.

Const, like let, provides block-level scope.

Now JavaScript developers may choose const as an identifier to declare variables that do not need to be rebound in the future.

Why is that? Because we should always use the simplest constructor to avoid errors.

type

Sometimes you will read that JS is untyped, but this is not true. It’s true that you can bind different types to a variable, but JavaScript is typed. It provides both primitive and object types.

Basic types of

The original types are:

  • Numbers
  • Strings
  • Booleans

There are two special types:

  • null
  • undefined

Let’s take a closer look at them in the next section.

Numbers

Inside the language, JavaScript has only one number type: all numbers are floating point.

A numeric literal is a number represented in the source code, and depending on how it is written, it can be either an integer literal or a floating-point literal.

Integer:

10
5354576767321
0xCC //hex
Copy the code

Floating point:

3.14
1234.
5.2 e4 / / 5.2 * 10 ^ 4
Copy the code

strings

A string type is a series of characters. In the source code, it is defined as a string literal, enclosed in single or double quotation marks.

'A string'
"Another string"
Copy the code

Strings can span multiple lines with backslashes:

"A \
string"
Copy the code

Strings can contain escape sequences that are interpreted when the string is printed, such as \n for line breaks. Backslashes are also useful when you need to prevent characters from being misunderstood as closing quotes:

'I\'m a developer'
Copy the code

Strings can be concatenated with the + operator:

"A " + "string"
Copy the code

Template string

Introduced in ES2015, template strings allow a more powerful way to define a string literal.

`a string`
Copy the code

You can embed any JavaScript expression and replace the result after execution:

`a string with ${something}`
`a string with ${something+somethingElse}`
`a string with ${obj.something()}`
Copy the code

You can easily get multi-line strings:

`a string
with
${something}`
Copy the code

Booleans

For Booleans, JavaScript has two reserved words: true and false. Most comparison operators == === > < and so on return one of these.

If, while statements, and other control structures use Booleans to determine the flow of a program.

They accept not only true and Fasle, but also Truthy and Falsy.

Falsy, the value is interpreted as false:

0
0
NaN
undefined
null
' ' //empty string
Copy the code

The rest belong to Truthy.

null

Null is a special value indicating that there is no value. This is also a common idea in other languages, such as nil or None in Python.

undefined

Undefined indicates that the variable has not been initialized and its value is null.

Undefined is returned when there is no return value in the function. Function arguments are undefined when not assigned by the caller.

To check whether a value is undefined, you can use this method:

typeof variable === 'undefined'
Copy the code

Object type

Anything that is not a basic type is an object type.

Functions, arrays, and what we call objects are all object types. They are special in themselves, but they inherit many features of objects, such as having properties and methods for making them work.

expression

Expressions are units of code that can be executed and parsed into values. Expressions in JS can be divided into several categories.

Arithmetic expression

All expressions under this category evaluate numbers:

1 / 2
i++
i -= 2
i * 2
Copy the code

String expression

Evaluate a string:

'A ' + 'string'
'A '+ ='string'
Copy the code

Original expression

Under this category, there are variable references, literals, and constants:

2
0.02
'something'
true
false
this //the current object
undefined
i //where i is a variable or a constant
Copy the code

There are also some language keywords:

function
class
function* / /the generator function
yield //the generator pauser/resumer
yield* / /delegate to another generator or iterator
async function* / /async function expression
await //async function pause/resume/wait for completion
/pattern/i //regex
() / /grouping
Copy the code

Array and object initial expressions

[] //array literal
{} //object literal
[1.2.3]
{a: 1.b: 2}
{a: {b: 1}}
Copy the code

Logical expression

Logical expressions use logical operators to get a Boolean value:

a && b a || b ! aCopy the code

The Left – hand side expression

new //create an instance of a constructor
super //calls the parent constructor. obj//expression using the spread operator
Copy the code

Attribute access expression

object.property //reference a property (or method) of an object
object[property]
object['property']
Copy the code

Object construction expression

new object()
new a(1)
new MyRectangle('name'.2, {a: 4})
Copy the code

Function definition expression

function() {}
function(a, b) { return a * b }
(a, b) => a * b
a => a * 2() = > {return 2 }
Copy the code

Call expression

a.x(2)
window.resize()
Copy the code

Prototype inheritance

JavaScript is a very unique presence in popular programming languages because of its use of archetypal inheritance. Let’s see what that means.

While most object-oriented languages are based on class inheritance, JavaScript is based on prototype inheritance.

What does that mean?

Each JavaScript object has a feature called Prototype that points to a different object.

This different object is the prototype object.

Our object inherits the properties and methods of the prototype object.

Suppose you created an object using object literal syntax:

const car = {}
Copy the code

Or create it with new Object:

const car = new Object(a)Copy the code

Either way, the prototype for car is Object.

Initialize an array, also an object:

const list = []
//or
const list = new Array(a)Copy the code

The prototype for this is Array.

You can verify this with the __proto__ attribute:

car.__proto__ == Object.prototype //true
car.__proto__ == new Object().__proto__ //true
list.__proto__ == Object.prototype //false
list.__proto__ == Array.prototype //true
list.__proto__ == new Array().__proto__ //true
Copy the code

The __proto__ attribute here is not standard but is widely implemented in browsers. A more reliable way to get a prototype is object.getProtoTypeof (new Object()).

All features and methods on the stereotype are available on objects that have the stereotype:

Object. Prototype is the prototype of all objects.

Array.prototype.__proto__ == Object.prototype
Copy the code

If you want to know what the prototype of Object.prototype is, it has no prototype. This is a special snowflake. ❄ ️

The example above is an example of a prototype chain.

I can create an Object that extends Array and any instantiated Object, and this Object will have Array and Object in its prototype chain, and it will inherit all of its ancestors’ features and methods.

In addition to creating an Object with new, or using literal syntax for objects and arrays, you can instantiate an Object with Object.create().

The first argument is the prototype of the object:

const car = Object.create({})
const list = Object.create(Array)
Copy the code

You can check the prototype of this object with the isPrototypeOf() method:

Array.isPrototypeOf(list) //true
Copy the code

Note, because you can instantiate arrays like this:

const list = Object.create(Array.prototype)
Copy the code

Therefore, Array. IsPrototypeOf (list) is false, and Array. Prototype. IsPrototypeOf (list) is true.

class

The ECMAScript 6 (ES6) standard, released in 2015, introduced classes.

Until then, JavaScript could only implement inheritance in a very strange way. And that’s archetypal inheritance, which I think is amazing, unlike any other popular language.

People from Java or Python or other languages have a hard time understanding the complexity of prototype inheritance, so the ECMAScript committee decided to introduce syntactic sugar based on this, similar to how it is implemented in other languages.

This is important: the JavaScript underlayer is still the same as before, and you can access prototype objects in a universal way.

Define the class

The class looks like this:

class Person {
  constructor(name) {
    this.name = name
  }
  hello() {
    return 'Hello, I am ' + this.name + '. '}}Copy the code

The class has an identifier that allows us to create a new object through new ClassIdentifier().

When the object is instantiated, the constructor method is called, and any arguments can be passed.

A class can have many methods, in this case Hello is a method that can be called by all objects derived from this class:

const flavio = new Person('Flavio')
flavio.hello()
Copy the code

Class inheritance

A class can extend another class, and objects initialized with that class inherit all the methods of both classes.

If the inherited class has a method of the same name as the higher class, the last-first method is called:

class Programmer extends Person {
  hello() {
    return super.hello() + ' I am a programmer.'}}const flavio = new Programmer('Flavio')
flavio.hello()
Copy the code

The above program prints: “Hello, I am Flavio. I am a Programmer.”

Classes do not explicitly declare variables, but they must be initialized in constructors.

In a class, you can introduce a parent class by calling super().

A static method

In general, methods are defined on instances, not classes.

Static methods can execute on a class:

class Person {
  static genericHello() {
    return 'Hello'
  }
}
Person.genericHello() //Hello
Copy the code

Private methods

JavaScript has no built-in means of defining private methods or protecting them. There are solutions, but I won’t go over them here.

Getters and Setters

You can add a get or set prefix to create a getter and setter, depending on what you want to do: access a variable or modify its value.

class Person {
  constructor(name) {
    this.name = name
  }
  set name(value) {
    this.name = value
  }
  get name() {
    return this.name
  }
}
Copy the code

If a property has only a getter, it cannot be set and all changes are ignored:

class Person {
  constructor(name) {
    this.name = name
  }
  get name() {
    return this.name
  }
}
Copy the code

If a property has only one setter, you can change its value as much as you like, but you can’t access it externally:

class Person {
  constructor(name) {
    this.name = name
  }
  set name(value) {
    this.name = value
  }
}
Copy the code

abnormal

When unexpected errors occur in your code, JavaScript is used to handling them with exceptions.

Create your

Create an exception with the keyword throw:

throw value
Copy the code

A value can be any JavaScript value, including a string, a number, or an object.

When the JavaScript code executes to this line, normal program flow is suspended and control is redirected to the nearest exception handler.

Handle exceptions

Exception handlers are try/catch statements.

Exceptions thrown within the try block are handled in the corresponding catch:

try {
  //lines of code
} catch (e) {
  
}
Copy the code

In this case, e stands for outlier.

You can add multiple handlers that can catch different errors.

finally

To complete the entire code statement, JavaScript has another statement, finally, in which the code executes regardless of the program flow, whether an exception is handled, and whether an exception is raised:

try {
  //lines of code
} catch (e) {
  
} finally{}Copy the code

You can also use finally without a catch to clear any resources, such as files or network requests, that might be opened ina try:

try {
  //lines of code
} finally{}Copy the code

Nested try code blocks

Tries can be nested, and exceptions are always handled in the nearest catch block:

try {
  //lines of code
  try {
    //other lines of code
  } finally {
    //other lines of code}}catch (e) {
  
}
Copy the code

If an exception is found in an internal try, it is handled in an external catch.

A semicolon

JavaScript semicolons are optional, and I personally prefer not to write semicolons, but many people prefer to wear them.

The semicolon has created a great divide in the JavaScript community. Some people like to use it no matter what, while others don’t.

After using semicolons for several years, in the fall of 2017 I decided to try to avoid them by automatically removing semicolons from code by Prettier unless a specific code construct requires them.

I naturally avoid semicolons now, and I think the code looks better and easier to read.

Since JavaScript doesn’t mandate semicolons, anything is possible. When a location requires a semicolon, it is automatically added.

The process of doing this becomes automatic insertion of semicolons.

It is important to understand the rules for using semicolons to avoid writing bad code that doesn’t behave as expected.

JavaScript inserts semicolon rules automatically

While interpreting the source code, the JavaScript interpreter automatically adds a semicolon when it finds:

  • The start of the next line interrupts the current line (the code may be multiple lines)
  • The next line of code starts with}Begins, closing the current code block
  • When executed at the end of the source code
  • The current line hasreturnstatements
  • The current line hasbreakstatements
  • The current line hasthrowstatements
  • The current line hascontinuestatements

Examples of inconsistent code behavior

Here are some other examples based on the above rules, such as:

const hey = 'hey'
const you = 'hey'
const heyYou = hey + ' ' + you
['h'.'e'.'y'].forEach((letter) = > console.log(letter))
Copy the code

Uncaught TypeError: Cannot read property ‘forEach’ of undefined because rule 1 tries to interpret the code as:

const hey = 'hey';
const you = 'hey';
const heyYou = hey + ' ' + you['h'.'e'.'y'].forEach((letter) = > console.log(letter))
Copy the code

There are:

(1 + 2).toString() / / 3
Copy the code
const a = 1
const b = 2
const c = a + b
(a + b).toString()
Copy the code

Instead, the above code raises TypeError: b is not a function because JavaScript tries to interpret it as:

const a = 1
const b = 2
const c = a + b(a + b).toString()
Copy the code

Another example of rule 4:

((a)= > {
  return
  {
    color: 'white'
  }
})()
Copy the code

You want the immediate function to return an object containing the color property, but it doesn’t. It returns undefined because JavaScript inserts a semicolon after return.

The correct way to do this is to put curly braces after the return:

((a)= > {
  return {
    color: 'white'
  }
})()
Copy the code

You think this code will pop 0:

1 + 1
- 1 + 1= = =0 ? alert(0) : alert(2)
Copy the code

But it says 2, because rule 1 interprets it as:

1 + 1 - 1 + 1= = =0 ? alert(0) : alert(2)
Copy the code

Watch your back Some people are paranoid about semicolons, but I’m fine with that. The tool gives us the option not to use semicolons, so we should avoid them. I’m not recommending anything. It’s your choice.

There are only a few special cases to be aware of, even if they are rarely present in your code.

Remember these rules:

  • Be careful withreturnStatements. If you want to return something, you should put it on the same line as return (break.throw.continueIn the same way)
  • Never start with parentheses; this may be concatenated to the previous line as a function call or array element reference

Finally, always test your code to make sure it is what you want.

quotes

Now we’ll talk about quotes and special features in JavaScript.

JavaScript allows three forms of quotation marks:

  • Single quotes
  • Double quotation marks
  • The quotation marks

The first two are similar:

const test = 'test'
const bike = "bike"
Copy the code

It doesn’t make much difference to use one of them. The only difference is that the quote characters used to delimit strings must be moved:

const test = 'test'
const test = 'te\'st'
const test = 'te"st'
const test = "te\"st"
const test = "te'st"
Copy the code

There are many different style guides and it is recommended to always use one of them.

I personally always use single quotes, and only use double quotes in HTML.

Backquotes are another option, introduced in ES6 in 2015.

They all have a special feature – allowing multi-line strings.

A multiline string may be a regular string with an escape character:

const multilineString = 'A string\non multiple lines'
Copy the code

Using backquotes (the number 1 key in the upper-left corner of the keyboard), you can do without escaping characters:

const multilineString = `A string
on multiple lines`
Copy the code

Not only that. You can parse variables with ${} :

const multilineString = `A string
on The ${1+1} lines`
Copy the code

This is also called a template string.

Template string

In ES2015, introduced in ES6, template strings provide a new way to display strings, and some interesting new constructs have been widely used.

Template strings as a feature of ES2015/ES6 allow you to handle strings in a more novel way than in ES5 and below.

At first glance, the backquote syntax is simple compared to single and double quotes:

const a_string = `something`
Copy the code

They provide more functionality than regular strings, in particular:

  • Provides excellent syntax for defining multi-line strings
  • Provides a simple way to interpret variables and expressions in strings
  • Allows DSLs to be created using template tags

Let’s take a closer look at these features.

Multiline string

Prior to ES6, creating a multi-line string required adding \ characters at the end of each line:

const string = 'first part \
second part'
Copy the code

This is a two-line string, but will render to a single line:

"first part second part"
Copy the code

To render multi-line strings, you need to add \n at the end of each line:

const string = 'first line\n \
second line'
Copy the code

or

const string = 'first line\n' +
               'second line'
Copy the code

Template strings make it easy to create multi-line strings:

Using backquotes to start a multi-line string, you just press enter to create a new line, without any special characters, like this:

const string = `Hey
this

string
is awesome!`
Copy the code

Note: Spaces are also meaningful, do this:

const string = `First Second`
Copy the code

A string like this will be created:

First
                Second
Copy the code

There’s a simple way to fix this: make the first line empty and add the trim() method after the closing backquote, which will ignore all Spaces before the first character:

const string = `
First
Second`.trim()
Copy the code

The interpolation

Template strings provide a simple way to insert variables and expressions into strings.

You can use ${… } syntax:

const var = 'test'
const string = `something The ${var}` //something test
Copy the code

In ${} you can add anything, even expressions:

const string = `something The ${1 + 2 + 3}`
const string2 = `something ${foo() ? 'x' : 'y' }`
Copy the code

Template tags

The template tag is a feature that may not sound very useful at first, but it is actually used by many popular libraries, such as Styled Components, Apollo, and the GraphQL client/server libraries, so it is also important to understand how it works.

In Styled Components, the template tag is used to define the CSS:

const Button = styled.button` the font - size: 1.5 em. background-color: black; color: white; `;
Copy the code

In Apollo, template tags are used to define the GraphQL query table:

const query = gql` query { ... } `
Copy the code

The highlighted Styled Button and GQL template tags in these examples are functions:

function gql(literals, ... expressions) {}Copy the code

This function returns a string that can be the result of any calculation.

Literals is an array of template tags that contain markup interpolated by expressions.

Expressions contains all interpolation.

Take the example above:

const string = `something The ${1 + 2 + 3}`
Copy the code

The Literals array contains two entries. The first starts with something until the first interpolation string is encountered, and the second is an empty string that is the space between the end of the first interpolation (we only have one) and the end of the entire string.

A more complicated example is:

const string = `something
another The ${'x'}
new line The ${1 + 2 + 3}
test`
Copy the code

In this example, the first item in the literals array is:

`something
another `
Copy the code

The second item is:

`
new line `
Copy the code

The third item is:

`
test`
Copy the code

Expressions is an array of x and 6.

The function that passes these values can do anything with them, which is the power of this feature.

The simplest example is the ability to assign interpolating strings by adding literals and expressions:

const interpolated = interpolate`I paid The ${10}Euro `
Copy the code

Interpolate is this:

function interpolate(literals, ... expressions) {
  let string = ` `
  for (const [i, val] of expressions) {
    string += literals[i] + val
  }
  string += literals[literals.length - 1]
  return string
}
Copy the code

JavaScript function

Now we look at all the functions in terms of faces and points to help you use them.

Everything in JavaScript is a function.

A function is a self-contained block of code that is defined once and can be used countless times.

Function arguments are optional and can only return one value.

Functions in JavaScript are also objects, a special kind of object: function objects. Their superpower is that they can be called.

In addition, functions are called first class functions because they can bind values, pass arguments, and return a value.

Let’s start with the “old” pre-ES6 syntax. This is a function declaration:

function dosomething(foo) {
  // do something
}
Copy the code

Now, in ES6/ES2015, it’s a normal function.

A function can be bound to a variable (this is called a function expression) :

const dosomething = function(foo) {
  // do something
}
Copy the code

Named function expressions are simple but useful in stack call tracing, which displays the name of the function when an error occurs:

const dosomething = function dosomething(foo) {
  // do something
}
Copy the code

ES6/ES2015 introduces arrow functions, which are useful as inline functions for arguments or callbacks:

const dosomething = foo= > {
  //do something
}
Copy the code

There is one big difference between the arrow function and the others above, and we’ll get into it later.

parameter

A function can have one or more arguments.

const dosomething = (a)= > {
  //do something
}

const dosomethingElse = foo= > {
  //do something
}

const dosomethingElseAgain = (foo, bar) = > {
  //do something
}
Copy the code

Starting with ES6/ES2015, function arguments can have default values:

const dosomething = (foo = 1, bar = 'hey') = > {
  //do something
}
Copy the code

This lets you call the calling function without filling it with arguments:

dosomething(3)
dosomething()
Copy the code

ES2018 can add trailing commas to arguments to help reduce the possibility of missing commas when moving arguments (such as moving the last argument to the middle) :

const dosomething = (foo = 1, bar = 'hey') = > {
  //do something
}
dosomething(2.'ho! ')
Copy the code

You can wrap all the arguments in an array and expand them with the extension operator when called:

const dosomething = (foo = 1, bar = 'hey') = > {
  //do something
}
const args = [2.'ho! '] dosomething(... args)Copy the code

Remember: function arguments are ordered. Using an object as a parameter, you can get the name of the parameter:

const dosomething = ({ foo = 1, bar = 'hey' }) = > {
  //do something
  console.log(foo) / / 2
  console.log(bar) // 'ho! '
}
const args = { foo: 2.bar: 'ho! ' }
dosomething(args)
Copy the code

The return value

Each function returns a value, undefined by default.

All functions terminate at the end of the last line of code, or at the return keyword. When JavaScript encounters this keyword, it automatically terminates the function execution and gives control to the caller.

If you pass a value, that value will be returned by the function.

const dosomething = (a)= > {
  return 'test'
}
const result = dosomething() // result === 'test'
Copy the code

You can only return one value:

To simulate returning multiple values, you can return an object literal or an array and use destruct to bind a single value when calling a function.

Using arrays:

Use objects:

Nested function

A function can be defined inside another function:

const dosomething = (a)= > {
  const dosomethingelse = (a)= > {}
  dosomethingelse()
  return 'test'
}
Copy the code

Nested functions are in the scope of external functions and cannot be called from other locations.

Object methods

When a function is called as a method as an object property:

const car = {
  brand: 'Ford'.model: 'Fiesta'.start: function() {
    console.log(`Started`)
  }
}
car.start()
Copy the code

“This” in the arrow function

As object methods, the pointing of the arrow function and the regular function “this” is important. Consider this example:

const car = {
  brand: 'Ford'.model: 'Fiesta'.start: function() {
    console.log(`Started The ${this.brand} The ${this.model}`)},stop: (a)= > {
    console.log(`Stopped The ${this.brand} The ${this.model}`)}}Copy the code

The stop() method will not work as you expect.

This is because this is not the same in the two function styles. The this in the arrow function refers to the closed context, in this case the window object:

Using function(), this points to the host object.

This means that arrow functions are not suitable for object methods and constructors (arrow constructors run TypeError on calls).

IIFE, execute function expression immediately

IIFE executes immediately after the declaration:

; (function dosomething() {
  console.log('executed')
})()
Copy the code

You can assign the result to a variable:

const something = (function dosomething() {
  return 'something'}) ()Copy the code

They’re handy because you don’t have to call the function separately after you define it.

Function increase

Before executing the code, JavaScript reorders it according to the rules.

For example, move the function to the top of the scope. Here’s why it’s legal to write this:


Copy the code

At the bottom, JavaScript moves this function to the front of the calling statement, in the same scope as other functions:

function dosomething() {
  console.log('did something')
}
dosomething()
Copy the code

Now, if you use named function expressions, things will be different because you used variables. The variable is promoted, but the value is not, so it is no longer a function.

dosomething()
const dosomething = function dosomething() {
  console.log('did something')}Copy the code

Can’t work:

This is because the interior becomes:

const dosomething
dosomething()
dosomething = function dosomething() {
  console.log('did something')}Copy the code

This does not work with let declarations or var declarations either, but throws different errors:

This is because the var declaration is raised and initialized to undefined, while const and let are only raised.

Arrow function

The arrow function is the most important change in ES6/ES2015 and is now widely used. They’re different from regular functions, and let’s see why.

I’ve covered arrow functions above, but they’re important, so I’ll cover them separately.

Arrow functions were introduced in ES6/ES2015, and their presence has changed the way JavaScript code is written (and worked) forever.

In my opinion, this change is so welcome that modern code rarely uses the function keyword.

Visually, it’s a welcome and simple change that lets you write a function in simpler syntax, starting with:

const myFunction = function foo() {
  / /...
}
Copy the code

To:

const myFunction = (a)= > {
  / /...
}
Copy the code

If the function body has only one statement, you can omit the curly braces and write everything on one line:

const myFunction = (a)= > doSomething()
Copy the code

Passing arguments in parentheses:

const myFunction = (param1, param2) = > doSomething(param1, param2)
Copy the code

If you have only one argument, you can ignore the parentheses:

const myFunction = param= > doSomething(param)
Copy the code

Thanks to this syntax, the arrow function encourages the use of short functions.

Implicit return

Arrow functions can return values implicitly: they do not return using the return keyword.

It is valid if the function body has only one statement:

const myFunction = (a)= > 'test'

myFunction() //'test'
Copy the code

Another example of returning an object (remember to wrap the return value in parentheses to avoid the interpreter thinking of it as a function body) :

const myFunction = (a)= > ({value: 'test'})

myFunction() //{value: 'test'}
Copy the code

How does this work in the arrow function

This is a difficult concept to master, and it is made different by context, and also by JavaScript modes (whether strict or not).

It’s important to clarify this concept, because arrow functions behave differently than regular functions.

When you define a method on an object, in normal functions this refers to the object, so you can:

const car = {
  model: 'Fiesta'.manufacturer: 'Ford'.fullName: function() {
    return `The ${this.manufacturer} The ${this.model}`}}Copy the code

Calling car.fullname() returns Ford Fiesta.

The arrow function’s this inherits from the execution context. The arrow function doesn’t bind this at all, so its value is queried in the call stack, so car.fullname () doesn’t make sense in this code, and returns undefined:

const car = {
  model: 'Fiesta'.manufacturer: 'Ford'.fullName: (a)= > {
    return `The ${this.manufacturer} The ${this.model}`}}Copy the code

Because of this, arrow functions are not suitable for object methods.

The arrow function also cannot be used to initialize an object’s constructor, which raises TypeError.

When dynamic context is not needed, regular functions should be used instead.

There are also problems with handling events. The DOM event listener sets this as the target element. If you need this from the event handler, you should use the normal function:

const link = document.querySelector('#link')
link.addEventListener('click', () = > {// this === window
})

const link = document.querySelector('#link')
link.addEventListener('click'.function() {
  // this === link
})
Copy the code

closure

This is a friendly introduction to the topic of closures and is key to understanding how JavaScript functions work.

If you’ve written JavaScript functions, you’ve already used closures. This is a key topic to understand and it affects what you do. When a function runs, it runs in the scope where it is defined, not where it is executed.

A scope is basically a collection of visible variables. A function remembers its lexical scope and can access variables defined in its parent scope.

In short, a function has a set of variables that can be accessed.

Let’s quickly verify this with an example:

const bark = dog= > {
  const say = `${dog}barked! `
  ;(() = > console.log(say))()
}

bark(`Roger`)
Copy the code

This is expected to print like: Roger Barked! .

If you want to return to the action, do this:

const prepareBark = dog= > {
  const say = `${dog}barked! `
  return (a)= > console.log(say)
}

const bark = prepareBark(`Roger`)

bark()
Copy the code

This code snippet prints Roger Barked! .

For a final example, get two different dogs prepareBark:

const prepareBark = dog= > {
  const say = `${dog}barked! `
  return (a)= > {
    console.log(say)
  }
}

const rogerBark = prepareBark(`Roger`)
const sydBark = prepareBark(`Syd`)

rogerBark()
sydBark()
Copy the code

Print:

Roger barked!
Syd barked!
Copy the code

As you can see, the result of the say variable is related to that returned by the function prepareBark.

The second call to prepareBark() redefines the new say variable, but does not affect the scope of the first prepareBark() call.

This is how closures work: the function returned remains in its original state in scope.

An array of

As JavaScript arrays get more and more functionality over time, it can sometimes be tricky to know what to use what methods. This section aims to explain what you should be using as of 2018.

Initializing an array

const a = []
const a = [1.2.3]
const a = Array.of(1.2.3)
const a = Array(6).fill(1) //init an array of 6 items of value 1
Copy the code

Don’t use old syntax (except for type arrays) :

const a = new Array(a)//never use
const a = new Array(1.2.3) //never use
Copy the code

Get the array length

const l = a.length
Copy the code

througheveryThrough the array

a.every(f)
Copy the code

Iterating through a until f() returns false.

throughsomeThrough the array

a.some(f)
Copy the code

Iterating through a until f() returns true.

Iterate through the array and return a new array of function results

const b = a.map(f)
Copy the code

Iterating through a returns the array of results from f() for each element of a.

Filter array

const b = a.filter(f)
Copy the code

Iterating through a returns a new array where f() is true for each element of a.

Reduce

a.reduce((accumulator, currentValue, currentIndex, array) = > {
  / /...
}, initialValue)
Copy the code

Reduce () calls the callback function for each item in the array and evaluates the result step by step. If initaiValue exists, accumulator is equal to this value on the first iteration.

Example:


Copy the code

foreach

ES6

a.forEach(f)
Copy the code

Repeat a to F without stopping.

Example:

a.forEach(v= > {
  console.log(v)
})
Copy the code

for… of

ES6

for (let v of a) {
  console.log(v)
}
Copy the code

for

for (let i = 0; i < a.length; i += 1) {
  //a[i]
}
Copy the code

To iterate through a, you can break the loop with a return or break, and exit the loop with a continue.

@@iterator

ES6

Get the value of the array iterator:

const a = [1.2.3]
let it = a[Symbol.iterator]()

console.log(it.next().value) / / 1
console.log(it.next().value) / / 2
console.log(it.next().value) / / 3
Copy the code

.entries() returns an iterator of key-value pairs:

let it = a.entries()

console.log(it.next().value) / / [0, 1]
console.log(it.next().value) / / [1, 2]
console.log(it.next().value) / / [2, 3]
Copy the code

.keys() returns an iterator containing all key names:

let it = a.keys()

console.log(it.next().value) / / 0
console.log(it.next().value) / / 1
console.log(it.next().value) / / 2
Copy the code

.next() returns undefined when the array ends. You can detect the end of the iteration by the value, done value returned by it.next(). Done is always true when iterating to the last element.

Appends the value to the end of the array

a.push(4)
Copy the code

Add values at the beginning of the array

a.unshift(0)
a.unshift(2 -.- 1)
Copy the code

Removes a value from an array

Delete the trailing value

a.pop()
Copy the code

Delete the leading value

a.shift()
Copy the code

Deletes a value at any position

a.splice(0.2) // get the first 2 items
a.splice(3.2) // get the 2 items starting from index 3
Copy the code

Do not use remove() because it leaves undefined values.

Remove and insert values

a.splice(2.3.2.'a'.'b') //removes 3 items starting from
//index 2, and adds 2 items,
// still starting from index 2
Copy the code

Merging multiple arrays

const a = [1.2]
const b = [3.4]
a.concat(b) // 1, 2, 3, 4
Copy the code

Find a specific element in an array

ES5

a.indexOf()
Copy the code

Returns the index of the first matched element, -1 if the element does not exist.

a.lastIndexOf()
Copy the code

Returns the index of the last element matched, -1 if the element does not exist.

ES6

a.find((element, index, array) = > {
  //return true or false
})
Copy the code

Returns the first element that matches the condition, or undefined if none exists.

Usually used this way:

a.find(x= > x.id === my_id)
Copy the code

The above example returns the first element of the array with id === my_id.

FindIndex returns the index of the first element that matches the condition, or undefined if none exists:

a.findIndex((element, index, array) = > {
  //return true or false
})
Copy the code

ES7

a.includes(value)
Copy the code

Return true if a contains value.

a.includes(value, i)
Copy the code

Returns true if a contains value after position I.

Gets a portion of the array

a.slice()
Copy the code

Sort an array

In alphabetical order (by ASCII values -0-9a-zA-z) :

const a = [1.2.3.10.11]
a.sort() //1, 10, 11, 2, 3

const b = [1.'a'.'Z'.3.2.11]
b = a.sort() //1, 11, 2, 3, Z, a
Copy the code

Custom sort

const a = [1.10.3.2.11]
a.sort((a, b) = > a - b) //1, 2, 3, 10, 11
Copy the code

The reverse

a.reverse()
Copy the code

Array to string

a.toString()
Copy the code

Returns a value of type string

a.join()
Copy the code

Returns a string concatenated with array elements. Pass parameters to custom delimiters:

a.join(', ')
Copy the code

Copy all values

const b = Array.from(a)
const b = Array.of(... a)Copy the code

Copy partial values

const b = Array.from(a, x => x % 2= =0)
Copy the code

Copies the value elsewhere in itself

const a = [1.2.3.4]
a.copyWithin(0.2) // [3, 4, 3, 4]
const b = [1.2.3.4.5]
b.copyWithin(0.2) // [3, 4, 5, 4, 5]
//0 is where to start copying into,
// 2 is where to start copying from
const c = [1.2.3.4.5]
c.copyWithin(0.2.4) // [3, 4, 3, 4, 5]
//4 is an end index
Copy the code

cycle

JavaScript provides a number of looping methods. This section explains all the looping methods in modern JavaScript through small examples and major properties.

for

const list = ['a'.'b'.'c']
for (let i = 0; i < list.length; i++) {
  console.log(list[i]) //value
  console.log(i) //index
}
Copy the code
  • Can be achieved bybreakinterruptforcycle
  • Can be achieved bycontinueSkip the currentforcycle

forEach

Introduced in ES5. Given an array, you can iterate through its properties with list.foreach () :

const list = ['a'.'b'.'c']
list.forEach((item, index) = > {
  console.log(item) //value
  console.log(index) //index
})
//index is optional
list.forEach(item= > console.log(item))
Copy the code

Unfortunately, you can’t break the cycle.

do… while

const list = ['a'.'b'.'c']
let i = 0
do {
  console.log(list[i]) //value
  console.log(i) //index
  i = i + 1
} while (i < list.length)
Copy the code

Break do… The while loop:

do {
  if (something) break
} while (true)
Copy the code

You can skip current do… with continue. The while loop:

do {
  if (something) continue
  //do something else
} while (true)
Copy the code

while

const list = ['a'.'b'.'c']
let i = 0
while (i < list.length) {
  console.log(list[i]) //value
  console.log(i) //index
  i = i + 1
}
Copy the code

We can break the while loop with break:

while (true) {
  if (something) break
}
Copy the code

The current while loop can be skipped with continue:

while (true) {
  if (something) continue
  //do something else
}
Copy the code

And the do… While… While loops at least once.

for… in

Iterate over all iterable property names of the object.

for (let property in object) {
  console.log(property) //property name
  console.log(object[property]) //property value
}
Copy the code

for… of

ES2015 introduced for… The of loop, which combines the ease of use of forEach with the ability to not break:

//iterate over the value
for (const value of ['a'.'b'.'c']) {
  console.log(value) //value
}

//get the index as well, using `entries()`
for (const [index, value] of ['a'.'b'.'c'].entries()) {
  console.log(index) //index
  console.log(value) //value
}
Copy the code

Note the use of const. This loop creates a new scope at each iteration, so we can safely use it instead of let.

for… in vs for… of

And for… The difference is that:

  • for... ofIterated attribute value
  • for... inIterated attribute name

The event

JavaScript in the browser uses an event-driven programming model. All things begin with events. This section describes how JavaScript event handlers work.

The event might be the DOM loading completes, or the asynchronous request ends, or the user clicks an element or scrolls a page, or the user presses a keyboard.

There are many different kinds of events.

Event handler

You can respond to all events through an event handler by calling the corresponding function when the event occurs.

You can register multiple handlers for the same event, and they will all be called when the event occurs.

JavaScript provides three ways to register event handlers:

Inline event handlers

This method is now rarely used due to its limitations, but in early JavaScript it was the only method:

<a href="site.com" onclick="dosomething();" >A link</a>Copy the code

DOM event handler

This method is often used when an object has only one event handler, and there is no way to add more than one handler in this example:

window.onload = (a)= > {
  //window loaded
}
Copy the code

This is also common when dealing with XHR requests:

const xhr = new XMLHttpRequest()
xhr.onreadystatechange = (a)= > {
  / /.. do something
}
Copy the code

You can check if the processor has already assigned a property by checking if (‘onsomething’ in window) {}.

Use the addEventListener ()

This is a very modern method. This method allows us to register multiple event handlers on demand, and you’ll find it the most popular:

window.addEventListener('load', () = > {//window loaded
})
Copy the code

Note: IE8 and below does not support this method and you can use attachEvent() instead. This is important if you need to be compatible with older browsers.

Listen for different elements

You can listen for Windows to intercept “global” events, such as keyboard use. You can also listen for events that happen on specific elements, such as a mouse click on a button.

That’s why addEventListener is sometimes called on a window, sometimes on a DOM element.

The event object

The Event handler gets an Event object as the first argument:

const link = document.getElementById('my-link')
link.addEventListener('click', event => {
  // link clicked
})
Copy the code

This object contains many useful properties and methods, such as:

  • target, the DOM element to which the event occurs
  • type, event type
  • stopPropagation(), to prevent DOM event propagation

(See the full list)

Other attributes are provided for specific events, and an Event is simply an interface to different events:

  • MouseEvent
  • KeyboardEvent
  • DragEvent
  • FetchEvent
  • , etc.

Each of the above links to the MDN page, where you can view all of their properties.

For example, when a keyboard event occurs, you can check which key was pressed to a readable value via the key property (Escape, Enter, etc.) :

window.addEventListener('keydown', event => {
  // key pressed
  console.log(event.key)
})
Copy the code

In mouse events we can wait until which button is pressed:

const link = document.getElementById('my-link')
link.addEventListener('mousedown', event => {
  // mouse button pressed
  console.log(event.button) //0=left, 2=right
})
Copy the code

Event bubbling and event capture

Event bubbling and event capture are two models of event propagation.

Suppose your DOM structure looks like this:

<div id="container">
  <button>Click me</button>
</div>
Copy the code

You want to keep track of when the user clicked the button, and you have two event handlers, one on the button and one on the #container. Remember, click events on child elements propagate to their parent elements, unless you prevent event propagation (more on that later).

These event handlers are invoked in the order determined by the event bubble/event capture model.

Bubbling means that the event propagates from the clicked element (child element) up to all ancestor elements, starting with the most recent.

In our example, the handler on the button will happen before # Container.

Capturing is just the opposite: the most external events occur before a particular handler, such as button.

The event bubble model is used by default.

You can also choose to use event capture by setting the third addEventListener parameter to true:

document.getElementById('container').addEventListener(
  'click', () = > {//window loaded
  },
  true
)
Copy the code

Note: The capture phase event handler is run first, followed by the bubbling event handler.

The order follows the principle that the DOM walks through all elements starting with the Window object until it finds the clicked object. When this is done, any bound event handlers are called (the capture phase). Once the target element is found, it repeats the process until it returns to the Window object, at which point it invokes the appropriate event handler (the bubble phase).

Stop transmission

The DOM element event will always be propagated on its parent tree unless it is manually prevented:

<html> <body> <section> <a id="my-link" ... >Copy the code

Click events on A propagate to section and then body.

You can stop event propagation by calling stopPropagation() at the end of the event handler:

const link = document.getElementById('my-link')
link.addEventListener('mousedown', event => {
  // process the event
  // ...
  
  event.stopPropagation()
})
Copy the code

Common event

This is a list of events that you will often use.

load

The load events for the window and body elements fire when the page loads.

Mouse events

The Click event is triggered when the mouse clicks. The DBClick event fires when the mouse is double-clicked, in which case, of course, the click event fires first. Mousedown, Mousemove, and Mouseup can be combined with drag events. Be careful with Mousemove, it fires many times during mouse movement (you’ll see throttling later).

Keyboard events

The keyDown event fires when the keyboard is pressed (and continues to fire while in the pressed state). The keyUp event is emitted when the keyboard is released.

rolling

The Scroll event is triggered each time the page is rolled. Inside the event handler, you can view the current scroll position via window.scrolly (Y-axis).

Note that this event is not a one-time event, it will continue to occur throughout the scroll, not just at the start and end of the scroll, so don’t do a lot of calculation and manipulation while handling the event – use throttling instead.

The throttle

As mentioned above, both Mousemove and Scroll are not one-off events; they continue to call the event handler during the operation. That’s because they need to provide the coordinates you need to know.

If you do complex operations in these event handlers, it will affect performance and give your web users a bad experience.

Libraries like Lodash provide 100-line throttling functions to help solve this problem. A simple and easy to understand implementation is to use timers to cache rolling events every 100ms:

let cached = null
window.addEventListener('scroll', event => {
  if(! cached) { setTimeout((a)= > {
      //you can access the original event at `cached`
      cached = null
    }, 100)
  }
  cached = event
})
Copy the code

Event loop

Event loops are the most important thing in JavaScript.

I’ve been using JavaScript for years without fully understanding how it works. It’s okay not to know the details, but in general, it’s helpful to know how it works, and you might be curious about it.

This section is devoted to explaining the inner details of how JavaScript works single-threaded and how asynchronous functions are handled.

Your JavaScript code runs in a single thread and only one thing happens at a time. This is a very useful limitation, it simplifies a lot of programs, you don’t have to worry about concurrency. You just have to focus on

How to write code to avoid thread-blocking things like synchronous network requests or infinite loops.

In general, most browsers have a separate event loop for each browser TAB to isolate the process and prevent pages with infinite loops or heavy processing from clogging the entire browser. Browsers manage multiple concurrent event loops to handle API calls. Web Workers also run in their own event loop.

You just need to understand that your code runs in a single event loop and write your code with that in mind to avoid blocking it.

Blocking event loop

Any JavaScript code that takes too long to return control to the event loop blocks the execution of other code within the page, or even the UI thread, preventing the user from clicking, scrolling, and so on.

Most JavaScript primitives are non-blocking, such as network requests, Node.js file system operations, and so on. Blocking is accidental, which is why JavaScript is based on a lot of callbacks and more recently Promises and Async /await.

The call stack

The call stack is the LIFO queue (Last In, First Out).

The event loop constantly checks to see if there are still functions in the call stack that need to be run. At the same time, it adds the functions it finds to the call stack and executes them in order.

Do you know the error stack trace in the debugger or browser console? The browser looks up the function name in the call stack and then marks which function triggered the current call:

A simple event loop illustration

Here’s an example:

const bar = (a)= > console.log('bar')
const baz = (a)= > console.log('baz')

const foo = (a)= > {
  console.log('foo')
  bar()
  baz()
}

foo()
Copy the code

This code prints:

foo
bar
baz
Copy the code

As expected.

When this code runs, initially foo() is called, bar() is called inside foo(), then baz() is called.

At this point our call stack looks like this:

Each iteration’s event loop looks to see if there is anything left in the call stack and executes it:

Until the entire call stack is empty.

Function execution queue

The above example is common and nothing special: JavaScript finds what needs to be executed and executes it in order.

Let’s look at how to delay function execution until the call stack is empty.

Calling a function with setTimeout(() => {}, 0) executes the function the moment all other functions have finished executing.

Here’s an example:

const bar = (a)= > console.log('bar')
const baz = (a)= > console.log('baz')

const foo = (a)= > {
  console.log('foo')
  setTimeout(bar, 0)
  baz()
}

foo()
Copy the code

The results were surprising:

foo
baz
bar
Copy the code

When this code runs, foo() is called first. Foo () internally calls setTimeout, passing bar as an argument to the timer, passing 0 to indicate that it executes immediately, and calling baz().

The stack is called like this:

The order of execution of all functions in our program:

Why is that?

The message queue

When setTimeout() is called, the browser or Node.js starts timing. In this example, we use 0 as the delay time, when the time is up, the callback function will be pushed to the message queue.

Message queues also contain click or keyboard events sent by the user, or queues that predate your code to get responses, or DOM events like onLoad.

The entire loop takes precedence over the call stack, processing everything it finds in the call stack, and picking it up in the event queue once it’s empty.

We don’t have to wait for other functions like setTimeout, fetch, etc. to do their own work because they are provided by the browser and have their own threads. For example, if you set a setTimeout timer with a delay of two seconds, you don’t have to wait two seconds – wait to finish somewhere else.

ES6 Job Queue

ECMASciprt 2015 introduces the work queue concept, in Promises (also introduced in ES6/ES2015). This is a way to execute asynchronous functions as quickly as possible, rather than at the end of the call stack.

Promises made before the end of the current function will be implemented after the current function.

I’ve found that riding a roller coaster at an amusement park explains this perfectly: the message queue puts you behind other visitors, and the work queue is a fast pass that allows you to jump the queue and get on the coaster early.

Example:

const bar = (a)= > console.log('bar')
const baz = (a)= > console.log('baz')

const foo = (a)= > {
  console.log('foo')
  setTimeout(bar, 0)
  new Promise((resolve, reject) = >
    resolve('should be right after baz, before bar')
  ).then(resolve= > console.log(resolve))
  baz()
}

foo()
Copy the code

This will print:

foo
baz
should be right after baz, before bar
bar
Copy the code

This is the biggest difference between Promises (including Promises based Async/await) and the native old asynchronous function setTimeout() or other platform apis.

Asynchronous programming and callbacks

JavaScript is asynchronous and single-threaded by default. This means that the code cannot create new threads and run in parallel. Let’s see what asynchronous code is.

Asynchrony in programming languages

Computers are asynchronous by design.

Asynchrony is something that can happen independently of the main program flow.

On today’s consumer computers, each program runs for a special period of time and then stops to allow other programs to run. This happens so quickly that we don’t notice it. We think our computers are running many programs at once, but this is a misconception (except for multi-process machines).

Program internal use interrupt, a signal submitted to the processor for the system’s attention.

I won’t go into that, but keep in mind that it’s very convenient for asynchronous programs to stop executing before they’re noticed, while the computer can do something else. When a program waits for a network response, it must wait for the request to end before terminating.

C, Java, C#, PHP, Go, Ruby, Swift, Python are all asynchronous by default. Some of them use threads to handle asynchrony and create a new process.

JavaScript

JavaScript is asynchronous and single-threaded by default. This means that the code cannot create new threads and run in parallel.

Each line of code is executed one after the other, for example:

const a = 1
const b = 2
const c = a * b
console.log(c)
doSomething()
Copy the code

But JavaScript was built for browsers, and its primary job at first was to respond to user actions such as onClick, onMouseOver, onChnage, onSubmit, and so on. How can it use asynchronous programming mode?

The answer lies in its environment. Browsers solve this problem by providing a set of apis that handle this functionality.

More recently, Node.js has introduced a non-blocking I/O environment that extends this concept to file access, network calls, and more.

The callback function

You have no way of knowing when the user is going to click the button, so you define an event handler for the Click event. This event handler accepts a function that will be called when the event is raised:

document.getElementById('button').addEventListener('click', () = > {//item clicked
})
Copy the code

This is also called a callback function.

A callback is a simple function that is passed to other functions as a value and is called only when an event occurs. We can do this because JavaScript first-class functions can bind to variables and pass in other functions (called higher-order functions).

It is common to wrap all your code in the load event listener on the window object so that the code will only run when the page is ready:

window.addEventListener('load', () = > {//window loaded
  //do what you want
})
Copy the code

Callbacks are used everywhere, not just for DOM events.

A common example is using timers:

setTimeout((a)= > {
  // runs after 2 seconds
}, 2000)
Copy the code

The XHR request also accepts a callback function. In this example, assign a function to a property so that it will be called when a specific event (in this case, a change in request state) occurs:

const xhr = new XMLHttpRequest()
xhr.onreadystatechange = (a)= > {
  if (xhr.readyState === 4) {
    xhr.status === 200 ? console.log(xhr.responseText) : console.error('error')
  }
}
xhr.open('GET'.'https://yoursite.com')
xhr.send()
Copy the code

Handle errors in the callback

How do you handle callback errors? A common strategy is to take node.js’s approach: the first argument to a callback function is always an error object: error-first callback.

If there are no errors, this object is null. If an error occurs, it contains a description of the error and other information.

fs.readFile('/file.json', (err, data) => {
  if(err ! = =null) {
    //handle error
    console.log(err)
    return
  }
  //no errors, process data
  console.log(data)
})
Copy the code

Callbacks come with problems

Callbacks make simple code simpler!

However, each callback adds a level of nesting, and if you have many callbacks, the code becomes very complex:

window.addEventListener('load', () = > {document.getElementById('button').addEventListener('click', () => {
    setTimeout((a)= > {
      items.forEach(item= > {
        //your code here})},2000)})})Copy the code

This is just a simple level 4 code, but I see a lot of nesting and it’s not fun.

How to solve this problem?

An alternative to a callback

Since ES6, JavaScript has introduced many features that allow us to write asynchronous code elegantly without callbacks:

  • Promises (ES6)
  • Async/Await (ES8)

Promises

Promises is a JavaScript solution to asynchronous code that requires too many callbacks.

Promises are usually defined as a proxy for a value that will eventually become available.

Promises are a way to solve asynchronous code without having too many callbacks in your code. Although it has become popular over the years, it was also introduced as a standard in ES2015 and replaced by async functions in ES2017.

How Promises Work (Brief)

Once a promise is invoked, it becomes a pending state. This means that the caller will continue to execute, wait for the result of its own processing, and then give some feedback to the calling function.

At this point, the calling function waits for the Promise to return the Resolved state or Rejected state, but you know that JavaScript is asynchronous, so the function will continue to execute the rest of the code while the Promise works.

Which JS apis use Promises?

In addition to your own code and a few libraries, Web APIS now use Promises:

  • Battery API
  • Fetch API
  • Service Workers

You can’t possibly not use Promises in modern JavaScript, so let’s dig a little deeper.

Create a promise

The Promise API exposes a Promise constructor that you can initialize with new Promise() :

let done = true

const isItDoneYet = new Promise(
  (resolve, reject) = > {
    if (done) {
      const workDone = 'Here is the thing I built'
      resolve(workDone)
    } else {
      const why = 'Still working on something else'
      reject(why)
    }
  }
)
Copy the code

You can see that this promise returns a resolved promise if the global constant done is true, or a rejected promise otherwise.

Use resolve and reject to return a value. In the above example, we returned a string, or we returned an object.

The use of promise

In the previous section, we showed you how to create a promise.

Now let’s look at how to use promise.

const isItDoneYet = new Promise(
  / /...
)

const checkIfItsDone = (a)= > {
  isItDoneYet
    .then((ok) = > {
      console.log(ok)
    })
    .catch((err) = > {
      console.error(err)
    })
}
Copy the code

Running checkIfItsDone() executes the isItDoneYet() promise and waits for it to resolve to call the then callback. If an error occurs, the error is handled in the catch callback.

Chain promise

A promise can return another promise to form a chained promise.

The Fecth API (the superior API of the XMLHttpRequest API) provides a good example. We can use it to get resources and chain them with promises.

The Fetch API is based on the Promise mechanism, and calling Fetch () is the same as defining a promise with new Promise ().

Chain promise example

const status = (response) = > {
  if (response.status >= 200 && response.status < 300) {
    return Promise.resolve(response)
  }
  return Promise.reject(new Error(response.statusText))
}

const json = (response) = > response.json()
fetch('/todos.json')
  .then(status)
  .then(json)
  .then((data) = > { console.log('Request succeeded with JSON response', data) })
  .catch((error) = > { console.log('Request failed', error) })
Copy the code

In this example, we call fetch() to get a TODO list from the todos.json file in the root domain. We create a Promises chain.

Running fetch() returns a response containing a number of attributes, of which we refer:

  • statusRepresents the value of the HTTP status
  • statusTextIs a status message when the request is successfulOK

Response also has a JSON () method that converts the successfully obtained response content into JSON and returns it as a promise.

Based on this: The first promise in the chain is the function status() we defined, which checks the status of the response and rejects the promise if the result is not between 200 and 299. This causes the promise chain to skip all lists and go straight to the catch() statement at the end, printing Request failed and error messages.

If successful, it calls the JSON () function we defined. When successful, the previous promise returns a Response object as input to the second promise.

In this process, we return the processed JSON data, so the third Promise gets the JSON object directly:

.then((data) = > {
  console.log('Request succeeded with JSON response', data)
})
Copy the code

This will be printed out on the console.

Handling errors

In the example above, Promises have a catch block behind them. If anything goes wrong with chain Promises or manually returns to rejects, control of the program is given to the closest catch() statement behind the error code.

new Promise((resolve, reject) = > {
  throw new Error('Error')
})
  .catch((err) = > { console.error(err) })

// or

new Promise((resolve, reject) = > {
  reject('Error')
})
  .catch((err) = > { console.error(err) })
Copy the code

Cascading error

If another error is thrown inside a catch(), you can add a second catch() to handle it, and so on.

new Promise((resolve, reject) = > {
  throw new Error('Error')
})
  .catch((err) = > { throw new Error('Error') })
  .catch((err) = > { console.error(err) })
Copy the code

Choreography promises

Promise.all()

Promises. Promise.all() can help you define a set of promises and wait for them all to be completed before you do anything about them. Such as:

const f1 = fetch('/something.json')
const f2 = fetch('/something2.json')

Promise.all([f1, f2]).then((res) = > {
    console.log('Array of results', res)
})
.catch((err) = > {
  console.error(err)
})
Copy the code

ES2015’s structural syntax also allows you to do this:

Promise.all([f1, f2]).then(([res1, res2]) = > {
    console.log('Results', res1, res2)
})
Copy the code

This is not limited to fetch; any promise can be handled.

Promise.race()

Race () is run when the incoming promise has a complete promise.race (), and the additional callback function is run only once, passing in the result returned by the promise that ran first. Example:

const first = new Promise((resolve, reject) = > {
    setTimeout(resolve, 500.'first')})const second = new Promise((resolve, reject) = > {
    setTimeout(resolve, 100.'second')})Promise.race([first, second]).then((result) = > {
  console.log(result) // second
})
Copy the code

Async and Await

Now, we’ll explore the more modern asynchronous function approach in JavaScript. JavaScript went from callback functions to promises in a very short time, and async and await have made asynchronous JavaScript much simpler since ES2017.

Asynchronous functions that combine promises and generators are basically abstract methods on Promises. I repeat: Async and await are based on Promises.

Why async and await?

They reduce promises’ boilerplate ‘promises and the “can’t break the chain” restrictions.

Promises were introduced in ES2015 as a solution to the problem of asynchronous code, which was well done. However, in the two years between ES2015 and ES2017 people have found that Promises are not the final solution.

Promises were introduced to solve the famous problem of callback hell, but were also complex in their own right, introducing more complex syntax.

They’re a good start, but there should be a better syntax for developers to use, so async functions ** were born. It makes the code look synchronous, but it’s asynchronous and doesn’t block subsequent code.

The working principle of

An asynchronous function returns a promise, as in this example:

const doSomethingAsync = (a)= > {
    return new Promise((resolve) = > {
        setTimeout((a)= > resolve('I did something'), 3000)})}Copy the code

You call the function with await in front and the code will pause until the promise becomes Resolved or Rejected. Note that the delegate function must be defined as async. Example:

const doSomething = async() = > {console.log(await doSomethingAsync())
}
Copy the code

example

Here is an example of running a function asynchronously with async/await:

const doSomethingAsync = (a)= > {
    return new Promise((resolve) = > {
        setTimeout((a)= > resolve('I did something'), 3000)})}const doSomething = async() = > {console.log(await doSomethingAsync())
}

console.log('Before')
doSomething()
console.log('After')
Copy the code

The code above is printed in the browser console:

Before
After
I did something //after 3s
Copy the code

It’s all promise

Prefixing async to any function causes the function to return a promise. Even if it doesn’t explicitly do so, it internally tells it to return a promise. That’s why this code is legal:

const aFunction = async() = > {return 'test'
}

aFunction().then(alert) // This will alert 'test'
Copy the code

It’s similar to this:

const aFunction = async() = > {return Promise.resolve('test')
}

aFunction().then(alert) // This will alert 'test'
Copy the code

Code is easier to read

See how simple our code above is compared to the native Promise chain callback. This is just a very simple example, and the more complex the code, the more powerful it is.

In Promises, for example, you need to take a JSON resource and parse it:

const getFirstUserData = (a)= > {
  return fetch('/users.json') // get users list
    .then(response= > response.json()) // parse JSON
    .then(users= > users[0]) // pick first user
    .then(user= > fetch(`/users/${user.name}`)) // get user data
    .then(userResponse= > response.json()) // parse JSON
}

getFirstUserData()
Copy the code

Implement the same requirement with async/await:

const getFirstUserData = async() = > {const response = await fetch('/users.json') // get users list
  const users = await response.json() // parse JSON
  const user = users[0] // pick first user
  const userResponse = await fetch(`/users/${user.name}`) // get user data
  const userData = await user.json() // parse JSON
  return userData
}

getFirstUserData()
Copy the code

Concatenation of multiple asynchronous functions

Asynchronous functions can be easily concatenated, and the syntax is much more readable than native promises:

const promiseToDoSomething = (a)= > {
    return new Promise(resolve= > {
        setTimeout((a)= > resolve('I did something'), 10000)})}const watchOverSomeoneDoingSomething = async() = > {const something = await promiseToDoSomething()
    return something + ' and I watched'
}
const watchOverSomeoneWatchingSomeoneDoingSomething = async() = > {const something = await watchOverSomeoneDoingSomething()
    return something + ' and I watched as well'
}

watchOverSomeoneWatchingSomeoneDoingSomething().then((res) = > {
    console.log(res)
})
Copy the code

Print out:

I did something and I watched and I watched as well
Copy the code

Easier to debug

Debugging promises is difficult because the debugger can’t skip asynchronous code.

Async/await is easy because to the compiler it is synchronous code.

Cyclic scope

JavaScript’s looping scope can be a pain in the neck for developers. We will learn about var and let techniques in the loop scope.

Example:

const operations = []

for (var i = 0; i < 5; i++) {
  operations.push((a)= > {
    console.log(i)
  })
}

for (const operation of operations) {
  operation()
}
Copy the code

It iterates five times, adding a function to the Operations array each time. This function prints out the index variable I for the loop. Run these functions later.

The desired results are:

0
1
2
3
4
Copy the code

But the real result is:

5
5
5
5
5
Copy the code

Why is that? The reason is var.

Since the var declaration is promoted, the above code is equivalent to:

var i;
const operations = []

for (i = 0; i < 5; i++) {
  operations.push((a)= > {
    console.log(i)
  })
}

for (const operation of operations) {
  operation()
}
Copy the code

So, in a for-of loop, I is always equal to 5. I is equal to 5 every time I call this function.

So what can we do to make it work the way we want it to?

The simplest way is to use the let declaration. Introduced in ES2015, it avoids some of the weird things that var declarations bring.

Change the var in the loop to let and everything works fine:

const operations = []

for (let i = 0; i < 5; i++) {
  operations.push((a)= > {
    console.log(i)
  })
}

for (const operation of operations) {
  operation()
}
Copy the code

Output:

0
1
2
3
4
Copy the code

How is that possible? The reason is that each iteration of I creates a new scope, and each function added to the Operations array gets a copy of I at that time.

Another solution to this problem, which was common before ES6, is to use the Immediate Execution function Expression (IIFE).

In this case, you can wrap the whole function and bind I. This creates a function that executes immediately each time and returns a new function, so we can execute it later:

const operations = []

for (var i = 0; i < 5; i++) {
  operations.push(((j) = > {
    return (a)= > console.log(j)
  })(i))
}

for (const operation of operations) {
  operation()
}
Copy the code

The timer

When writing JavaScript code, you might want to delay the execution of a function. In this section we discuss how to use setTimeout and setInterval to schedule future functions.

setTimeout()

When writing JavaScript code, you might want to delay the execution of a function. The job is left to setTimeout. You can specify a function to delay execution and the required delay time, in milliseconds:

setTimeout((a)= > {
  // runs after 2 seconds
}, 2000)

setTimeout((a)= > {
  // runs after 50 milliseconds
}, 50)
Copy the code

This syntax defines a new function. You can call it from anywhere, you can also pass an existing function name, and you can set some parameters:

const myFunction = (firstParam, secondParam) = > {
  // do something
}

// runs after 2 seconds
setTimeout(myFunction, 2000, firstParam, secondParam)
Copy the code

SetTimeout returns a timer identifier. It is not usually used, but you can save the id and empty it if you need to delete the timer function:

const id = setTimeout((a)= > {
  // should run after 2 seconds
}, 2000)

// I changed my mind
clearTimeout(id)
Copy the code

The zero time delay

If you specify a delay of 0, the callback will execute as quickly as possible, but only after the current function has finished executing:

setTimeout((a)= > {
  console.log('after ')},0)

console.log(' before ')
Copy the code

It prints before after.

By ordering functions in the scheduler, this is particularly useful for avoiding blocking the CPU on intensive tasks and enabling other functions to perform heavy computations.

Some browsers (IE and Edge) implement the setImmediate() method for this purpose, but it does not become standard and cannot be used in other browsers. But it is available in Node.js.

setInterval()

SetInterval is similar to setTimeout, except that instead of running the callback once, it can keep running at a specified interval (in milliseconds) :

setInterval((a)= > {
  // runs every 2 seconds
}, 2000)
Copy the code

The above function runs every two seconds, unless you stop it with clearInterval, passing in the interval ID returned by setInterval:

const id = setInterval((a)= > {
  // runs every 2 seconds
}, 2000)

clearInterval(id)
Copy the code

It is common to call clearInterval in the setInterval callback, leaving it to decide if it needs to continue running. The code in the example runs until app.SomethingiWait is equal to arrived:

const interval = setInterval((a)= > {
  if (App.somethingIWait === 'arrived') {
    clearInterval(interval)
    return
  }
  // otherwise do things
}, 100)
Copy the code

Recursive setTimeout

SetInterval executes a function every n milliseconds, regardless of whether the function is complete. There is no problem if functions all take the same time to execute:

However, there may be inconsistent execution time, such as network conditions:

And the execution time overlaps with the next one:

To avoid this, you can use recursive setTImeout, which is called after the callback has finished:

const myFunction = (a)= > {
  // do something
  setTimeout(myFunction, 1000)
}

setTimeout(
  myFunction()
}, 1000)
Copy the code

To achieve this:

SetTimeout and setInterval are available in the Timers module of Node.js.

Node.js also provides setImmediate(), which uses the same effect as setTimeout(() => {}, 0), primarily for Node.js event loops.

This

The value of this depends on where it is used. Not knowing this little detail can be heady, so take five minutes to learn it.

Strict mode of this

Strictly, this is always undefined outside the object.

Notice I mentioned strict mode. If strict mode is not turned on (by default, unless you explicitly add Use Strict to the file header), otherwise known as sloppy mode, the following this points to global objects. In the browser environment, the window object.

This in the method

Methods are functions attached to objects.

You can see different formats, such as:

const car = {
  maker: 'Ford'.model: 'Fiesta',
  drive() {
    console.log(`Driving a The ${this.maker} The ${this.model}car! `)
  }
}

car.drive()
//Driving a Ford Fiesta car!
Copy the code

In this example, the regular function is used, and this automatically binds the object.

Drive: function() {drive: function() { } same, only shorter:

const car = {
  maker: 'Ford'.model: 'Fiesta'.drive: function() {
    console.log(`Driving a The ${this.maker} The ${this.model}car! `)}}Copy the code

The same is true in this example:

const car = {
  maker: 'Ford'.model: 'Fiesta'
}

car.drive = function() {
  console.log(`Driving a The ${this.maker} The ${this.model}car! `)
}

car.drive()
//Driving a Ford Fiesta car!
Copy the code

The arrow function is different because it is a lexical binding:

const car = {
  maker: 'Ford'.model: 'Fiesta'.drive: (a)= > {
    console.log(`Driving a The ${this.maker} The ${this.model}car! `)
  }
}

car.drive()
//Driving a undefined undefined car!
Copy the code

Bind arrow function

You can’t bind values to arrow functions like normal functions. This is because they work differently. This is a lexical binding, meaning that its values come from the context in which they are defined.

Explicitly pass the object to which this points

JavaScript provides a way to map this to an object.

Use bind() at the function declaration stage:

const car = {
  maker: 'Ford'.model: 'Fiesta'
}

const drive = function() {
  console.log(`Driving a The ${this.maker} The ${this.model}car! `)
}.bind(car)

drive()
//Driving a Ford Fiesta car!
Copy the code

You can also remap an existing object as this:

const car = {
  maker: 'Ford'.model: 'Fiesta',
  drive() {
    console.log(`Driving a The ${this.maker} The ${this.model}car! `)}}const anotherCar = {
  maker: 'Audi'.model: 'A4'
}

car.drive.bind(anotherCar)()
//Driving a Audi A4 car!
Copy the code

Use call() and apply() at the function call stage:

const car = {
  maker: 'Ford'.model: 'Fiesta'
}

const drive = function(kmh) {
  console.log(`Driving a The ${this.maker} The ${this.model} car at ${kmh}km/h! `)
}

drive.call(car, 100)
//Driving a Ford Fiesta car at 100 km/h!

drive.apply(car, [100])
//Driving a Ford Fiesta car at 100 km/h!
Copy the code

The first argument passed to call() or apply() is always bound to this. Call () differs from apply() in that apply() takes an array of arguments as function arguments, whereas call() can take multiple arguments.

Special cases in browser event handling

In the event handler callback. This refers to the HTML element that received the event:

document.querySelector('#button').addEventListener('click'.function(e) {
  console.log(this) //HTMLElement
})
Copy the code

You can bind like this:

document.querySelector('#button').addEventListener(
  'click'.function(e) {
    console.log(this) //Window if global, or your context
  }.bind(this))Copy the code

Strict mode

Strict mode is an ES5 feature that makes JavaScript perform better – turning on strict mode changes the semantics of the JavaScript language. Knowing the difference between strict and generic patterns, often referred to as sloppy patterns, is important in JavaScript code.

Strict mode mainly removes functionality that exists in ES3 and is deprecated starting with ES5 (the need for backward compatibility has not been removed).

How do I start Strict mode

Strict mode is optional. With incompatible changes, we can’t simply change the default behavior of the language, which breaks a lot of JavaScript code, and JavaScript has gone to great lengths to ensure that code from 1996 still works today. And that’s the key to its success.

So when we need to turn on strict mode, we have the Use Strict directive. You can place it at the beginning of the file and apply it throughout the file:

'use strict'

const name = 'Flavio'
const hello = (a)= > 'hey'

/ /...
Copy the code

You can also start strict mode in a separate function by placing use strict at the beginning of the function body:

function hello() {
  'use strict'
  
  return 'hey'
}
Copy the code

This is useful for working with legacy code that you don’t have the time to test or the confidence to turn on strict mode throughout the file.

Changes in strict patterns

Unexpected global variables

If you bind a value to an undeclared variable, JavaScript creates the variable on the global object by default:

; (function() {
  variable = 'hey'}) () ((a)= > {
  name = 'Flavio'
})()

variable //'hey'
name //'Flavio'
Copy the code

Turn on strict mode, doing so throws an error:

; (function() {
  'use strict'
  variable = 'hey'}) () ((a)= > {
  'use strict'
  myname = 'Flavio'}) ()Copy the code

Distribution of error

JavaScript quietly handles some conversion errors.

In strict mode, these errors are shown:

const undefined = 1((a)= > {
  'use strict'
  undefined = 1}) ()Copy the code

Infinity, NaN, Eval, Arguments, etc.

In JavaScript, you can define a non-writable object property, such as:

const car = {}
Object.defineProperty(car, 'color', { value: 'blue'.writable: false })
Copy the code

In strict mode, you can’t override this value, but in sloppy mode you can:

Same principle as Getters:


Copy the code

An unextensible object can be extended in sloppy mode:

const car = { color: 'blue' }
Object.preventExtensions(car)

car.model = 'Fiesta'(
  //ok() = > {'use strict'
    car.owner = 'Flavio' //TypeError: Cannot add property owner, object is not extensible
  }
)()
Copy the code

And you can set the original value of the property without any error, but also does not take effect:

true.false = ' '(
  / /"
  1
).name =
  'xxx' //'xxx'
var test = 'test' //undefined
test.testing = true //true
test.testing //undefined
Copy the code

None of this is allowed in strict mode:

;(() = > {
  'use strict'
  true.false = ' '(
    //TypeError: Cannot create property 'false' on boolean 'true'
    1
  ).name =
    'xxx' //TypeError: Cannot create property 'name' on number '1'
  'test'.testing = true //TypeError: Cannot create property 'testing' on string 'test'}) ()Copy the code

Delete the error

In sloppy mode, JavaScript will only return false if you try to delete an attribute value that cannot be deleted, but in strict mode, it will raise TypeError:

delete Object.prototype(
  //false() = > {'use strict'
    delete Object.prototype //TypeError: Cannot delete property 'prototype' of function Object() { [native code] }
  }
)()
Copy the code

Function parameters of the same name

In general functions, there may be conflicting parameter names:

(function(a, a, b) {
  console.log(a, b)
})(1.2.3)
/ / 2, 3

(function(a, a, b) {
  'use strict'
  console.log(a, b)
})(1.2.3)
//Uncaught SyntaxError: Duplicate parameter name not allowed in this context
Copy the code

In this example, the arrow function always throws a SyntaxError:

((a, a, b) = > {
  console.log(a, b)
})(1.2.3)
//Uncaught SyntaxError: Duplicate parameter name not allowed in this context
Copy the code

octal

Octal syntax is disabled in strict mode. By default, prefixing a digit with an octal compatible 0 can be interpreted as an octal digit (sometimes confusing) :

((a)= > {
  console.log(010)
})()
/ / 8

((a)= > {
  'use strict'
  console.log(010)
})()
//Uncaught SyntaxError: Octal literals are not allowed in strict mode.
Copy the code

You can still use octal digits in strict mode with the 0oXX syntax:

;(() = > {
  'use strict'
  console.log(0o10)
})()
/ / 8
Copy the code

Removed with

Strict mode does not use the with keyword, removing some boundary cases for better compiler level optimization.

Execute functions now (IIFE)

Immediate functions are executed as soon as they are created.

Executing functions immediately is useful because they do not contaminate global variables and can isolate variable declarations.

The syntax for executing a function immediately looks like this:

; (function() {
  / * * /}) ()Copy the code

The immediate function can also be used with the arrow function:

;(() = > {
  / * * /}) ()Copy the code

Basically, we define the function in parentheses followed by a pair of () to execute the function :(/* function */)().

These parentheses are actually what makes our function internally treated as an expression; otherwise, the function declaration would be invalid because we didn’t specify any name:

Function declarations need a name; function expressions do not.

You can also put the call parentheses inside the expression parentheses, which are the same, just written differently:

(function() {
  / * * /} ()) ((a)= > {
  / * * /} ())Copy the code

Use unary operators

Using arbitrary unary operators with IIFE is strange, but useful in practice:

; - (function() {
  / * * /}) () + (function() {
 / * * /}) () ~ (function() {
  / * * /}) ()! (function() {
  / * * /}) ()Copy the code

(Invalid on arrow function)

Named after the IIFE

IIFE can also name regular functions (not arrow functions). This does not cause the function to “leak” into the global scope, and it cannot be called again after execution:

; (function doSomething() {
  / * * /}) ()Copy the code

Semicolon before IIFE

You will see:

; (function() {
  / * * /}) ()Copy the code

This is to prevent problems when putting two files together. Because JavaScript does not enforce semicolons, you might use statements to concatenate a file in the last line, resulting in syntax errors.

This problem can be solved with a “clever” code packaging tool such as Webpack.

Mathematical operator

It is common to perform mathematical operations on any programming language. JavaScript provides several operators to help us manipulate numbers.

Arithmetic operator

Plus (+)

const three = 1 + 2
const four = three + 1
Copy the code

The + operator also concatenates strings, so note:

const three = 1 + 2
three + 1 / / 4
'three' + 1 // three1
Copy the code

Minus (-)

const two = 4 - 2
Copy the code

In addition to (/)

Returns the quotient between the first and second digits:

const result = 20 / 5 //result === 4
const result = 20 / 7 / / result = = = 2.857142857142857
Copy the code

If you divide by 0, JavaScript does not throw any errors and instead returns Infinity (-infinity if it is negative).

1 / 0 //Infinity
- 1 / 0 //-Infinity
Copy the code

Mod (%)

Mod is useful in many cases:

const result = 20 % 5 //result === 0
const result = 20 % 7 //result === 6
Copy the code

Mod 0 is always NaN, meaning “not a number” :

1 % 0 //NaN
- 1 % 0 //NaN
Copy the code

By (*)

1 * 2 / / 2
- 1 * 2 / / - 2
Copy the code

Exponentiation (**)

Multiply the first operand times the second operand:

1台湾国2 / / 1
2台湾国1 / / 2
2台湾国2 / / 4
2台湾国8 / / 256
8台湾国2 / / 64
Copy the code

Unary operator

Incrementing (++)

Increasing numbers. This is a unary operator that returns the incremented value if placed before a number. If you place it after a number, the initial value is returned and then incremented.

let x = 0
x++ / / 0
x / / 1
++x / / 2
Copy the code

Diminishing (–)

Similar to the increment operator, except that it decrement values.

let x = 0
x-- / / 0
x / / 1
--x / / - 2
Copy the code

One minus (-)

Returns a negative value

let x = 2
-x / / - 2
x / / 2
Copy the code

Unary plus theta plus theta.

If the target is not a number, it will try to convert it. Otherwise, do nothing.

let x = 2
+x / / 2

x = '2'
+x / / 2

x = '2a'
+x //NaN
Copy the code

A quick assignment

The regular assignment operator = has a shortcut for all mathematical operators that allows you to combine assignments, assigning the result of the first and second operands to the first operand.

They are:

  • + =: Addition assignment
  • - =: Division assignment
  • * =: multiplication assignment
  • / =: Division assignment
  • % =: Take the remainder assignment
  • * * =: Exponentiate assignment

Example:

const a = 0
a += 5 //a === 5
a -= 2 //a === 3
a *= 2 //a === 6
a /= 2 //a === 3
a %= 2 //a === 1
Copy the code

priority

Each complex statement introduces a priority problem. Look at this:

const a = 1 * 2 + 5 / 2 % 2
Copy the code

That equals 2.5. But why? Which operation comes first and which comes later?

Some operators have higher precedence than others. Its priority follows the following rules:

  • - + ++ --Unary operator, increment, decrement
  • * / %Multiplication/division
  • + -Addition/subtraction
  • = + = - = * = / = % = * * =The assignment operation

Sibling operators (such as + and -) are executed in order.

Based on the order above, we can calculate this formula:

const a = 1 * 2 + 5 / 2 % 2
const a = 1 * 2 + 5 / 2 % 2
const a = 2 + 2.5 % 2
const a = 2 + 0.5
const a = 2.5
Copy the code

The Math object

The Math object contains a number of math-related tools. Let’s see what we have.

constant

function

All function methods are static and Math cannot be inherited.

Math.abs()

Returns the absolute value of a number

Math.abs(2.5) / / 2.5
Math.abs(2.5) / / 2.5
Copy the code

Math.acos()

Returns the arccosine value. The argument must be between -1 and 1.

Math.acos(0.8) / / 0.6435011087932843
Copy the code

Math.asin()

Returns the arcsine value, which must be between -1 and 1.

Math.asin(0.8) / / 0.9272952180016123
Copy the code

Math.atan()

Returns the arctangent value

Math.atan(30) / / 1.5374753309166493
Copy the code

Math.atan2()

Returns the arctangent of its argument quotient

Math.atan2(30.20) / / 0.982793723247329
Copy the code

Math.ceil()

Take up the whole

Math.ceil(2.5) / / 3
Math.ceil(2) / / 2
Math.ceil(2.1) / / 3
Math.ceil(2.99999) / / 3
Copy the code

Math.cos()

Cosine of an Angle in radians

Math.cos(0) / / 1
Math.cos(Math.PI) / / 1
Copy the code

Math.exp()

Return math.e to the argument power

Math.exp(1) / / 2.718281828459045
Math.exp(2) / / 7.38905609893065
Math.exp(5) / / 148.4131591025766
Copy the code

Math.floor()

Take down the whole

Math.floor(2.5) / / 2
Math.floor(2) / / 2
Math.floor(2.1) / / 2
Math.floor(2.99999) / / 2
Copy the code

Math.log()

Returns the natural logarithm of base e

Math.log(10) / / 2.302585092994046
Math.log(Math.E) / / 1
Copy the code

Math.max()

Returns the maximum value of a sequence of numbers passed in

Math.max(1.2.3.4.5) / / 5
Math.max(1) / / 1
Copy the code

Math.min()

Returns the minimum of a sequence of numbers passed in

Math.max(1.2.3.4.5) / / 1
Math.max(1) / / 1
Copy the code

Math.pow()

Returns the second power of the first argument

Math.pow(1.2) / / 1
Math.pow(2.1) / / 2
Math.pow(2.2) / / 4
Math.pow(2.4) / / 16
Copy the code

Math.random()

Returns a pseudo-random value between 0.0 and 1.0

Math.random() / / 0.9318168241227056
Math.random() / / 0.35268950194094395
Copy the code

Math.round()

rounded

Math.round(1.2) / / 1
Math.round(1.6) / / 2
Copy the code

Math.sin()

Calculate the sine of an Angle in radians

Math.sin(0) / / 0
Math.sin(Math.PI) / / 1.2246467991473532 e-16)
Copy the code

Math.sqrt()

prescribing

Math.sqrt(4) / / 2
Math.sqrt(16) / / 4
Math.sqrt(5) / / 2.23606797749979
Copy the code

Math.tan()

Calculate the tangent of an Angle in radians

Math.tan(0) / / 0
Math.tan(Math.PI) / / 1.2246467991473532 e-16
Copy the code

ES module

The ES module is the ECMAScript standard for handling modules. Node.js has been using CommonJS for a long time, but browsers don’t yet have a modular system. Every major decision (such as a module system) must first be standardized by ECMAScript and then implemented by the browser.

This standardization process was completed in ES6, while browsers began to gradually implement the standard in an effort to keep things consistent. The ES module is now supported by Chrome, Safari, Edge, and Firefox (starting with version 60).

Modules are cool, they allow you to encapsulate arbitrary functionality and then expose it as a library to other JavaScript files.

ES module syntax

Importing a module can be done with:

import package from 'module-name'
Copy the code

However CommonJS uses:

const package = require('module-name')
Copy the code

A module is a JavaScript file that exports one or more values (objects, functions, or variables) via export. For example, this module exports a function for uppercase strings:

uppercase.js

export default str => str.toUpperCase()
Copy the code

In this example, the module defines a unique default export, so it can be an anonymous function. Otherwise it needs a name to distinguish other exports. Now, any other JavaScript module can import this function by importing uppercase.js.

An HTML page can add a module via the special type=”module” attribute on the

<script type="module" src="index.js"></script>
Copy the code

Note: The module import behavior is similar to the defer script load. See efficient loading of JavaScript using latency and asynchrony.

It is important that any module with type=”module” is loaded in strict mode.

In this case, the uppercase.js module defines a default export, so we can import the module and give it a name we like:

import toUpperCase from './uppercase.js'
Copy the code

Then we can use it:

toUpperCase('test') //'TEST'
Copy the code

You can also import modules using absolute paths to reference modules defined in other domains:

import toUpperCase from 'https://flavio-es-modules-example.glitch.me/uppercase.js'
Copy the code

This syntax is also valid:

import { foo } from '/uppercase.js'
import { foo } from '.. /uppercase.js'
Copy the code

This doesn’t work:

import { foo } from 'uppercase.js'
import { foo } from 'utils/uppercase.js'
Copy the code

The path must be absolute or preceded by a./ or /.

Other import/export methods

We already know this example:

export default str => str.toUpperCase()
Copy the code

This creates a default export. However, you can export multiple contents in a single file, such as:

const a = 1
const b = 2
const c = 3

export { a, b, c }
Copy the code

Another module can import all:

import * from 'module'
Copy the code

You can choose to import only parts of the content, using deconstructed binding:

import { a } from 'module'
import { a, b } from 'module'
Copy the code

You can also import the default export, and then import content that doesn’t have a default export by name, like the common React import:

import React, { Component } from 'react'
Copy the code

See an example of the ES module here.

Cross-domain resource sharing CORS

Getting modules via CORS means that if you import scripts from other domains, they must have a valid CORS header to Allow loading across web pages (like Access-Control-Allow-Origin).

What about browsers that don’t support modules?

Use type=module and nomodule together:

<script type="module" src="module.js"></script>
<script nomodule src="fallback.js"></script>
Copy the code

conclusion

The ES module is the most important feature introduced in modern browsers. They are part of ES6, but the process of implementing them is slow.

Now we can use them! But it’s also important to know that multiple modules can affect page performance, because this is a step the browser must take.

Webpack will play an important role even if the ES module is already in the browser, but building such functionality directly in the language is a heavy task to unify the way modules work on the client and node.js.

CommonJS

The CommonJS module specification is the module standard in Node.js.

Browser-side JavaScript uses ES module.

They allow you to create easy-to-split and reusable snippets of code, each of which can be tested independently.

The vast NPM ecosystem is built on the CommonJS format.

The syntax for importing modules is as follows:

const package = require('module-name')
Copy the code

In CommonJS, modules load synchronously and are executed in the order found at runtime. The system was originally built for server-side JavaScript and was not compatible with clients (which is why the ES module was introduced).

JavaScript files are modules that export one or more symbols defined within them, which can be variables, functions, or objects:

uppercase.js

exports.uppercase = str= > str.toUpperCase()
Copy the code

Any JavaScript file can be imported and used with this module:

const uppercaseModule = require('uppercase.js')
uppercaseModule.uppercase('test')
Copy the code

A simple example on Glitch.

You can export multiple values:

exports.a = 1
exports.b = 2
exports.c = 3
Copy the code

Import each value individually by destructing assignment:

const { a, b, c } = require('./uppercase.js')
Copy the code

Or just export one value:

//file.js
module.exports = value
Copy the code

Then import:

const value = require('./file.js')
Copy the code

The vocabulary

Finally, some front-end development terms may be different from what you understand.

asynchronous

Asynchronous code is when you start something, you forget about it, you don’t have to wait, you get it automatically when it’s ready. The typical example is an AJAX call, which can take a lot of seconds while you’re doing other things, and when you get the response, the callback is called. Promises and Async /await are more modern ways to deal with asynchronous code.

Block-level scope

Any variables defined within a block in a block-level scope are visible to the whole block and can be accessed internally, not externally.

The callback

A callback is a function that is called when an event occurs. The click event for the element binding is called when the user clicks on the element. The callback of a FETCH request is invoked after the resource has been downloaded.

The statement

The declarative method is you specify the details, you tell the machine what you need to do. React is considered declarative because it focuses more on abstract reasoning than directly editing the DOM. Each high-level programming language is more declarative than a low-level programming language such as Assembler. JavaScript is more declarative than C, and HTML is declarative.

The fallback

Fallbacks are used to provide a better experience when a user cannot access a particular feature. For example, if JavaScript is turned off in the browser, the user should get a native version of the HTML page. Or when you encounter an API that the browser doesn’t implement, you should have a reliable way to avoid severely impacting the user experience.

Function scope

Any variable defined within the scope of a function is visible to the whole function and can be accessed inside the function, not outside it.

consistency

A variable whose value cannot be changed after its creation is said to be immutable. Mutable variables are mutable. This is the same for arrays and objects.

Vocabulary range

A lexical scope is a specific scope in which the variables of the parent function can be used in the inner function. The scope of the inner function also includes the scope of the parent function.

Polyfill

Polyfill is designed to provide older browsers with new features that modern browsers don’t have native support for. Polyfill is a special shim.

Pure functions

A pure function is a function that has no side effects (does not modify external resources) and whose output is dictated by its parameters. You can call a function a million times, each time with the same arguments, and the output is always the same.

To assign a value

Var and let allow you to reassign variables an infinite number of times. Const declares an immutable string, integer, Boolean, object (but you can still modify it by providing methods).

The scope of

A range is the part of a set of variables that are visible to the program.

scope

A scope is a set of rules defined in a programming language that determine the value of a variable.

Shim

Shim includes a number of features or apis. It is often used to abstract content, pre-populate parameters, or add polyfills for browsers that do not support certain features. You can think of it as a compatibility layer.

Side effects

A side effect is that a function interacts with other functions or objects. Interacting with the network, file system, or UI has side effects.

State

When we talk about components, we have to talk about states. A component manages data stateful, otherwise stateless.

Stateful

A stateful component, function, or class manages its own state (data). It can store an array, a timer or something else.

Stateless

A stateless component, function or class is also called dumb because it cannot make decisions using its own data, so its output or presentation is complete based on its parameters. That means pure functions are stateless.

Strict mode

Strict mode is a new feature in ECMAScipt 5.1 that causes JavaScript runtime to catch more errors, but it can help you improve your JavaScript code by rejecting undeclared variables and conflicting object attributes and other easily overlooked issues. Suggestion: Use strict mode. Another “sloppy pattern” is a bad thing by name.

Tree Shaking

Tree Shaking means removing “dead code” from the code you package and send to users. If you add code to important statements that will never be used, it will not be sent to your application users, reducing file size and load time.

Thanks for reading!

Note: You can get the Markdown, ePub, Mobi (extract code: R4i3) versions of this complete JavaScript manual for better reading.