This series of articles is for front-end developers who want to learn systematic, advanced JavaScript, or literacy about TESST

What is a JavaScript

The birth of

JavaScript was born to solve the problem of form validation. At the time, users were using 28.8 bit/s modems as web pages grew larger and more complex. At this time, when users submit A form, they need to exchange A lot of data with the server. In such A slow network situation at that time, after 30 seconds, A message pops up telling you that field A is mandatory, which is not very frustrating. So Netscape wanted to develop a client-side scripting language to handle this simple scripting in the browser.

So it was left to Brendan Eich, who developed it in two weeks as LiveScript and later changed the name to JavaScript to ride the Java hype.

When Netscape released JavaScript 1.0, which was successful, Microsoft came out of the blue and added JScript to Internet Explorer 3. The inclusion of Microsoft meant that there were two versions of JavaScript implemented at a time when there were no standards regulating the syntax or features of JavaScript. The coexistence of the two versions exacerbated the problem, and standardization emerged.

JavaScript 1.1 was submitted to Ecma as a proposal, and TC39 took on the standardization work. It took months to build ECMA-262, the ECMAScript language standard, and since then, All browsers use ECMAScript as the basis for their JavaScript implementation.

JavaScript && ECMAScript

Ecma-262 defines a language’s syntax, types, statements, keywords, reserved words, operators, and global objects. ECMAScript (ES) is a (pseudo) language based on the ECMA-262 definition that exists as a benchmark definition on which to build more robust scripting languages, such as browser-hosted JavaScript, Node.js is the server-side JavaScript hosting environment. The host environment provides the baseline implementation of ECMAScript and the extensions necessary to interact with the environment itself, such as DOM, which use ECMAScript’s core types and syntax to provide additional functionality specific to the environment. A complete JavaScript implementation consists of the following three parts:

  • Core (ECMAScript)
  • Document Object Model (DOM)
  • Browser Object Model (BOM)

HTML 中的 JavaScript

The introduction of JavaScript

Introduce JavaScript by embedding script tags in HTML

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Document</title>
</head>
<body>
  <! -- JavaScript script -->
  <script></script>
</body>
</html>
Copy the code

The Script tag can be placed within the head tag and the body tag, but it is generally a best practice to place the script tag at the end of the body tag for better performance and user experience on the page.

HTML is parsed sequentially from top to bottom, and putting the script tag inside the head tag means that all the JavaScript code must be downloaded, parsed, and executed before the page can begin rendering (which begins when the page is parsed to the body tag). This results in significant delays in page rendering, and a lot of white screen time in the browser window. And if there is code for manipulating the DOM in JavaScript, the DOM does not yet exist, so the JavaScript code will directly report an error.

Therefore, it is best practice to place the script tag at the end of the body element. This way the page is completely rendered before the JavaScript code is processed, the white screen time is reduced, and the page loads faster.

Inline code and external files

<! -- Line code -->
<script>
  function example() {
    console.log('In-line Code Sample')
  }
  example()
</script>
<! -- External file -->
<script src="./example.js"></script>
Copy the code

There are two ways to introduce scripts in HTML: inline code and external scripts. Inline code is to directly embed JavaScript code in HTML files, external files are to unify all JavaScript code into a single JS file, and then imported through script tags. The best practice is external scripting for the following reasons:

  • Maintainability. If JavaScript is scattered over many HTML pages, the pages will be flooded with JavaScript code, which will cause maintenance difficulties. It’s easier to write and maintain JavaScript in a separate directory, and it’s easier to develop HTML and JavaScript in parallel.
  • Caching. HTML pages typically use negotiated caching, while JavaScript is better suited for mandatory caching. The browser caches all externally linked JavaScript files based on specific Settings, which means faster page loading.
  • For the future, the syntax for including external JavaScript files is the same in HTML as in XHTML, by putting JavaScript in external files without considering XHTML’s comment hacks.

Asynchronous script

Asynchronous scripts are effective only for external files. They are classified into deferred scripts, asynchronous scripts, and dynamic loading scripts

Defer executing the script – defer

This is achieved by setting the defer property on the Script tag, which means that the scripts are downloaded asynchronously but are deferred to sequential execution until the page has been parsed (before the DOMContentLoad event). In practice, however, sequential execution and execution point in time are not guaranteed, so the page ends up with just one script with the defer attribute

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Document</title>
  <script defer src="./example.js"></script>
</head>
<body>
</body>
</html>
Copy the code

Execute scripts asynchronously – async

The async attribute implementation is set on the script tag to tell the browser to asynchronously download the script and execute it immediately after the script is downloaded, which will block THE HTML rendering. Therefore, async cannot guarantee the execution order of the script, and DOM modification cannot be performed in the script during initialization.

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Document</title>
  <script async src="./example1.js"></script>
  <script async src="./example2.js"></script>
</head>
<body>
</body>
</html>
Copy the code

The two examples above put the script tag inside the head tag without affecting page performance, and the script download and page rendering can be done in parallel (asynchronously). Examples are just to illustrate the phenomenon; don’t forget best practices.

Dynamically loading scripts

Dynamic loading of scripts is realized through JavaScript DOM API. Load the specified script by dynamically adding a script tag to the DOM as follows:

const script = document.createElement('script')
script.src = './example.js'
document.head.appendChild(script)
Copy the code

Only when this JavaScript code is executed will a dynamic request be sent to fetch and load the example.js file. Script elements created in this way are loaded asynchronously, adding an async property. Preloading can be configured for better performance.

<link rel="preload" href="./example.js">
Copy the code

< noscript > element

An elegant degradation solution for pages that do not support JavaScript, displaying the contents of NoScript when the browser does not support scripting or script support is turned off

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Document</title>
</head>
<body>
  <noscript>
    <p>Browsers do not support JavaScript scripts</p>
  </noscript>
</body>
</html>
Copy the code

Language foundation

grammar

Identifiers are case sensitive

// Identifiers are case sensitive
const t1 = 't1', T1 = 'T1';
Copy the code

Identifiers refer to variable, function, attribute, and function parameter names. Identifiers can consist of one or more of the following characters:

  • The first character must be a letter, underscore (_), or dollar sign ($)
  • The remaining characters can be letters, numbers, underscores, and dollar signs

Best practice: Use small camel case for identifiers, with the first letter of the first word in lower case and the first letter of each subsequent word in upper case, such as testVariable

annotation

// Single-line comment
/** * multiline comments */
Copy the code

Strict mode

ECMAScript adds the concept of strict mode, in which non-canonical versions of ECMAScript 3 are handled automatically and errors are generated for unsafe parts.

Add use Strict to the beginning of the file to enable strict mode for the entire script

"use strict";
Copy the code

Add use strict to the beginning of the function to enable strict mode for this function

function fn() {
	"use strict"
	console.log('test')}Copy the code

“Use strict” is a preprocessor directive that any supported JavaScript engine that sees will switch to strict mode.

statements

Semicolons at the end of statements are optional. No parser will try to add semicolons in the right place, so extra points can improve performance, but now all the packaging tools can do this, so it doesn’t matter whether they are added or not, just the team’s coding style.

let test = 'test';
if (test) {
  test = false;
  console.log(test);
}
Copy the code

Keywords, reserved words

Ecma-262 describes a set of keywords and reserved words that should not be used as identifiers.

  • Key words:
break       do          in            typeof
case        else        instanceof    var
catch       export      new           void
class       extends     return        while
const       finally     super         with
continue    for         switch        yield
debugger    function    this
default     if          throw
delete      import      try
Copy the code
  • Reserved words:
Always Reserved: enum Reserved in strict mode: implements Package public interface Protected static let private Module code Reserved: awaitCopy the code

variable

ECMAScript variables are loosely typed, and each variable can be used to hold any type of data. Variables are declared with three keywords: var, const, and let

The var keyword

Using var, you can declare global variables and local variables. The global variable exists as an attribute of the window object. Using var in a function allows you to declare a local variable that is destroyed at the end of the function execution. Declaring variables in non-strict mode without the var keyword will declare a global variable and is not recommended. Errors are reported in strict mode

var t1 = 't1';
// Legal but not recommended
t2 = 't2';
function fn() {
  var t3 = 't3';
  // Legal but not recommended
  t4 = 't4'
}
Copy the code

Using the var keyword to declare variables has a declaration boost, and the JavaScript compiler pulls all declarations to the top of the scope

function fn() {
  console.log(test)
  var test = 'test'
}
// The above functions will be compiled by the compiler
function fn() {
  var test;
  / / output is undefined
  console.log(test)
  test = 'test'
}
Copy the code

Let the keyword

Let and VAR work similarly, but there are some very important differences:

  • The scope of the LET declaration is block-level scope, while the scope of the var declaration is function scope

    // var
    if (true) {
      var t1 = 't1'
      console.log(t1)	// t1
    }
    console.log(t1)	// t1
    
    // let
    if (true) {
      let t2 = 't2'
      console.log(t2)	// t2
    }
    console.log(t2)	// ReferenceError: t2 is not defined
    Copy the code
  • Let declared variables do not have the problem of declaring promotion (temporary dead zone)

    // var
    console.log(t1)	// undefined
    var t1 = 't1'
    
    // let
    console.log(t2)	// ReferenceError: t2 is not defined
    let t2 = 't2'
    Copy the code
  • Let is not allowed to be declared twice in the same block-level scope. Var overwrites the previous one

    // var
    var t1 = 't1';
    var t1 = 'tt1';
    console.log(t1)	// tt1
    
    // let
    let t2 = 't2'
    // SyntaxError: Identifier 't2' has already been declared
    let t2 = 'tt2'
    
    // var + let
    var t3 = 't3'
    // SyntaxError: Identifier 't3' has already been declared
    let t3 = 'tt3'
    Copy the code
  • Global variables declared by let do not become properties of the window object; var does

    // var
    var t1 = 't1'
    console.log(window.t1)	// t1
    
    // let
    let t2 = 't2'
    console.log(window.t2)	// undefined
    Copy the code
  • For loop, var declares variables outside the loop body, let is limited to the inside of the for loop block

    // var
    for (var i = 0; i < 5; i++) {
      setTimeout(() = > {
        // After 2 seconds, the output is the value of the variable I that permeates the loop, where I = 5
        console.log(i)
      }, 2000)}console.log(i)	/ / 5
    // Output 5 5 after 2 seconds
    
    // let
    for (let j = 0; j < 5; j++) {
      // The JavaScript engine declares a new iteration variable behind the scenes for each iteration loop, so each setTimeout references a different instance of the variable, so console.log outputs the desired value after 2 seconds. This behavior applies to all for loops, For example, for-in and for-of
      setTimeout(() = > {  
        console.log(j)
      }, 2000)}console.log(j)	// ReferenceError: j is not defined
    Output 0, 1, 2, 3, 4 after 2 seconds
    Copy the code

The const keyword

Const behaves in much the same way as let. The only important difference is that a variable declared with const must be initialized at the same time and cannot be changed again.

const t1 = 't1'
// TypeError: Assignment to constant variable
t1 = 't2'
Copy the code

This is fine, because you’re not modifying obj itself, which is a memory address, and you’re modifying the object in that memory address

const obj = {
  t: 'tt'
}
obj.t = 'ttt'
obj.tt = 'tttt'
Copy the code

The JavaScript engine creates separate instances of variables for let declarations in the for loop. Although const variables are similar to let variables, we cannot use const to declare an iteration variable because the iteration variable incrementally

// TypeError: Assignment to constant variable.
for (const i = 0; i < 5; i++) {}
Copy the code

Best practices

The advent of const and let solves all of the quirks of var, so the best practice is to not use var, const first, let second.

The data type

ECMAScript has six simple data types (primitives) : Undefined, Null, Boolean, Number, String, and Symbol. Another complex data type is called Object. Object is an unordered collection of name-value pairs.

Undefined type

Undefined has only one value – Undefined. When declaring a variable without assigning a value, the value of the variable is Undefined by default.

let message;
console.log(message)	// undefined
console.log(message === undefined)	true
Copy the code

Null type

The Null type also has only one value – Null. Logically, Null represents a pointer to an empty object, which is why passing a Null to typeof returns “Object”

const t = null
console.log(typeof t)	// object
Copy the code

To improve the semantics of your code, it is recommended to initialize an empty object with NULL, so that when someone looks at your code they will know that the value of the variable is a reference. Undefined is derived from null because ECMA-262 defines them as ostensibly equal.

console.log(undefined= =null)	// true
Copy the code

Boolean type

Boolean types have two literals: true and false. Although there are only two Booleans, any other type of value can be converted to a Boolean using the Boolean() transformation function.

const t = 'test'
console.log(Boolean(t))	// true
Copy the code
The data type The value converted to true Convert to a value of false
Undefined There is no Undefined
Null There is no Null
Boolean true false
Number Non-zero value 0, NaN
String Non-empty string “” Empty string
Symbol Any value There is no
Object Not null values null

It is important to understand the conversions above, because flow control statements like if automatically convert values of other types to Booleans (implicit type conversions). So the best practice is to use the ===(third class) operator to improve code readability and avoid implicit type conversions.

The Number type

The Number type uses IEEE 754 format to represent integer and floating point values. Different numeric types have different numeric literal formats:

  • Decimal, const t1 = 20, t2 = 30.1

  • Octal, the first digit must be 0 followed by the corresponding octal number (0-7). If the literal contains more than 7, the prefix 0 is ignored and the following digit is treated as a decimal number. Octal numbers are invalid in strict mode, causing the JavaScript engine to throw a syntax error

    const t1 = 070	// octal 56
    console.log(t1)	/ / 56
    const t2 = 079	// An invalid octal number, as a decimal 79
    console.log(t2)	/ / 79
    Copy the code
  • Hexadecimal, the number must be prefixed with 0x followed by A hexadecimal number (0-9, A-F [case insensitive])

    const t1 = 0xA
    console.log(t1)	/ / 10
    const t2 = 0x1f
    console.log(t2)	/ / 31
    Copy the code

Floating point value

A floating-point value must contain a decimal point and be followed by at least one digit. The number before the decimal point, although omitted, is not recommended

const t1 = 1.1, t2 = 0.1, t3 = 2.
console.log(t3)	/ / 0.2
Copy the code

Floating-point values take up twice as much memory as integers, so ECMAScript always tries to convert values to integer storage. For example, if there is no number behind the decimal point (1.), or if the value itself is an integer but is followed by 0 (1.0), both cases are converted to integers

const t1 = 1.
console.log(t1)	/ / 1
const t2 = 11.0
console.log(t2)	/ / 11
const t3 = 12.1
console.log(t3)	/ / 12.1
Copy the code

For very large or very small values, it is more concise to use scientific notation

const t1 = 3.125 e7	// Equivalent to 3.125 * 10^7
console.log(t1)	/ / 31250000
Copy the code

Floating-point values can be as accurate as 17 decimal places, but are far less accurate in arithmetic than integers. For example, 0.1 + 0.2 gives a value of 0.300 000 000 000 000 04 instead of 0.3. Because of this smiling rounding error, it is difficult to test a particular floating-point value, so you never test a particular floating-point value. This rounding error is caused by the use of IEEE 754 numbers and is not unique to ECMAScript, as it is found in any language that uses IEEE 754 format

console.log(0.1 + 0.2= = =0.3)	// false
console.log(0.15 + 0.15= = =0.3)	// true
Copy the code

The range of values

Due to memory limitations, there is a range of values that ECMAScript can represent. The minimum value is number.min_value (5E-324) and the maximum value is number.max_value (1.797 693 134 862 315 7E308). If a calculated value is out of range, it is automatically converted to plus or minus Infinity, and the Infinity value cannot be evaluated further because it has no numerical representation available. To determine whether a value is infinite, use the isFinite() function. NEGATIVE_INFINITY and POSITIVE_INFINITY can be used to get plus or minus Infinity.

NaN

NaN means “Not a Number” and is used to indicate that an operation that should have returned a Number failed (rather than throwing an error directly)

console.log(-0 / +0)	// NaN
console.log(0 / 0)	// NaN
console.log(5 / 'test')	// NaN
Copy the code

Any operation that involves NaN always returns NaN, such as NaN / 10; NaN is not equal to any value including NaN. The isNaN function is used to determine whether a given parameter is a NaN, and the parameter is automatically converted to a value during the determination (implicit type conversion).

console.log(NaN= = =NaN)	// false
console.log(isNaN(NaN))	// true
console.log(isNaN(10))	// false, 10 is a number
console.log(isNaN('10'))	// false, '10' can be converted to the number 10
console.log(isNaN('blue'))	// true, 'blue' cannot be converted to a valid value
Copy the code

Numerical transformation

There are three functions that convert non-numeric values to numeric values: Number, parseInt, and parseFloat. Number is a transformation function that can be used for any type of data. The latter two functions are mainly used to convert strings to numbers.

The conversion rules for the Number function are as follows:

  • Boolean if true is 1 and false is 0
  • Value, return directly
  • Null returns 0
  • Undefined, return NaN
  • String, apply the following rules
    • If the string contains only a valid decimal number, it is converted to a valid decimal value, ignoring the first 0 of the valid value
    • If the string contains only valid hexadecimal numbers, convert them to valid decimal values
    • An empty string returns 0
    • All other cases return NaN
  • Object, calling the valueOf method, and then converting the returned value according to the above rules; If the conversion result is NaN, the toString method is called on the conversion result, applying the value returned by the above rule conversion
  • TypeError: Cannot convert a Symbol value to a number
// Boolean
console.log(Number(true))	/ / 1
console.log(Number(false))	/ / 0
// Number
console.log(Number(+9))	/ / 9
console.log(Number(-09)) / / - 9
// null
console.log(Number(null)) / / 0
// undefined
console.log(Number(undefined)) // NaN
/ / string
console.log(Number('123'))	/ / 123
console.log(Number('0x1A'))	/ / 26
console.log(Number(' '))	/ / 0
/ / object
const obj = { t: 'tt' }
console.log(Number(obj))	// NaN
// Symbol
console.log(Number(Symbol(1)))	// TypeError: Cannot convert a Symbol value to a number

Copy the code

Although it is rare to actively use Number to convert data types in normal programming, there are many implicit conversions, so it is necessary to fully understand the conversion function. Given the complexity of Number, you can usually use parseInt when you need to get an integer. ParseInt is more focused on whether the string contains a numeric pattern. ParseInt converts from the first non-null character, and returns NaN immediately if the first character is not a valid decimal, octal, or hexadecimal. If it is a valid character, each successive character is checked until the end of the string or the end of the first non-valid character.

console.log(parseInt(' -1234test'))	/ / 1234
console.log(parseInt('test124'))	// NaN
console.log(parseInt(' '))	// NaN
console.log(parseInt('0xA'))	/ / 10
console.log(parseInt('10.5'))	/ / 10
console.log(parseInt('077'))	/ / 63
Copy the code

ParseInt also supports the second argument, which displays the base number of the first argument

console.log(parseInt('10.5'.2))	// 2, the first argument is parsed as a binary number
Copy the code

The parseFloat function works in a similar way to parseInt, but more simply. ParseFloat can only parse decimal numbers, takes no second argument, and ends up on the first invalid floating-point character

console.log(parseFloat('- 000.1124.2'))	// -0.1124
Copy the code

Type String

String data types can be represented by double quotation marks (“”), single quotation marks (“”), or backquotation marks (“), unlike in some languages (such as PHP) where different quotation marks change the way strings are interpreted. However, it is best to use the same style across teams, such as single quotes.

immutability

Strings in ECMAScript are immutable. This means that once created, their values cannot be changed. To change a string value in a variable, you must first destroy the original string and then assign a new string to the variable, pointing to a changed memory address.

let lang = 'Java'
lang = lang + 'Script'
Copy the code

The whole process here is to allocate a space that can hold 10 characters, then fill it with the ‘Java’ and ‘Script’ strings, then destroy the original ‘Java’ and ‘Script’ strings, and point lang to the newly allocated space. All the processing happens in the background, which is why some early browsers are very slow at concatenating strings. Later versions of these browsers addressed this problem.

Conversion string

There are four ways to convert a value to a String: the toString method, the String conversion function, the plus (+) operator, and template literals

All data types except null and undefined have the toString method, which returns the current value as a string. If the converted value is of a numeric type, you can also pass toString an argument indicating what base (base) to output as a string.

var str = '12', num = 12
console.log(str.toString())	/ / '12'
console.log(str.toString(2))	// '12', the argument is ignored and valid only if the converted value is numeric
console.log(num.toString())	/ / '12'
console.log(num.toString(2))	/ / '1100'
Copy the code

If you are not sure whether a value is null or undefined, you can use the String function, which returns the corresponding String as follows:

  • If the value has a toString method, that method is called and the result is returned
  • Otherwise return null and undefined as strings, ‘null’ and ‘undefined’
console.log(String(10))	/ / '10'
console.log(String(true))	// 'true'
console.log(String(null))	// 'null'
console.log(String(undefined))	// 'undefined'
Copy the code

A value with a null character (“”) can also be converted to a string

console.log(12 + ' ')	/ / '12'
Copy the code

A value can also be converted to a string by means of a literal

const num = 12
const str = `${num}`
console.log(typeof num)	// number
console.log(typeof str)	// string
Copy the code

Template literals (template strings)

ECMAScript 6 adds template strings. Instead of using single and double quotation marks to define strings, template strings can keep newlines and Spaces, which is useful when defining templates.

The first line of the file is empty and the content begins on line 2, so 
      
should start after the backquotes
const pageHTML = ` ` console.log(pageHTML) Copy the code

Technically, a template string is not a string, but an immediate JavaScript expression that evaluates to a string. The template string inserts any JavaScript expression into the string via ${}, which is evaluated at the nearest scope and converted to a string by calling the toString method.

const a = 1, b = 2, c = 3;
const expStr = 'Addition operation:${a} + ${b} = ${c}`
console.log(expStr)	// add: 1 + 2 = 3
Copy the code

Template strings also support defining tag functions that allow you to customize the behavior of template strings. The tag function takes an array of templates split by the interpolation notation (${exp}) (string.prototype.split) and evaluates each expression

function tagFunction(templateArray, aExp, bExp, cExp) {
  // Split the template string with interpolation notation (${})
  console.log(templateArray)	// ["", "+ "," = ", "]
  // The result of evaluating each expression
  console.log(aExp)	/ / 1
  console.log(bExp)	/ / 2
  console.log(cExp)	/ / 3
  return 'custom result'
}
const a = 1, b = 2, c = 3;
const result = tagFunction`${a} + ${b} = ${c}`
console.log(result)	// custom result
Copy the code

Raw tag functions can be used to retrieve raw strings (such as newlines or Unicode characters) rather than the converted character representation

// Outputs the newline character \n itself
console.log(`\n`)	// Outputs a newline
console.log(`\\n`)	// You need to use the translation symbol to print \n
console.log(String.raw`\n`)	// \n
Copy the code

Symbol type

The Symbol type is a new primitive data type added to ECMAScript 6. It is unique and immutable, such as being used as an object property to ensure that existing properties are not overwritten.

Basic usage

Symbol has no literal syntax and can only be used to initialize a Symbol value using the Symbol() function

const sym = Symbol(a)console.log(typeof sym)	// symbol
console.log(sym)	// Symbol()
const fooSym = Symbol('foo')
console.log(fooSym)	// Symbol(foo)
const bar1Sym = Symbol('bar'), bar2Sym = Symbol('bar')
console.log(bar1Sym === bar2Sym)	// false to prove that no existing attributes are overwritten
// as an object attribute
const obj = {}
obj[bar1Sym] = 'test1'
obj[bar2Sym] = 'test2'
console.log(obj)	// {Symbol(bar): "test1", Symbol(bar): "test2"}
Copy the code

Global symbol registry

If an instance of a Symbol needs to be shared and reused in multiple places, you can use a string as the key to create a Symbol in the global Symbol registry via symbol.for (strParam).

Symbol.for() performs an idempotent operation on each string. When symbol.for is called with a string, the global registry is checked to see if the corresponding Symbol already exists, if not, a new Symbol instance is created and added to the global registry, and if it does, an instance of the existing Symbol is returned. This is different from a Symbol created through Symbol.

The Symbol. For argument must be a string. If a non-string type is passed, the toString method is called to convert it to a string by default.

Use the symbol.keyfor () method to query the string keyFor the specified Symbol in the global registry, or return undefined if it is not a global Symbol.

const fooGlobalSym = Symbol.for('foo')
const otherFooGlobalSym = Symbol.for('foo')
console.log(fooGlobalSym === otherFooGlobalSym)	// true to prove reuse of existing symbols
const fooSym = Symbol('foo')
console.log(fooSym === fooGlobalSym)	// false, even with the same string, Symbol and symbol.for create different symbols
console.log(Symbol.keyFor(fooGlobalSym))	// foo
console.log(Symbol.keyFor(fooSym))	// undefined
Copy the code

Use symbols as attributes

Wherever you can use strings or values as attributes, you can use symbols. Using symbols as object attributes is best done through variables, otherwise it will be difficult to use obj[key] indexes later.

  • Object. GetOwnPropertyNames () returns an Object instance all the general properties of the array
  • Object. GetOwnPropertySymbols (), return all the symbolic attribute array Object instance
  • Object. GetOwnPropertyDescriptors (), return to include both conventional and symbol of attribute descriptor Object
  • Reflect.ownkeys (), which returns two types of keys
const obj = { t1: 't1' }
const t2Sym = Symbol('t2')
obj[t2Sym] = 't2Sym'
console.log(obj)	// {t1: "t1", Symbol(t2): "t2Sym"}
console.log(Object.getOwnPropertyNames(obj))	// ['t1']
console.log(Object.getOwnPropertySymbols(obj))	// [Symbol(t2)]
console.log(Object.getOwnPropertyDescriptors(obj))	// A property descriptor object that contains both general and symbolic attributes
console.log(Reflect.ownKeys(obj))	// ['t1', Symbol(t2)]
Copy the code

Common built-in symbols

ECMAScript 6 introduces a number of commonly used built-in notations to expose the language’s internal behavior, which developers can directly access, rewrite, or emulate. This section will focus on the implementation of JavaScript API principles.

The Object type

An object in ECMAScript is a collection of data and functionality. Create an Object either as a new Object() or as an Object literal {}. Object is also the base class for deriving other objects. All properties and methods of Object also exist on derived objects. Each object instance has the following properties and methods:

  • Constructor: a function used to create an Object, new Object(). Object is the value of this constructor property
  • HasOwnProperty (propertyName): Checks whether the specified property exists on the specified object
  • IsPrototypeOf (obj): Determines whether the current object is a prototype object of OBj
  • PropertyIsEnumerable (propertyName): determines whether a given propertyIsEnumerable
  • ToLocaleString (): Returns a string representation of an object that is dependent on its local execution environment
  • ToString (): Returns a string representation of an object
  • ValueOf (): Returns the value, string, or Boolean of an object. It is usually the same as the return value of toString.
const obj = { a: 'aa' }
console.log(obj.constructor)	/ / the Object function
console.log(obj.hasOwnProperty('a'))	// true
console.log(Object.prototype.isPrototypeOf(obj))	// true
console.log(obj.propertyIsEnumerable('a'))	// true
console.log(obj.toLocaleString())	// [object Object]
console.log(obj.toString())	// [object Object]
console.log(obj.valueOf())	// {a: "aa"}
Copy the code

The operator

Ecma-262 describes a set of operators that can be used to manipulate any type of numeric value, including mathematical operators (addition, subtraction, multiplication, division), bitwise operators, relational operators, and so on.

Unary operator

Unary operators include increment (++), decrement (–), positive (+), and negative (-).

Autoincrement and autodecrement are prefixed and postfix languages. The difference is that the operation takes place at different times. The prefixed version evaluates the statement before the postfix version evaluates the statement before the autoincrement (autodecrement) operation.

If non-numeric types are involved in the calculation process, the data should be converted to numeric types through the Number transformation function before calculation.

let age = 10, num1 = 2, num3 = 3;
/ / the prefix version
console.log(++age)	// 11
console.log(--age)	// 10, autodecrement before output
console.log(++num1 + num3)	/ / 6
console.log(--num1 + num3)	/ / 5
/ / the suffix
console.log(age++)	// 10, output first and then add
console.log(age--)	// 11, output and then decrement
console.log(age)	/ / 10
console.log(num1++ + num3)	/ / 5
console.log(num1-- + num3)	/ / 6
console.log(num1)	/ / 2
// add, subtract
let test = 25
console.log(+test)	/ / 25
console.log(-test)	/ / to 25
/ / the value
let str1 = "11", str2 = "a", bVal = false, fVal = 1.1, obj = { valueOf() { return -1}}console.log(str1++ + 1)	// add ()
console.log(+str2)	// NaN
console.log(--bVal)	// -1
console.log(++fVal) / / 2.1
console.log(--obj)	/ / - 2
Copy the code

An operator

Bitwise operators are used for low-level manipulation of numbers, directly manipulating bits of data in memory. All values in ECMAScript are stored in 64-bit format, but bit-operations do not directly manipulate 64-bit values. Instead, they convert values to 32-bit integers, then bit-operations, and then the result to 64-bit storage, all in the background. For developers, it’s like having only 32-bit integers.

Signed numbers use the first 31 bits of 32 for integer values, the 32nd bit for signed bits, 0 for positive and 1 for negative, and the 32nd bit cannot be accessed and manipulated when working with signed integers. Positive numbers are stored in the true binary format of numeric values, and negative numbers are stored in the binary complement (binary complement). The binary complement of a number is computed in three steps:

  • So let’s figure out the binary representation of the absolute value, say -18, let’s figure out the binary representation of 18
  • Computes the inverse (complement) of the numeric value, that is, each 0 becomes 1, and 1 becomes 0
  • Add 1 to the result

Take -18 as an example and start with the binary representation of 18:

0000 0000 0000 0000 0000 0001 0010

Then compute the inverse, that is, invert each bit

1111 1111 1111 1111 1111 1111 1110 1101

Finally, add one to the inverse to get the binary representation of -18

1111 1111 1111 1111 1111 1111 1110 1110

In ECMAScript, when we print a negative value as a binary string, we get an absolute value preceded by a negative sign, such as -18, which yields “-10010”. ECMAScript encapsulates all of this logic internally, rendering the results in a more logical form.

const num = -18;
console.log(num.toString(2))	/ / - 10010
Copy the code

All integers in ECMAScript are signed numbers. For unsigned integers, the 32nd bit does not represent signs because there are only positive numbers, so unsigned integers have a wider representation range than signed integers because sign bits are used to represent values. The switch from 64 bits to 32 bits to 64 bits resulted in the odd side effect that the special values NaN and Infinity were treated as zeros in on-bit operations. If a bitwise operation is applied to a non-numeric value, the background automatically converts the value to a numeric value using the Number transformation function before the bitwise operation.

Bitwise not, or, not, xor

With, or, not, doubt just remember the following formula

  • 1 only if both and & : are 1, and all others are 0
  • Or | : both returns 0 to 0, the other is 1
  • Non ~: 0 becomes 1,1 becomes 0
  • Different or ~: Different is 1, same is 0
const num = 18, num1 = 3;
console.log(num.toString(2))	/ / 10010
console.log(num1.toString(2))	/ / 00011
const and = num & num1
console.log(and, and.toString(2))	/ / 2, 00010
const or = num | num1
console.log(or, or.toString(2))	/ / 19, 10011
// The result of bitwise non is to invert the value and subtract 1
const not = ~num
console.log(not, not.toString(2)) // -19, -10011
const xor = num ^ num1
console.log(xor, xor.toString(2))	/ / 17, 10001
Copy the code

Shift to the left

Left-shift operators are represented by two less-than signs (<<), and all operators are moved to the left by the specified number of bits, with the left bits filled with zeros. A left shift preserves its sign bit.

// For each step to the left, multiply by 2
console.log(2 << 5)	/ / 64
console.log(-2 << 5)	/ / - 64
Copy the code

Signed numbers shift to the right

A signed number represented by two greater than signs (>>) moves all 32 bits of the value to the right of the specified number of digits, reserving the signed bit and filling the left bit with 0. There’s actually a sign shift to the right which is the inverse of a shift to the left.

// Divide by 2 for every step to the right
console.log(64 >> 5)	/ / 2
console.log(-64 >> 5)	/ / - 2
Copy the code

Unsigned numbers shift to the right

The unsigned right shift is represented by a single greater-than sign (>>>), which moves all 32 bits, including the sign bit, to the right. For positive numbers, an unsigned shift to the right is the same as a signed shift to the right, because the sign bit is 0, and if you move to the right, it makes no difference. For negative numbers, sometimes it’s very different, because negative numbers have a sign bit of 1.

Boolean operator

There are three Boolean operators: logical not, logical and, and logical or.

Logic is not

Logical non-operators use exclamation marks (!) Can be used for any value and always returns a Boolean value. The logic will first convert the operand to a Boolean value and then invert it. The conversion rules are as follows:

  • Return false if the operand is an object, a non-empty string, or a non-zero value (including Infinity)
  • Return true if operands are null, undefined, NaN
// false
console.log(!true)
console.log(! {})console.log(!'test')
console.log(!10)
console.log(!Infinity)
// true
console.log(!null)
console.log(!undefined)
console.log(!NaN)
Copy the code

Use two exclamation points (!!) at the same time You can also convert any value to a Boolean, equivalent to calling the Boolean() transformation function. The first exclamation mark always returns the Boolean value taken negative, and the second one returns the true Boolean value of the variable.

Logic is not

Logical and is represented by two ampersand signs (&&). Is a short-circuit operator that will never evaluate the second operand if the first operand determines the result. Returns true if only two values are true.

console.log(true && false)	// false
Copy the code

Logic and operators can be used for any operand, not just Boolean values. If an operand is not a Boolean and the result is not necessarily a Boolean, follow the following rules:

  • The second operand is returned if the first operand is true
  • If the first operand is null, undefined, NaN, the first operand is returned
console.log(true && {})	/ / {}
console.log({} && 'test')	// test
console.log(null && 'test') // null
Copy the code

Logic or

Logic or the operator with two pipe (| |) said. If any of the operands are true, the logic or operator follows: If any of the operands are true, the result is true. Similar to logic, if one of the operands is not a Boolean, the result does not necessarily return a Boolean. The rule is: if the first operand is true, the first operator is returned; otherwise, the second operand is returned.

console.log(true || 'test') // true
console.log({} || false)	/ / {}
console.log(false| | {})/ / {}
Copy the code

Multiplicative operator

ECMAScript defines three multiplicative operators: multiplication, division, and modulus. These operations are normal math operations as they are in Java and C, but ECMAScript includes some automatic type conversions for non-numeric values. This is done by calling the Number() function in the background.

Multiplication operator

The multiplication operator, represented by an asterisk (*), is used to compute the product of two numbers as follows:

  • If both operands are numeric, normal math is performed, and Infinity or -infinity is returned if the result is out of the representation range
  • If one of the operands is NaN or Infinity * 0, return NaN
  • Infinity * a non-zero value that returns Infinity or -infinity depending on the sign of the second operand
  • Infinity * Infinity, returns Infinity
  • If you have an operand that is not a value, you now call Number() in the background to convert it to a value, and then apply the above rules

The division operator

The division operator, represented by a slash (/), is used to compute the quotient of two operands as follows:

  • If both operands are numeric, normal math is performed, and Infinity or -infinity is returned if the result is out of the representation range
  • NaN is returned if any of the operators are NaN or Infinity/Infinity or 0/0
  • A nonzero finite value divided by 0 returns Infinity or -infinity based on the sign of the first operand
  • Infinity/any value, returns Infinity or -infinity based on the second operand
  • If an operand is not a value, the Nunber() transformation function is called in the background to convert it to a value before applying the above rules

The modulo operator

The modulo (mod) operator is represented by a percentage sign (%), such as:

console.log(26 % 5)	/ / 1
Copy the code

Like other multiplicative operators, the modulo operator has some special behavior for special values:

  • If both operands are numeric, the normal complement operation is performed
  • Infinity % finite value, finite value % 0, Infinity % Infinity, returns NaN
  • Finite value (1) % Infinity, return finite value (1)
  • 0 % Arbitrary value, return 0
  • If an operand is not a value, the Number function is called in the background to convert it to a value before applying the above rules

Exponential operator

ECMAScript 7 adds the exponential operator, represented by two asterisks (**), instead of math.pow ()

console.log(Math.pow(3.2))	/ / 9
console.log(3六四屠杀2)	/ / 9
let ret = 3
console.log(ret **= 2)	/ / 9
Copy the code

The additive operator

The additive operators, the addition and subtraction operators, are inherently the simplest operators. But in ECMAScript, these two operators have some special behavior. Like the multiplicative operator, the additive operator converts different data types behind the scenes. It’s just that the conversion rules aren’t that intuitive.

Addition operator

  • Both operands are numeric
    • Any value + NaN returns NaN
    • Infinity plus Infinity, returns Infinity
    • -infinity + (-infinity) returns -infinity
    • Infinity + (-infinity), returns NaN
    • +0 + (+0), returns +0
    • -0 + (+0), returns +0
    • Minus 0 plus minus 0, returns minus 0
  • If there is a string in the operand, string concatenation is performed
    • Both operands are strings, concatenated directly
    • If one of the operands is not a string, it is converted to a string and then concatenated
  • If any of the operands are objects, values, or booleans, their toString() method is called to get a string, and the string rules are applied. For undefined and null, the String() function is called to get ‘undefined’ and ‘null’.

The subtraction operator

  • Both operands are numeric
    • If any operand is NaN, NaN is returned
    • Infinity – Infinity, returns NaN
    • -infinity – (-infinity), returns NaN
    • Infinity – (-infinity), returns Infinity
    • -infinity – (Infinity), returns -infinity
    • Plus 0 minus (plus 0), returns plus 0
    • Plus 0 minus minus 0, returns minus 0
    • -0 – (-0) returns +0
  • If any of the operands are strings, booleans, null, or undefined, the Number() function is used behind the scenes to convert them to a Number, and then the math is performed according to the above rules
  • If any of the operands are objects, the valueOf() method is called to get its numeric representation. If there is no valueOf() method, the toString() method is called, the resulting string is converted to a number, and the above rules apply.

Relational operator

The relational operator is used to compare two values, including <, >, <=, >=, and is used mathematically, returning a Boolean value. As with other ECMAScript operators, type conversions and other behaviors occur when they are applied to different data types.

  • Both operands are numeric values, which can be compared normally
  • If operands are all strings, compare the encoding of the corresponding character in the string one by one, such as ‘a’ > ‘a’
  • If any of the operands is a value, the other operand is converted to a value and a numeric comparison is performed
  • If any of the operands are objects, the valueOf() method of the object is called; if there is no valueOf(), the toString() method is called and the results are compared according to the previous rules
console.log(23 > 3)	// true
console.log('23' > 3) // true
console.log('23' > '3') // false
// In special cases, any relational operator that involves comparing NaN returns false
console.log(NaN < 3)	// false
console.log(NaN> =3)	// false
Copy the code

Equality operator

ECMAScript provides two sets of equalization operators. The first set is equal and unequal (==,! =), they perform a cast before performing a comparison, which is notorious for the second group, congruence and total inequality (===,! ==), they do not perform type conversion before comparison, different types are directly considered as unequal.

Equals and does not equal

When performing the comparison, follow the following rules:

  • If any of the operands is a Boolean, it is converted to a numeric value and compared. False converts to 0 and true converts to 1
  • If one of the operands is a string and one is a value then convert the string to a value and compare
  • If one operand is an object and the other is not, the valueOf method of the object is called to get the original value and is compared according to the previous rules
  • If both operands are objects, they are compared to see if they refer to the same object. If so, they are considered equal and return true, otherwise false
  • Null and undefined are equal, and null and undefined cannot be converted to other types of values for comparison
  • If either operator is NaN, the equality operator returns false and the inequality operator returns true. But NaN == NaN, returns false
console.log(null= =undefined)	// true
console.log("NaN"= =NaN)	// false
console.log(5= =NaN)	// false
console.log(NaN= =NaN)	// false
console.log(NaN! =NaN) // true
console.log(false= =0) // true
console.log(true= =1)	// true
console.log(true= =2) // false
console.log(undefined= =0) // false
console.log(null= =0)	// false
console.log("5"= =5)	// true
Copy the code

Congruence and incongruence

Congruence (===) and total inequality (! ==) does not need to perform type conversion. If two operands are of different types, they are considered unequal. Congruence and total inequality are a best practice.

// Different data types, so not equal
console.log('55'= = =55) // false
console.log(null= = =undefined)	// false
Copy the code

Conditional operator

Conditional operators are also called ternary expressions. Simple if-else conditional operators can be replaced by simpler conditional operators.

// const variable = boolean_expression ? true_value : false_value;
const num1 = 1, num2 = 2;
const max = num1 > num2 ? num1 : num2;
console.log(max)	/ / 2
Copy the code

The assignment operator

Assignment operators are divided into simple assignment (=) and compound assignment (multiplicative, additive, bitwise operator followed by equal sign). Compound operators are shorthand and do not improve performance.

let num = 10;
// Simple assignment
num = num + 10;
// compound assignment
num += 10;
num -= 10;
num *= 10;
num /= 10;
num %= 10;
num <<= 2;
num >>= 2;
num >>>= 2;
Copy the code

Comma operator

The comma operator is usually used in variable name statements or function arguments, such as:

const num1 = 1, num2 = 2;
const test = (5.3.4.0)	// Test has a value of 0
function fn(param1, param2) {
  console.log('fn')}Copy the code

statements

Ecma-262 describes statements called flow control statements, where most of the syntax in ECMAScript is found. Statements typically use one or more keywords to accomplish a given task. Statements can be simple or complex. This can be as simple as telling a function or process to quit, or as responsible as listing a bunch of instructions to execute repeatedly.

  • if

  • do-while

  • while

  • for

  • For-in, a non-signed key for enumerated objects, including circular objects, usually used in conjunction with the hasOwnProperty method

  • For-of, iterating over the elements (values) of the iterable

  • For-await-for, creates a loop that facilitates both asynchronous and synchronous iterables. Like the await operator, this statement can only be used inside an async function

    /** * variable is the result of each iteration. If the iterable contains asynchronous variables, then variable is the Promise value. For await (variable of iterable) {statement} */
    async function* asyncGenerator() {
      var i = 0;
      while (i < 3) {
        yieldi++; }} (async function() {
      for await (num of asyncGenerator()) {
        console.log(num);
      }
    })();
    / / 0
    / / 1
    / / 2
    Copy the code
  • Label statements. Label statements are used to label statements, such as label: Statement. The label is consumed by the break, continue statements. A typical use of label statements is nested loops.

    // start is a tag
    start: for (let i = 0; i < 10; i++) {
      for (let j = 0; j < 10; j++) {
        if (i === 5 && j === 5) {
          // continue skips to the outer loop
        	continue start;
          // Break breaks the outer loop
          // break start;
      	}
      	console.log(i)
      }   
    }
    Copy the code
  • The break and continue statements are used for loop statements, with break indicating that the loop is broken and continue indicating that the loop is skipped

    for (let i = 0; i < 10; i++) {
      for (let j = 0; j < 10; j++) {
        if (i === 5 && j === 5) {
          // Skip this loop
          continue;
          // Out of the inner loop
          // break;
        }
        console.log(i)
      }
    }
    Copy the code
  • The with statement is used to set the scope of code to a specific object. It is convenient to set the scope of code to that object when repeated operations are performed on the same object

    // Do not use with
    const qs = location.search.substring(1);
    const hostName = location.hostname;
    console.log(qs, hostName)
    
    // with
    with(location) {
      const qs = search.substring(1)
      const hostName = hostname
      console.log(qs, hostName)
    }
    Copy the code

    While with can be a convenience, its performance is poor, so strictly mode disabled and use of the with statement is not recommended. The reason for its poor performance is that it extends the scope chain at run time, making it impossible for the compiler to optimize its performance at static compile time, so the compiler skips all performance optimizations when it encounters with the WITH statement. Why is with poor performance?

  • The switch statement uses the congruence operator when comparing each condition

function

Functions are a core component of any language because they can encapsulate statements, combine logic, and then execute anywhere, at any time. ECMAScript functions use the function keyword name, followed by a set of parameters, and then the function body

function fn(arg0, arg1, ... args) {
  console.log(arg0, arg1, ... args) } fn(1.2.3.4.5)	// 1, 2, 3, 4, 5
Copy the code

As a best practice, a function either returns a value or does not return a value. A function that returns a value only under certain conditions can cause some unexpected problems. Functions that do not specify a return statement return undefined by default

Function names cannot be eval or arguments in strict mode, nor can function arguments have the same name.