Common development knowledge points of ES6: Introduction

ES6 introduction

ES6, full name ECMAScript 6.02015.06 release.

let and const commands

let command

Let command to declare variables. Its usage is similar to var, the difference is that the variables declared by var are globally valid, and the variables declared by let are only valid in the code block in which it is located.

Use var to declare:

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10

Use closures to resolve:

var a = [];
for (var i = 0; i < 10; i++) {
    (function(i){
        a[i] = function () {
          console.log(i);
        };
    })(i); 
}
a[6](); // 6

Using let:

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6
  • let does not have variable promotion, and must be declared before use, otherwise an error is reported; var has variable promotion, and the output undefined is used before being declared.
  • Let has a temporary deadband, which is not available in the code block until the let command is used to declare a variable.
  • let does not allow duplicate declarations.

const command

const declares a read-only constant. Once declared, the value of a constant cannot be changed. You cannot declare no assignments only.

const a = 10;
a = 20; // Report errors

const b; // Report errors

const has the same scope as let.

if(true) {
  const num = 5;
}
console.log(num); // Report errors

const declares the object and the memory address of the constant object, so the object itself can be changed, but an error will be reported if the constant is reassigned.

const obj = {};
obj.a = 'a';

obj = {}; // Report errors

Block level scope and function scope

ES5 only has global scope and function scope, and there is no block level scope, which brings many unreasonable scenarios.

In the first scenario, inner variables may override outer variables.

var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
}

f(); // undefined

In the second scenario, the loop variables used to count are leaked to global variables.

var s = 'hello';

for (var i = 0; i < s.length; i++) {
  console.log(s[i]);
}

console.log(i); // 5

Block level scope of ES6

let actually adds a block level scope to JavaScript.

function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5
}

The emergence of block level scope actually makes the widely used anonymous immediate execution function expression (anonymous IIFE) unnecessary.

// IIFE Writing method
(function () {
  var tmp = ...;
  ...
}());

// Block level scope writing
{
  let tmp = ...;
  ...
}

ES5 specifies that functions can only be declared in the top-level scope and function scope, not at the block level scope.

// Situation 1
if (true) {
  function f() {}
}

// Situation two
try {
  function f() {}
} catch(e) {
  // ...
}

The above two function declarations are illegal according to ES5.

ES6 introduces a block level scope that explicitly allows functions to be declared in a block level scope. ES6 stipulates that in block level scope, function declaration statement behaves like let and cannot be referenced outside block level scope.

function f() { console.log('I am outside!'); }

(function () {
  if (false) {
    // Repeat function declaration once f
    function f() { console.log('I am inside!'); }
  }

  f();
}());

The above code runs in ES5 and gets "I am inside!" because the function f declared in if will be promoted to the function header. The actual running code is as follows.

// ES5 Environmental Science
function f() { console.log('I am outside!'); }

(function () {
  function f() { console.log('I am inside!'); }
  if (false) {
  }
  f();
}());

ES6 is totally different. In theory, it will get "I am outside!". Because functions declared within a block level scope are similar to let, there is no effect outside the scope. However, if you really run the above code in ES6 browser, it will report an error. Why?

// Browser ES6 Environmental Science
function f() { console.log('I am outside!'); }

(function () {
  if (false) {
    // Repeat function declaration once f
    function f() { console.log('I am inside!'); }
  }

  f();
}());
// Uncaught TypeError: f is not a function

The above code in the ES6 browser will report an error.

Originally, if you change the processing rules of the functions declared in the block level scope, it will obviously have a great impact on the old code. In order to reduce the incompatibilities, the implementation of browser can not abide by the above rules and have its own behavior.

  • Allows functions to be declared within a block level scope.
  • Function declarations are similar to var, that is, they are promoted to the head of the global scope or function scope.
  • At the same time, the function declaration is promoted to the head of the block level scope.

Note that the above three rules are only valid for the browser implementation of ES6, and the implementation of other environments does not need to comply with them, or treat the function declaration of block level scope as let.

According to these three rules, functions declared in block level scope in browser's ES6 environment behave like variables declared by var. The above example actually runs the following code.

// Browser ES6 Environmental Science
function f() { console.log('I am outside!'); }
(function () {
  var f = undefined;
  if (false) {
    function f() { console.log('I am inside!'); }
  }

  f();
}());
// Uncaught TypeError: f is not a function

Considering that the behavior differences caused by the environment are too large, you should avoid declaring functions within the block level scope. If necessary, it should also be written as a function expression instead of a function declaration statement.

In addition, there is another thing to pay attention to. ES6's block level scope must have braces. Without braces, the JavaScript engine considers that there is no block level scope.

// The first way of writing, reporting mistakes
if (true) let x = 1;

// The second way is not to report mistakes
if (true) {
  let x = 1;
}

In the above code, the first writing method has no braces, so there is no block level scope, and let can only appear at the top level of the current scope, so an error is reported. The second style has braces, so block level scopes hold.

The same is true for function declaration. In strict mode, a function can only be declared at the top level of the current scope.

// No mistake
if (true) {
  function f() {}
}

// Report errors
'use strict';
if (true)
  function f() {}

Deconstruction and assignment of variables

ES6 allows you to extract values from arrays and objects and assign values to variables according to certain patterns, which is called destructing.

Deconstruction and assignment of arrays

If the deconstruction is not successful, the value of the variable is equal to undefined.

let [a, [[b], c]] = [1, [[2], 3]];
console.log(a,b,c); // 1, 2, 3

let [x, , y, z] = [1, 2, 3];
console.log(x); // 1
console.log(y); // 3
console.log(z); // undefined

Incomplete deconstruction assignment, the pattern to the left of the equal sign, matches only a part of the array to the right of the equal sign.

let [x, [y], z] = [1, [2, 3], 4];
console.log(x); // 1
console.log(y); // 2
console.log(z); // 4

The right side of the array structure assignment must be an array. If the pattern does not match, an error will be reported.

let [a] = {}; // Report errors

The deconstruction assignment can add default values and reference other variables of the deconstruction assignment.

let [a = 1, b = 2] = [, 3];
console.log(a); // 1
console.log(b); // 3

let [x = 1, y = x] = [];  // x = 1; y = 1
let [x = 1, y = x] = [2]; // x = 2; y = 2

Array deconstruction assignments can be used to exchange values of variables.

let [a, b] = [1, 2];
console.log(a, b); // 1, 2
[b, a] = [a, b];
console.log(a, b); // 2, 1

Deconstruction and assignment of objects

Variable must have the same name as property

let { a, b, c } = { a: 'aaa', b: 'bbb' };
console.log(a); // 'aaa'
console.log(b); // 'bbb'
console.log(c); // undefined

Variable name is inconsistent with property name

let { a: x, b: y } = { a: 'aaa', b: 'bbb' };
console.log(x); // 'aaa'
console.log(y); // 'bbb'

Nested assignment. If the parent property of the child object does not exist, an error will be reported. Use with caution.

let { a, a: {x}, b: y } = { a: {x: 'xxx',y: 'yyy'}, b: "bbb" };
console.log(a); // { x: 'xxx', y: 'yyy' }
console.log(x); // 'xxx'

let {c: {d: {e}}} = {c: 'ccc'}; // Report errors
console.log(e)

String deconstruction assignment

String deconstruction assignment, convert string to array object

const [a,b,c] = '123456789';
const {length} = '123456789';
console.log(a, b, c, length); // 1, 2, 3, 9

Objects like arrays have a length attribute, so you can also deconstruct the value of this attribute.

let {length : len} = 'hello';
len // 5

Deconstruction and assignment of function parameters

function add([x, y]){
  return x + y;
}

add([1, 2]); // 3

Here is another example:

const arr = [[1, 2], [3, 4]].map(([a, b]) => a + b);
console.log(arr); // [ 3, 7 ]

You can also use default values for the deconstruction of function parameters.

function move({x = 0, y = 0} = {}) {
  return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]

In the above code, the parameter of function move is an object. By deconstructing the object, the values of variables x and y are obtained. If the deconstruction fails, x and y are equal to the default values.

Note that the following notation will yield different results.

function move({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]

The above code specifies the default value for the parameter of the function move, rather than the variables x and y, so it will get a different result from the previous writing method.

String extension

for...of traversal string

for(let codePoint of 'string'){
  console.log(codePoint)
}
// 's'
// 't'
// 'r'
// 'i'
// 'n'
// 'g'

includes(),startsWith(),endsWith()

let s = 'Hello world!';

const [a, b, c] = [
    s.startsWith('Hello', 2),
    s.endsWith('!'),
    s.includes('o w')
];

console.log(a, b, c); // false true true

repeat()

The repeat method returns a new string, indicating that the original string is repeated n times.

  • If the parameter is [- Infinity,-1] or Infinity, an error will be reported;
  • When the parameter is (- 1,1), it is equal to 0;
  • When the parameter is decimal, round down;
  • Parameter NaN is equal to 0;
  • If the parameter is a string, it will be converted to a number first.
'str'.repeat('3') // 'strstrstr'

padStart(), padEnd()

padStart(), padEnd(), has two parameters. The first parameter is the maximum length of string completion, and the second parameter is the completed string.

The second parameter is blank by default. When the second parameter is omitted, it is filled with blank by default.

When the first parameter is less than the original length of the string, the original string is returned.

If the sum of the length of the complement string and the original string exceeds the maximum length, the complement string exceeding the number of digits will be truncated.

Common use: fill in the specified number of digits, prompt string format.

'123456'.padStart(10, '0') // "0000123456"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"

Template string (`)

const str = 'world';
const template = `Hello ${str}`;
console.log(template); // Hello world

Numerical expansion

Binary and octal representation

Use binary notation, prefix 0b, octal notation, prefix 0o, ES6 does not support the use of 00 prefix for octal notation.

Use toString method for base conversion and Number method for direct conversion to decimal system.

0b1100100 === 100; // true
0o144 === 100; // true

(0b1100100).toString(8); // 144
(0b1100100).toString(10); // 100
Number('0b1100100'); // 100

Number.isFinite(),Number.isNaN()

Number. Isfinish() is used to check whether a value is finite, that is, not infinite. Parameter type is not a numeric value, and number.isfinish returns false.

Number.isNaN() is used to check whether a value is NaN. Parameter type is not NaN, Number.isNaN returns false.

Number.isFinite(15); // true
Number.isFinite(-Infinity); // false

Number.isNaN(15) // false
Number.isNaN(9/0) // true

Number.parseInt(), Number.parseFloat()

ES6 migrates the global methods parseInt() and parseFloat() to the Number object, with the behavior completely unchanged.

Number.isInteger()

Number.isInteger() is used to determine whether a value is an integer.

Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false
  • Number.EPSILON is a minimum constant. If the error of floating-point number is less than this value, it can be considered that there is no error;
  • The maximum range of number.max'safe'integer;
  • Number.MIN SAFE INTEGER the minimum range of the security integer;
  • Number.isSafeInteger() is used to determine whether an integer falls within the safe integer range.
Number.isSafeInteger(9007199254740993) // false
Number.isSafeInteger(990) // true
Number.isSafeInteger(9007199254740993 - 990) // true

Extension of Math object

Math.trunc() removes the decimal part of a number and returns the integer part. Parameter is not a numerical value. Number() will be called internally to be a numerical value. For null value and integer value that cannot be intercepted, NaN will be returned. (the extension method of Math object is the same for non numerical value)

Math.trunc(5.9) // 5
Math.trunc(-4.9) // -4
Math.trunc(null) // 0
Math.trunc('foo'); // NaN

Math.sign() determines whether a number is positive, negative, or zero.

Math.sign(-5) // -1 negative
Math.sign(5) // +1 Positive number
Math.sign(0) // +0 Zero
Math.sign(-0) // -0 Zero
Math.sign(NaN) // NaN

Math.cbrt() computes the cube root of a number.

Math.cbrt(2)  // 1.2599210498948734

// Math.sqrt(x) Calculate the square root
Math.sqrt(2) // 1.4142135623730951

// exponentiation Math.pow(x,y)
Math.pow(2, 3)

Math.hypot() returns the square root of the sum of squares of all parameters.

Math.hypot(3, 4);        // 5
Math.hypot(3, 4, 5);     // 7.0710678118654755

Function extension

rest parameter

ES6 introduces the rest parameter (in the form of... Variable name) to get the redundant parameters of the function. The variable matched with the rest parameter is an array, which puts the redundant parameters into the array. It can only be the last parameter, the length property of the function, excluding the rest parameter.

function sum1(x, y, ...args) {
    let sum = 0;

    for (let arg of args) {
        sum += arg;
    }

    return sum;
}

console.log(sum1(1, 2, 3, 4)) // 7

function sum2(...args) {
    return args.reduce((prev, curr) => {
        return prev + curr
    }, 0)
}

console.log(sum2(1, 2, 3)); // 6

name attribute

The name property of the Function, which returns the Function name of the Function. For anonymous functions, ES5 returns',, ES6 returns variable name; for Function instances returned by Function constructor, the value of name attribute is anonymous; for functions returned by bind, the value of name attribute will be prefixed with bound.

function fn() {}
fn.name // 'fn'

function foo() {};
foo.bind({}).name // 'bound foo'

(new Function).name // "anonymous"

(function(){}).bind({}).name // 'bound '

Arrow function

const fn = v => v;

// Equate to
const fn = function (v) {
  return v;
};

Points for attention

  • this object in the function body is the object when it is defined, not when it is used;
  • It can't be used as a constructor, that is, you can't use the new command, otherwise an error will be thrown;
  • The arguments object cannot be used, it does not exist in the function body. If you want to use, you can use the rest parameter instead;
  • You cannot use the yield command, so the arrow function cannot be used as a Generator function.

Array extension

Extended operator

The spread operator is three dots (...). It is like the inverse operation of rest parameter, which transforms an array into a comma separated sequence of parameters.

const arr = [1, 2, 3];
arr.push(...[4, 5, 6]);

Application of extension operator:

  • Array expansion
const arr = [1, 2, 3];
...arr // 1, 2, 3
  • Replicated array
const a1 = [1, 2];
// Write a way
const a2 = [...a1];
// Writing two
const [...a2] = a1;

// Amount to
const a1 = [1, 2];
const a2 = a1.concat();
  • Deconstruct assignment, string to array
const list = [1, 2, 3];
[a, ...b] = list;
console.log(a) // 1
console.log(b) // [2, 3]

[...'hello'] // ['h', 'e', 'l', 'l', 'o']

Array.from()

The Array.from method is used to convert two types of objects into real arrays: array like objects and iteratable objects (including the new data structures Set and Map in ES6).

Common array like objects include the NodeList collection returned by DOM operation and the arguments object inside the function.

let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};

// ES5 Writing
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6 Writing
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

Array.from('hello');
// ['h', 'e', 'l', 'l', 'o']

let namesSet = new Set(['a', 'b']);
Array.from(namesSet); // ['a', 'b']

Array.from can also accept the second parameter, which is similar to the array map method. It is used to process each element and put the processed value into the returned array.

let arrayLike = {
    '0': 1,
    '1': 2,
    '2': 3,
    length: 3
};
Array.from(arrayLike, x => x * x); // [ 1, 4, 9 ]

Array.of()

The Array.of method is used to convert a set of values to an array. The main purpose of this method is to make up for the shortage of Array() constructor. Because the number of parameters is different, the behavior of Array() will be different.

Array.of() // []
Array.of(undefined) // [undefined]
Array.of(1) // [1]
Array.of(1, 2) // [1, 2]

copyWithin()

Parameters:

  • Target (required): required. Copy to the specified target index location.
  • start (optional): optional. The starting location of the element copy.
  • end (optional): optional. The index location where replication stops (array. Length by default). If it is a negative value, it means the reciprocal.

All three parameters should be numerical values. If they are not, they will be automatically converted to numerical values.

var result = [1, 2, 3, 4, 5].copyWithin(0, 3) 
console.log(result)//[4,5,3,4,5]

find() and findIndex()

Find method of array instance is used to find the first qualified array member. If there is no qualified member, undefined is returned.

The findIndex method returns the location of the first eligible array member, or - 1 if none of the members meet the criteria.

[1, 4, -5, 10].find(n => n < 0); // -5
[1, 4, -5, 10].findIndex(n => n < 0); // 2

Both methods can take a second parameter to bind the callback's this object.

function f(v){
  return v > this.age;
}
let person = {name: 'John', age: 20};
[10, 12, 26, 15].find(f, person);  // 26

Both of these methods can find NaN, which makes up for the lack of indexOf method of array.

fill() fills the array

The fill method fills an array with the given value. The fill method takes a second and a third parameter that specifies the start and end positions of the fill. If the filled type is object, then the object assigned is the object with the same memory address, not the deep copy object. If you change one item in the array, all items will change.

let arr = Array.of(1, 2, 3).fill({
    num: 20
});

console.log(arr); // [ { num: 20 }, { num: 20 }, { num: 20 } ]

arr[0].num = 10;
console.log(arr); // [ { num: 10 }, { num: 10 }, { num: 10 } ]

entries(), keys() and values() traverse the array

for (let index of ['a', 'b'].keys()) {
  console.log(index);
}
// 0
// 1

for (let elem of ['a', 'b'].values()) {
  console.log(elem);
}
// 'a'
// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}
// 0 "a"
// 1 "b"

includes()

The includes method returns a Boolean value indicating whether an array contains the given value, similar to the includes method of a string. The second parameter of the method indicates the starting position of the search. The second parameter is a negative number, which is taken as its reciprocal. The second parameter is greater than the array length, which is taken as 0.

[1, 2, 3].includes(3, -1); // true

flat(),flatMap()

By default, flat() only flattens one layer. If you want to flatten multiple nested arrays, you can write the parameters of the flat() method as an integer to indicate the number of layers you want to flatten. The default is 1.

The parameter of flat() is 2, which means to "flatten" the nested array of two layers. If no matter how many layers are nested, you want to convert it to a one-dimensional array, you can use the Infinity keyword as a parameter.

[1, [2, [3]]].flat(Infinity);
// [1, 2, 3]

flatMap() traverses the array first, then "flattens" one layer, and only one layer can be flattened. The parameter is similar to the map() method.

[2, 3, 4].flatMap(x => [x, x * 2]); // [2, 4, 3, 6, 4, 8]

// Amount to
[2, 3, 4].map(x => [x, x * 2]).flat(); // [2, 4, 3, 6, 4, 8]

Object extension

Attribute concise representation

const a = 1;
const b = 2;

const c = {a, b};
// Equate to
const c = {a: a, b: b};

const o = {
  method() {
    return "Hello!";
  }
};
// Equate to
const o = {
  method: function() {
    return "Hello!";
  }
};

function f(x, y) {
  return {x, y};
}
// Equate to
function f(x, y) {
  return {x: x, y: y};
}

Extension operator for object

Object extenders are similar to array extenders and are mainly used to deconstruct assignments.

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }

let ab = { ...a, ...b };
// Equate to
let ab = Object.assign({}, a, b);

Object.is()

It is used to compare whether two values are strictly equal, which is basically consistent with the behavior of the strict comparison operator (====).

Object.is('str', 'str'); // true
Object.is({}, {}); // false

There are only two differences: one is that + 0 is not equal to - 0, and the other is that NaN is equal to itself.

+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

Object.assign()

The Object.assign method is used to merge objects, copy all enumerable properties of the source object to the target object.

The first parameter of the Object.assign method is the target object, and the subsequent parameters are all source objects. If the target object has a property with the same name as the source object, or if more than one source object has a property with the same name, the following properties overwrite the previous ones.

Because undefined and null cannot be converted to objects, if they are used as the first parameter, an error will be reported.

const target = { a: 1, b: 1 };

const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

Common use:

  • Add properties and methods to objects
  • Clone or merge objects
  • Assign default values to properties

Object.keys(),Object.values(),Object.entries()

ES5 introduces the Object.keys method to return an array. The members are the key names of all the traversable properties of the parameter object itself (excluding the inherited).

var obj = { foo: 'bar', baz: 42 };
Object.keys(obj)
// ["foo", "baz"]

The Object.values method returns an array whose members are the key values of all the enumerable properties of the parameter object itself (excluding inheritance).

const obj = { foo: 'bar', baz: 42 };
Object.values(obj)
// ["bar", 42]
const obj = { 100: 'a', 2: 'b', 7: 'c' };
Object.values(obj)
// ["b", "c", "a"]

In the above code, the attribute named numerical value is traversed from small to large according to the numerical value size, so the return order is b, c, a.

Object.values only returns the traversable properties of the object itself.

const obj = Object.create({}, {p: {value: 42}});
Object.values(obj) // []

In the above code, if the object property (Property p) added by the second parameter of the Object.create method is not explicitly declared, it cannot be traversed by default, because the enumerable of the property description object of p is false by default, and Object.values will not return this property. As long as the enumerable is changed to true, Object.values will return the value of property p.

const obj = Object.create({}, {p:
  {
    value: 42,
    enumerable: true
  }
});
Object.values(obj) // [42]

The Object.entries() method returns an array of key value pairs of all enumerable properties of the parameter object itself (excluding inheritance).

const obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]

Except for the return value, the behavior of this method is basically the same as that of Object.values.

Object.fromEntries()

The Object.fromEntries() method is the reverse operation of Object.entries(), which is used to convert a key value pair array into an object.

Object.fromEntries([
  ['foo', 'bar'],
  ['baz', 42]
])
// { foo: "bar", baz: 42 }

Tags: Javascript Attribute REST less

Posted on Wed, 05 Feb 2020 07:58:30 -0500 by pernest