ECMAScript 6.0 (ES6 for short), as the language standard of the next generation of JavaScript, was officially released in June 2015. It has been more than 4 years since its release. However, due to the wide syntax contained in it, it takes some time to fully digest it. As well as ES6 after the new grammar knowledge points, use scenarios, I hope to help you.
As the saying goes, a good memory is better than a bad pen, and you can remember the code quickly and deeply
Chapter 1 Strict mode
Strict mode is introduced in ES5 and has the following restrictions.
- Variables must be declared before use
- Function arguments cannot have attributes of the same name; otherwise, an error is reported
- Can’t use the with statement (honestly I hardly ever use it)
- Cannot assign a value to a read-only attribute, otherwise an error is reported
- You can’t use the prefix 0 to represent octal numbers.
- Do not delete data that cannot be deleted; otherwise, an error is reported
- Cannot delete variable delete prop, error will be reported, only delete property delete global[prop]
- Eval does not introduce variables in its outer scope
- Eval and arguments cannot be reassigned
- Arguments do not automatically reflect changes to function arguments
- Can’t use arguments. Caller
- Can’t use arguments.callee (I rarely use arguments.callee to be honest)
- Disallow this from pointing to a global object
- Can’t use fn.caller and fn.arguments to get the stack of function calls (which I rarely use to be honest)
- Added reserved words (such as protected, static, and interface)
Chapter 2 let and const commands
The basic usage is the same as var in ES5, but let states that there is no variable promotion
Var declarations promote variables, while let and const declarations do not
The temporary dead zone is called TDZ
As long as the let command exists in the block-level scope, the variables it declares are “binding” to the region, no longer subject to external influence
var num = 123
if (true) {
num = 'abc' // Cannot access 'num' before initialization
let num
}
Copy the code
ES6 explicitly states that if there are let and const commands in a block, the variables declared by the block to those commands are closed from the start, and any use of those variables before declaration will result in an error
The nature of temporary death is that, as soon as the current scope is entered, all variables used already exist, but cannot be retrieved until the line of code that declared the variable appears
Duplicate declarations are not allowed
Let does not allow the same variable to be declared twice in the same scope
/ / an errorfunction fn() {
letA = 1 var a = 2function fn() {
let a = 1
let a = 2
}
Copy the code
Therefore, arguments cannot be redeclared inside functions.
function fn(arg) {
letarg; / / error}function fn(arg) {
{
letarg; // no error}}Copy the code
Block-level scope
Why fast scope
ES5 has only global and function scopes, not block scopes, which leads to a lot of irrational scenarios
In the first scenario, the inner variables may override the outer variables
var num = 123
function fn() {
console.log(num)
if (falseVar num = 456}} fn() {var num = 456}} fn()Copy the code
In the second scenario, loop variables used to count are leaked as global variables
var s = 'hello';
for(var i = 0; i < s.length; I ++) {console.log(s[I]); } console.log(i); / / 5Copy the code
ES6 block-level scope
function fn() {
let n = 5
if (true) {
let n = 10
}
console.log(n)
}
fn() // 5
Copy the code
ES6 allows for arbitrary nesting of block-level scopes.
The advent of block-level scopes has virtually eliminated the need for widely used IIFE/immediate-call-immediate-function expressions.
// IIFE ()function() { var tmp = ... ; . } ()); // block level scope {lettmp = ... ; . }Copy the code
Block-level scopes do not return values unless t is a global variable.
Const command
Const declares a read-only constant.
Const has the same characteristics as let except in the following two points:
1. Once a const variable is declared, it must be initialized immediately and cannot be left for later assignment. 2. Once declared, the value of a constant cannot be changed.Copy the code
nature
Const limits assignment behavior.
const a = 1; a = 2; // const arr = []; Arr.push (1) // [1] // Const holds a pointer to a variable when declaring a reference constant, as long as the pointer is kept constant. The following behavior will report an error arr = []; // An error is reported because it is an assignment. The pointer held by the variable arr has changed.Copy the code
Top-level object properties and global variables
In ES5, the properties of the top-level object are equivalent to global variables
window.a = 1
a // 1
a = 2;
window.a // 2
Copy the code
The attributes of top-level objects are linked to global variables, which is considered one of the biggest design failures of the JavaScript language
To solve this problem, ES6 introduced the Let Cosnt class to declare global variables that are no longer attributes of top-level objects
At the same time, for backward compatibility, var and function declare variables that are still properties of the global object
var a = 1;
window.a // 1
let b = 1;
window.b // undefined
Copy the code
The third chapter is the deconstruction of variable assignment
Basic usage
ES6 allows extracting values from arrays and objects and assigning values to variables in a pattern called Destructuring.
ES5 declares multiple variables at once
var a = 1,
b = 2,
c = 3;
Copy the code
ES6 declares multiple variables at once
let [a, b, c] = [1, 2, 3]
// a = 1
// b = 2
// c = 3
Copy the code
Essentially, this is “pattern matching,” in which the variable on the left is assigned a corresponding value as long as the pattern on both sides of the equal sign is the same.
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [ , , third] = ["foo"."bar"."baz"];
third // "baz"
let [x, , y] = [1, 2, 3];
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []
Copy the code
If the deconstruction fails, the value of the variable is equal to undefined.
let [foo] = [];
let[bar, foo] = [1]; // foo is undefinedCopy the code
Another case is incomplete deconstruction, where the pattern to the left of the equals sign matches only part of the array to the right of the equals sign. In this case, deconstruction can still succeed.
let [x, y] = [1, 2, 3];
x // 1
y // 2
let[a, [b], d] = [1, [2, 3], 4]; A // 1 b // 2 d // 4 // The above two examples are incomplete deconstruction, but can be successful.Copy the code
If the right-hand side of the equals sign is not an array, an error will be reported.
/ / an errorlet [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
Copy the code
The default value
Destruct assignment allows you to specify default values.
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
Copy the code
Note that ES6 internally uses the strict equality operator (===) to determine whether a position has a value. Therefore, if an array member is not strictly equal to undefined, the default value will not take effect.
let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null
Copy the code
If the default value is an expression, the expression is lazy, that is, evaluated only when it is used.
function f() {
console.log('aaa');
}
let[x = f()] = [1]; // [1] // equivalent tolet x;
if ([1][0] === undefined) {
x = f();
} else {
x = [1][0];
}
Copy the code
The default value can refer to another variable of the deconstructed assignment, but that variable must already be declared.
let [x = 1, y = x] = []; // x=1; y=1
let [x = 1, y = x] = [2]; // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let[x = y, y = 1] = []; // ReferenceError // The last expression above failed because y was not declared when x used the default value yCopy the code
Object destructuring assignment
Deconstruction can be applied to objects as well as arrays
let { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
Copy the code
Object deconstruction differs from arrays in one important way. The elements of an array are arranged in order, and the value of a variable is determined by its position. The attributes of an object have no order, and variables must have the same name as the attributes to get the correct value.
let { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined
Copy the code
If the variable name does not match the attribute name, it must be written as follows.
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
Copy the code
In effect, object deconstruction assignment is shorthand for the following
let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };
Copy the code
In other words, the internal mechanism of deconstructive assignment of an object is to find the property of the same name and then assign it to the corresponding variable. It is the latter, not the former, that is really assigned.
let { foo: baz } = { foo: "aaa", bar: "bbb" };
baz // "aaa"
foo // error: foo is not defined
Copy the code
Like arrays, deconstruction can also be used for nested structured objects.
let obj = {
p: [
'Hello',
{ y: 'World'}};let { p: [x, { y }] } = obj;
x // "Hello"
y // "World"
Copy the code
The deconstruction of an object can also specify default values.
var {x = 3} = {};
x // 3
var {x, y = 5} = {x: 1};
x // 1
y // 5
var {x: y = 3} = {};
y // 3
var {x: y = 3} = {x: 5};
y // 5
var { message: msg = 'Something went wrong' } = {};
msg // "Something went wrong"
Copy the code
The default is valid only if the object’s attribute value is strictly equal to undefined.
var {x = 3} = {x: undefined};
x // 3
var {x = 3} = {x: null};
x // null
Copy the code
An error is reported if the deconstruction pattern is a nested object and the parent property of the child object does not exist.
/ / an errorlet {foo: {bar}} = {baz: 'baz'}; // The attribute foo of the object to the left of the equals sign corresponds to a child object. The bar property of this child object will report an error when deconstructed. And the reason for that is very simple, because foo is undefined, and if you take a subattribute,Copy the code
Since arrays are special objects in nature, they can be deconstructed as object properties.
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
Copy the code
Destruct assignment of a string
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
Copy the code
Array-like objects have a length attribute, so you can also deconstruct and assign to this attribute.
let {length : len} = 'hello';
len // 5
Copy the code
Destruct assignment of function arguments
function add([a,b]){
returna + b; } the add () [2, 3] / / 5Copy the code
Function arguments can also be destructed using default values.
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]Copy the code
Note that the following will give you 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]Copy the code
The above code specifies default values for the arguments to move, not for the variables x and y, so the result is different from the previous one.
Deconstructive assignment of values and Bores
When deconstructing an assignment, if the value and Boolean are to the right of the equals sign, the object is converted first.
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
Copy the code
parentheses
While deconstructing assignments is convenient, it’s not easy to parse. There is no way for the compiler to know from the start whether an expression is a pattern or an expression. It must be parsed to (or not) the equals sign.
This raises the question of what to do if parentheses are present in the schema. The rule of ES6 is that parentheses should not be used whenever there is a possibility of ambiguity about deconstruction.
However, this rule is actually not so easy to discern and can be quite troublesome to deal with. Therefore, it is recommended that parentheses not be placed in schemas whenever possible.
Parentheses cannot be used for the following three types of destructuring assignments.
1) variable declaration statement // all errorlet [(a)] = [1];
let {x: (c)} = {};
let ({x: c}) = {};
let {(x: c)} = {};
let {(x): c} = {};
let{ o: ({ p: p }) } = { o: { p: 2 } }; 2) Function arguments - function arguments are also variable declarations and therefore cannot have parentheses. / / an errorfunction f([(z)]) { returnz; } / / an errorfunction f([z,(x)]) { returnx; {p: a} = {p: 42}; ([a]) = [5]; // The above code puts the entire schema in parentheses, causing an error. [({p: a}), {x: c}] = [{}, {}];Copy the code
** There is only one case in which parentheses can be used: parentheses can be used on the non-pattern part of an assignment statement.
[(b)] = [3]; {p: (d)} = {}); // Correct [(parseint.prop)] = [3]; / / rightCopy the code
All three of the above lines execute correctly because they are assignment statements, not declarations; Second, their parentheses are not part of the schema. In the first line, the schema takes the first member of the array, regardless of parentheses; In the second line, the mode is P, not D; The third line of statements has the same properties as the first.
use
1. In addition to defining more than one variable at a time 2. You can easily match function parameters to values. Default values for function argumentsCopy the code
Chapter 4 String Extension
Includes (), startsWith(), endsWith()
Traditionally, JavaScript has only had the indexOf method, which can be used to determine whether one string is contained within another. ES6 offers three new approaches.
- Includes () : Returns a Boolean value indicating whether the parameter string was found.
- StartsWith () : Returns a Boolean value indicating whether the argument string is at the head of the original string.
- EndsWith () : Returns a Boolean value indicating whether the argument string is at the end of the original string.
let s = 'Hello world! ';
s.startsWith('Hello') / /true
s.endsWith('! ') / /true
s.includes('o') / /true
Copy the code
All three of these methods support a second parameter indicating where the search begins.
let s = 'Hello world! ';
s.startsWith('world'/ /, 6)true
s.endsWith('Hello'/ /, 5)true
s.includes('Hello'/ /, 6)false
Copy the code
The code above shows that with the second argument n, endsWith behaves differently from the other two methods. It works for the first n characters, while the other two work from the NTH position up to the end of the string.
repeat
The repeat method returns a new string, repeating the original string n times.
'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""
Copy the code
- If the argument is a decimal, it is rounded down.
- If the repeat argument is negative or Infinity, an error is reported.
- The decimal between 0 and minus 1 is the same thing as 0, because it’s rounded first. If the decimal number between 0 and -1 is rounded to -0, repeat is regarded as 0.
- The NaN argument is equal to 0.
- If the argument of the repeat is a string, it is converted to a number first.
PadStart (), padEnd ()
ES2017 introduces the function of string completion length. If a string is not of a specified length, the header or tail is completed. PadStart () is used for head completion and padEnd() for tail completion.
'x'.padStart(5, 'ab') / /'ababx'
'x'.padStart(4, 'ab') / /'abax'
'x'.padEnd(5, 'ab') / /'xabab'
'x'.padEnd(4, 'ab') / /'xaba'
Copy the code
- Returns the original string if the length of the original string is equal to or greater than the specified minimum length.
- If the sum of the length of the completion string and the original string exceeds the specified minimum length, the completion string exceeding the number of bits is truncated.
- If the second argument is omitted, Spaces are used to complete the length by default.
Template strings (which I use a lot in my projects)
Es5 string template output usually uses + concatenation.
Such shortcomings are obvious: string concatenation content is too chaotic, easy to make mistakes.
ES6 introduced template strings to solve this problem.
var name = "Tomato",trait = "Handsome";
//es5dDdD
var str = "His name is"+name+"Very much."+trait+"And talk nice."; //es6 var str2 =${name}And people are very${trait}And speak well;Copy the code
Template strings are enhanced strings, identified by backquotes (‘). It can be used as a regular string, it can be used to define multi-line strings, or it can be used to embed variables in strings.
- If you need to use backquotes in a template string, it is preceded by backslash escape.
- If a template string is used to represent a multi-line string, all whitespace and indentation are preserved in the output.
- A variable is embedded in the template string. You need to write the variable name in ${}.
- You can put any JavaScript expression inside curly braces, perform operations, and reference object properties.
- You can also call functions from a template string.
- If the value in braces is not a string, it is converted to a string according to the usual rules. For example, if the braces are an object, the toString method of the object will be called by default.
- An error is reported if variables in the template string are not declared.
The label template
The template string can be immediately followed by the name of a function that will be called to process the template string. This is called the “tag template” feature.
Alert '123' // equivalent to alert(123)Copy the code
A tag template is not really a template, but a special form of function call. The “label” refers to the function, and the template string immediately following it is its argument.
If there are variables in the template character, the call is not simple. Instead, the template string is processed into multiple arguments before the function is called.
let a = 5;
let b = 10;
tag`Hello ${ a + b } world ${ a * b }`; // same as tag(['Hello '.' world '.' '], 15, 50);
Copy the code
Chapter 5 Extension of numerical values
Number. IsFinite (), Number. The isNaN ()
ES6 provides two new methods on Number objects, number.isfinite () and number.isnan ().
Number.isfinite () is used to check whether a Number isFinite.
Number.isFinite(15); // trueNumber. IsFinite (0.8); //true
Number.isFinite(NaN); // false
Number.isFinite(Infinity); // false
Number.isFinite(-Infinity); // false
Number.isFinite('foo'); // false
Number.isFinite('15'); // false
Number.isFinite(true); // false
Copy the code
Number.isnan () is used to check whether a value isNaN.
In contrast to the global isNaN() function, this method does not force the argument to a number and only returns true if the argument is a true numeric type and the value isNaN.
Number.isNaN(NaN); // true
Number.isNaN(Number.NaN); // true
Number.isNaN(0 / 0) // trueThe following are returned if isNaN() is used globallytrue. Number.isNaN("NaN"); // falseThe string"NaN"Is not implicitly converted to the number NaN. Number.isNaN(undefined); //false
Number.isNaN({}); // false
Number.isNaN("blabla"); // false// all the following are returnedfalse
Number.isNaN(true);
Number.isNaN(null);
Number.isNaN(37);
Number.isNaN("37");
Number.isNaN("37.37");
Number.isNaN("");
Number.isNaN("");
Copy the code
Number. The parseInt (), Number. The parseFloat ()
ES6 migrates the global methods parseInt() and parseFloat() onto the Number object, leaving the behavior exactly the same.
// ES5 parseInt('12.34') // 12
parseFloat('123.45 #') // 123.45 // ES6'12.34') // 12
Number.parseFloat('123.45 #') / / 123.45Copy the code
The goal is to gradually reduce the global approach and make the language more modular.
Number.parseInt === parseInt // true
Number.parseFloat === parseFloat // true
Copy the code
Number.isInteger()
Number.isinteger () is used to determine whether a value is an integer. Note that inside JavaScript, integers and floating point numbers are stored the same way, so 3 and 3.0 are treated as the same value.
Number.isInteger(25) // true/ / Number. IsInteger (25.0)true/ / Number. IsInteger (25.1)false
Number.isInteger("15") / /false
Number.isInteger(true) / /false
Copy the code
An extension of the Math object
ES6 adds 17 new math-related methods to the Math object. All of these methods are static and can only be called on Math objects.
Math.trunc() the math.trunc method is used to remove the fractional part of a number and return the integer part.
Math. Trunc (4.1) / / 4 math.h trunc (4.9) / / 4 math.h trunc (4.1) / / - 4 math.h trunc (4.9) / / - 4 math.h trunc (0.1234) / / 0Copy the code
- For non-numeric values, math.trunc internally converts them to numeric values using the Number method.
- NaN is returned for null values and values that cannot intercept integers.
The math.sign () math.sign method is used to determine whether a number is positive, negative, or zero. For non-numeric values, they are converted to numeric values first.
It returns five values.
- If the argument is positive, return +1;
- Argument is negative, return -1;
- Argument 0, returns 0;
- Parameter -0, return -0;
- Other values, return NaN.
Math.sign(-5) // -1
Math.sign(5) // +1
Math.sign(0) // +0
Math.sign(-0) // -0
Math.sign(NaN) // NaN
Math.sign(' ') // 0
Math.sign(true) // +1
Math.sign(false) // 0
Math.sign(null) // 0
Math.sign('9') // +1
Math.sign('foo') // NaN
Math.sign() // NaN
Math.sign(undefined) // NaN
Copy the code
Math.cbrt() the math.cbrt method is used to calculate the cube root of a number.
For non-numeric values, math.cbrt methods are internally converted to numeric values using the Number method first.
Math.cbrt(-1) // -1 math.cbrt (0) // 0 math.cbrt (1) // 1 math.cbrt (2) // 1.2599210498948734Copy the code
Math.hypot() the math.hypot method returns the square root of the sum of squares of all parameters.
Math.hypot(3, 4); // 5 Math.hypot(3, 4, 5); / / 7.0710678118654755 Math. The hypot (); // 0 Math.hypot(NaN); // NaN Math.hypot(3, 4,'foo'); // NaN
Math.hypot(3, 4, '5'); / / 7.0710678118654755 math.h hypot (3); / / 3Copy the code
Index operator ES2016 has a new index operator (**).
2 ** 2 // 4
2 ** 3 // 8
Copy the code
The exponential operator can be combined with the equal sign to form a new assignment operator (**=).
letA = 1.5; a **= 2; // a = a * a;letb = 4; b **= 3; // the same as b = b * b * b;Copy the code
Chapter 6 extension of functions
Basic usage
Before ES6, you could not specify default values for function parameters directly. You could only use workarounds.
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello'.'China') // Hello China
log('Hello'.' ') // Hello
Copy the code
Used in conjunction with destructively assigned default values
function foo({x, y = 5}) {
console.log(x, y);
}
foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefined
Copy the code
The position of the default value
In general, the parameter that defines the default value should be the last parameter of the function. Because it’s easier to see what parameters are being omitted. If non-trailing arguments are set to default values, they cannot be omitted.
/ / afunction f(x = 1, y) {
return[x, y]; } f () / / [1, undefined] f (2) / / / 2, undefined) f (1) / / an error f (undefined, 1) / / / / [1, 1] two casesfunction f(x, y = 5, z) {
return[x, y, z]; } f () / / / undefined ", undefined, 5 f (1) / / [1, 5, undefined] f (1, 2) / / an error f (1, undefined, 2) / / [1, 5, 2)Copy the code
The length property of the function
When a default value is specified, the length property of the function returns the number of arguments for which no default value is specified. That is, the length attribute is distorted when a default value is specified.
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
Copy the code
- Fn. length Returns the number of parameters
- Arguments. length returns the number of arguments
scope
Once the default values of parameters are set, the parameters form a separate scope when the function is declared initialized. After initialization, the scope will disappear. This syntactic behavior does not occur if the parameter defaults are not set.
var x = 1;
function f(x, y = x) {
console.log(y);
}
f(2) // 2
Copy the code
In the code above, the default value of parameter y is equal to variable x. When f is called, the arguments form a separate scope. In this scope, the default variable x refers to the first parameter x, not the global variable x, so the output is 2.
let x = 1;
function f(y = x) {
let x = 2;
console.log(y);
}
f() // 1
Copy the code
In the above code, the function f is called with the argument y = x forming a separate scope. In this scope, the variable x itself is undefined, so it points to the outer global variable x. When a function is called, the local variable x inside the function body does not affect the default variable x.
var x = 1;
function foo(x = x) {
// ...
}
foo() // ReferenceError: x is not defined
Copy the code
In the code above, the parameter x = x forms a separate scope. Instead, let x = x is executed. Due to the temporary dead zone, this line of code will report an error “x undefined”.
var x = 1;
function foo(x, y = function() { x = 2; }) {
var x = 3;
y();
console.log(x);
}
foo()//3
x//1
Copy the code
If var x = 3 is removed, foo’s internal variable x points to the first argument x, which is the same as the anonymous function’s internal x, so the final output is 2, while the outer global variable x remains intact.
var x = 1;
function foo(x, y = function() { x = 2; }) {
x = 3;
y();
console.log(x);
}
foo() // 2
x // 1
Copy the code
Rest parameters
ES6 introduces the REST parameter (of the form… Variable name), used to get extra arguments to a function so you don’t need to use the arguments object. The rest argument goes with a variable that puts the extra arguments into an array.
functionadd(... values) {let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
Copy the code
The Arguments object is not an array, but an array-like object. So, in order to use an Array, the method of Array must be used. The prototype. Slice. Call it first into an Array. The REST argument does not have this problem; it is a true array, and any array-specific methods can be used. Here is an example of rewriting an array push method with the rest argument.
functionpush(array, ... items) { items.forEach(function(item) {
array.push(item);
console.log(item);
});
}
var a = [];
push(a, 1, 2, 3)
Copy the code
Note that the REST argument cannot be followed by any other argument (that is, only the last one), otherwise an error will be reported.
/ / an errorfunctionf(a, ... b, c) { // ... }Copy the code
The length property of the function, excluding the REST argument.
(function(a) {}).length // 1
(function(... a) {}).length // 0 (function(a, ... b) {}).length // 1Copy the code
Strict mode
Starting with ES5, functions can be set to strict mode internally.
ES2016 has changed a bit, stating that functions cannot be explicitly set to strict mode internally if they use default values, destructed assignments, or extended operators. Otherwise, an error will be reported
The name attribute
Returns the function name.
function foo() {}
foo.name // "foo"
var f = function () {}; // "f"
Copy the code
Arrow function
- Basic usage
ES6 allows functions to be defined using arrows (=>). var f = v => v; // The arrow function above is equivalent to var f =function(v) {
returnv; }; If the arrow function requires no arguments or more than one argument, use a parenthesis to represent the argument part. var f = () => 5; // the same thing as var f =function () { return5}; var sum = (num1, num2) => num1 + num2; Var sum = var sum =function(num1, num2) {
returnnum1 + num2; }; If the arrow function has more than one statement in the code block, enclose them in braces, and usereturnStatement return. var sum = (num1, num2) => {returnnum1 + num2; } Because braces are interpreted as blocks of code, if the arrow function returns an object directly, parentheses must be placed around the object, otherwise an error will be reported. / / an errorlet getTempItem = id => { id: id, name: "Temp"}; / / is not an errorlet getTempItem = id => ({ id: id, name: "Temp" });
Copy the code
- The use note arrow function has several use note points.
- The this object inside the function is the object at which it is defined, not used.
- Should not be used as a constructor, that is, the new command should not be used, otherwise an error will be thrown.
- You cannot use the Arguments object, which does not exist in the function body. If you do, use the REST argument instead.
- Yield cannot be used, so arrow functions cannot be used as Generator functions.
This is fixed, not because the arrow function has a mechanism to bind this, but because the arrow function does not have its own this, so the inner this is the outer code block’s this. Because it does not have this, it cannot be used as a constructor.
The code for converting the arrow function to ES5 is as follows.
// ES6
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
// ES5
function foo() {
var _this = this;
setTimeout(function () {
console.log('id:', _this.id); }, 100); } // The converted VERSION of ES5 makes it clear that the arrow function does not have its own this at all, but instead refers to the outer this.Copy the code
Since the arrow function does not have its own this, it cannot of course use call(), apply(), or bind() to change the direction of this.
The trailing comma of a function argument
ES2017 allows the trailing comma as the last argument to a function.
This also makes function arguments consistent with the trailing comma rule for arrays and objects.
function clownsEverywhere(
param1,
param2,
) { /* ... */ }
clownsEverywhere(
'foo'.'bar',);Copy the code
Chapter 7 Extension of arrays
Extended operator
The spread operator (spread) is three points (…) . It is like the inverse of the REST argument, turning an array into a comma-separated sequence of arguments.
console.log(... [1, 2, 3]) // 1 2 3 console.log(1, ... [2, 3, 4], 5) // 1 2 3 4 5 [...document.querySelectorAll('div')]
// [<div>, <div>, <div>]
Copy the code
This operator turns an array into a sequence of arguments.
Extended operators can also be followed by expressions.
var x = 1
const arr = [...(x > 0 ? ['a'] : [], 'b')]
console.log(arr) // ['a'.'b']
Copy the code
If the extension operator is followed by an empty array, nothing happens.
var arr = [...[], 1]
console.log(arr) // [1]
Copy the code
- Since the extension operator can expand an array, the apply method is no longer needed to convert the array to a function argument.
// ES5functionfn(x, y, z) { // ... } var arr = [1, 2, 3] fn. Apply (null, arrfunctionfn(x, y, z) { // ... } var arr = [1, 2, 3] fn(... arr)Copy the code
In ES5, we used math.max to get the maximum value of an array
/ / es5 Math. Max. Apply (null,,5,2,8 [1]) / / / / 8 es6 math.h Max (... [1,5,2,8]) // 8 //Copy the code
- Extend the application of operators
Var arr = [1, 2, 3] var arr1 = arr.concat() arr1[arr1.length] = 5 console.log(arr) // [1, 2, 3] console.log(arr1) // [1, 2, 3, 5] // The extension operator provides an easy way to copy arrays. Var arr = [1, 2, 3] var arr = [...arr] console.log(arr1) var arr = [1, 2, 3] console.log(arr1) 3] var [...arr1] = arr console.log(arr1) // [1, 2, 3]Copy the code
- The merge array extension operator provides a new way of writing an array merge.
// ES5
[1, 2].concat(more)
// ES6
[1, 2, ...more]
var arr1 = ['a'.'b'];
var arr2 = ['c'];
var arr3 = ['d'.'e']; // ES5 merge array arr1 = arr1.concat(arr2, arr3); / / /'a'.'b'.'c'.'d'.'e'[...arr1,...arr2,...arr3] // ['a'.'b'.'c'.'d'.'e' ]
Copy the code
- Combined with deconstruction assignment
Extension operators can be used in conjunction with destructuring assignments to generate arrays.
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
const [first, ...rest] = [];
first // undefined
rest // []
const [first, ...rest] = ["foo"];
first // "foo"Rest / / [] / / wrong usage const [... butLast last] = [1, 2, 3, 4, 5]. / / error const [first,... the middle, and last] = [1, 2, 3, 4, 5]. / / an errorCopy the code
- The string extension operator can also turn a string into a true array
[...'hello'] / ["h"."e"."l"."l"."o"]
Copy the code
- Turn an array of classes into an array
// The dom node array is a class array, can not use the array methodlet nodeList = document.querySelectorAll('div'); // Convert to array by extending cloud algorithmlet array = [...nodeList];
Copy the code
- Array.from()
The array. from method is used to convert two types of objects into real arrays
/ / the NodeList objectlet ps = document.querySelectorAll('p');
Array.from(ps).forEach(function(p) { console.log(p); }); / / the arguments objectfunction foo() {
var args = Array.from(arguments);
// ...
}
Copy the code
Parameters:
- First argument: an array-like object that is converted to a real array
- Second argument: an array-like map method that processes each element and puts the processed value into the returned array.
- Third argument: If this is used in the map function, you can also pass a third argument to array. from to bind this.
- Array.of()
The array. of method converts a set of values to an Array.
The main purpose of this method is to complement the Array constructor Array(). Array() behaves differently because of the number of arguments.
//Array Array() // [] Array(3) // [, , ,] Array(3, 11, 8) // [3, 11, 8] Array.of(3, 11, 8) / /,11,8 [3] Array of (3) / / [3] Array) of (3). The length / / 1Copy the code
- Array instance copyWithin()
The copyWithin method of an array instance, inside the current array, copies the members at the specified location to other locations (overwriting the original members) and returns the current array. That is, using this method, you modify the current array. It takes three arguments. - target (required) : replaces data from this location. - start (Optional) : reads data from this position. The default value is 0. If it is negative, it is the reciprocal. - end (Optional) : stops reading data before this position. The default value is the array length. If it is negative, it is the reciprocal. All three arguments should be numeric; if not, they are automatically converted to numeric values. [1, 2, 3, 4, 5]. CopyWithin (0, 3) // [4, 5, 3, 4, 5] // Change the first number, the value assigned from the third valueCopy the code
- Find () and findIndex() of array instances
// The find method of an instance of the find() array, used to find the first eligible array member. Its argument is a callback function that is executed by all array members until the first value that returns is foundtrueAnd then returns that member. If there is no qualified member, undefined is returned. [1, 4, -5, 10].find((n) => n < 0) // -5 [1, 5, 10, 15].find(function(value, index, arr) {
returnvalue > 9; }) // 10 // the find callback can take three arguments, the current value, the current position, and the original array. The findIndex method is used very much like the find method, returning the position of the first qualified array member, or -1 if all members fail. [1, 5, 10, 15].findIndex(function(value, index, arr) {
returnvalue > 9; / / 2})Copy the code
I generally use find a lot myself
Both methods can take a second argument that binds the this object of the callback function.
- Fill () for array instances
The fill method fills an array with the given value. The Fill method is handy for initializing an empty array. Any existing elements in the array will be erased. ['a'.'b'.'c'].fill(7) // [7, 7, 7] new Array(3).fill(7) // [7, 7, 7] The fill method can also accept the second and third parameters, which specify the start and end positions of the fill. var arr = [1, 2, 3] arr.fill(6, 1, 2) // [1, 6, 3] arr.fill(6, 1) // [1, 6, 6]Copy the code
- for… of
Es6 introduced as a unified approach to traversing all data structures. A data structure that deploys the Symbol. Iterator attribute is considered to have an iterator interface. The of loop iterates through its members. That is to say, for... Inside the of loop is a call to the symbol. iterator side of the data structure.Copy the code
- Array instance entries(), keys() and values()
Entries (), keys() and values() — For traversing groups of numbers. They both return a traverser object, which can be used for… The of loop is traversed, the only differences being that keys() is traversal of key names, values() is traversal of key values, and entries() is traversal of key value pairs.
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"
Copy the code
- Includes () of array instances
The includes method returns a Boolean value indicating whether an array contains a given value, similar to the includes method of strings. ES2016 introduced this approach.
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // false
[1, 2, NaN].includes(NaN) // true
Copy the code
The second argument to this method represents the starting position of the search, which defaults to 0. If the second argument is negative, it represents the reciprocal position, and if it is greater than the array length (for example, if the second argument is -4, but the array length is 3), it is reset to start at 0.
In the absence of this method, we usually use the indexOf method of arrays to check for the inclusion of a value.
The indexOf method has two drawbacks. First, it is not semantic enough. It means to find the first occurrence position of the parameter value, so it is not intuitive enough to compare whether the value is not equal to -1. Second, it uses the strict equality operator (===) internally for judgment, which leads to misjudgment of NaN.
- A vacancy in an array means that there is no value at any point in the array.
Note that a vacancy is not undefined, and a position equal to undefined is still valued. A vacancy is one that has no value, as the IN operator can indicate.
0 in [undefined, undefined, undefined] // true
0 in,,, / /false
Copy the code
ES5’s treatment of empty seats is already quite inconsistent, mostly ignoring empty seats.
- ForEach (), filter(), reduce(), every() and some() all skip empty Spaces.
- Map () skips the empty space but keeps the value
- Join () and toString() treat empty Spaces as undefined, while undefined and null are treated as empty strings.
ES6 explicitly converts empty space to undefined.
The array. from method converts the empty space of the Array to undefined, which means it doesn’t ignore the empty space.
The array. from method converts the empty space of the Array to undefined, which means it doesn’t ignore the empty space.
Array.from(['a'.'b'/ / [])"a", undefined, "b" ]
Copy the code
Extended operators (…) It also changes the empty space to undefined.
[[...'a'.'b']] / ["a", undefined, "b" ]
Copy the code
CopyWithin () copies the empty space.
[,'a'.'b',,] copyWithin (2, 0) / / /,"a"."a"]
Copy the code
Fill () treats the empty space as a normal array position.
new Array(3).fill('a') / / /"a"."a"."a"]
Copy the code
for… The of loop also iterates over the empty space.
let arr = [, ,];
for (let i of arr) {
console.log(1);
}
// 1
// 1
Copy the code
In the above code, the array arr has two empty Spaces, for… Of doesn’t ignore them. If the map method is used to traverse, the empty space will be skipped.
Entries (), keys(), values(), find() and findIndex() treat empty Spaces as undefined.
// entries()
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]
// keys()
[...[,'a'.keys()] // [0,1] // values() [...[,'a'].values()] // [undefined,"a"]
// find()
[,'a'].find(x => true) // undefined
// findIndex()
[,'a'].findIndex(x => true) / / 0Copy the code
Because the rules for dealing with vacancies are very inconsistent, it is recommended that vacancies be avoided.
Chapter 8 Extension of objects
- A concise representation of the property
ES6 allows variables and functions to be written directly as properties and methods of objects. It’s much more concise.
const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"} // equivalent to const baz = {foo: foo};Copy the code
Methods can also be abbreviated.
const o = {
method() {
return "Hello!"; }}; // const o = {method:function() {
return "Hello!"; }};Copy the code
- Attribute name expression
JavaScript defines attributes of objects in two ways.
// method 1 obj.foo =true; // obj['a' + 'bc'] = 123;
Copy the code
However, if you define objects in a literal fashion (using braces), in ES5 you can only define attributes using method one (identifier).
var obj = {
foo: true,
abc: 123
};
Copy the code
ES6 allows literals to define objects using method two (expressions) as the object’s attribute name, that is, by placing the expression inside square brackets.
let propKey = 'foo';
let obj = {
[propKey]: true['a' + 'bc']: 123
};
Copy the code
Expressions can also be used to define method names.
let obj = {
['h' + 'ello'] () {return 'hi'; }}; obj.hello() // hiCopy the code
Note that property name expressions and compact representations cannot be used at the same time and will cause an error.
// Error const foo ='bar';
const bar = 'abc'; const baz = { [foo] }; // Correct const foo ='bar';
const baz = { [foo]: 'abc'};
Copy the code
- Object.is()
ES5 compares whether two values are equal with only two operators: the equality operator (==) and the strict equality operator (===). They both have the disadvantages that the former automatically converts the data type, the latter that NaN is not equal to itself, and +0 equals -0. JavaScript lacks the operation that, in all environments, two values should be equal as long as they are the same.
ES6 proposes equal value equality algorithm to solve this problem. Object. Is is a new way to deploy this algorithm. It is used to compare whether two values are strictly equal, basically the same behavior as the strict comparison operator (===).
Object.is('foo'.'foo') / /true
Object.is({}, {})
// false
Copy the code
There are only two differences: +0 does not equal -0, and NaN equals itself.
+ 0 = = = 0 / /true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
Copy the code
- Object.assign() is commonly used
The Object.assign method is used to merge objects. It copies all the enumerable attributes of the source Object to the target Object.
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
Copy the code
If there is only one argument, object. assign returns that argument directly.
const obj = {a: 1};
Object.assign(obj) === obj // true
Copy the code
Because undefined and NULL cannot be converted to objects, they are reported as arguments.
Object.assign(undefined) // An error was reported object. assign(null) // An error was reportedCopy the code
Note: Object.assign can be used to work with arrays, but treats arrays as objects.
Object.assign([1, 2, 3], [4, 5]) // [4, 5, 3] // Treat arrays as objects with attributes 0, 1, 2, so attribute 4 of the source array overwrites attribute 1 of the destination array.Copy the code
- Keys () ES5 introduced the Object.keys method, which returns an array of all the keys that enumerable the parameter Object’s own (not inherited) properties.
var obj = { foo: 'bar', baz: 42 };
Object.keys(obj)
// ["foo"."baz"]
Copy the code
ES2017 introduced object. values and object. entries with Object.keys as complementary means of traversing an Object for… Of recycling.
let {keys, values, entries} = Object;
let obj = { a: 1, b: 2, c: 3 };
for (let key of keys(obj)) {
console.log(key); // 'a'.'b'.'c'
}
for (letvalue of values(obj)) { console.log(value); // 1, 2, 3}for (let[key, value] of entries(obj)) { console.log([key, value]); / / /'a', 1], ['b', 2], ['c', 3)}Copy the code
- Object.values()
The Object.values method returns an array of all the key values of an Enumerable property of the parameter Object itself (not including inheritance).
const obj = { foo: 'bar', baz: 42 };
Object.values(obj)
// ["bar", 42]
Copy the code
Returns the order of the members of an array
const obj = { 100: 'a', 2: 'b', 7: 'c' };
Object.values(obj)
// ["b"."c"."a"]
Copy the code
In the above code, the property named value is traversed in ascending order of value, so the return order is B, C, and A.
- Object.entries
The Object.entries method returns an array of all the key/value pairs of the parameter Object’s own (not inherited) enumerable properties.
const obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo"."bar"], ["baz", 42]]Copy the code
This method behaves basically the same as Object.values, except that the return value is different.
Object extension operator
- Deconstruction assignment
let{ x, y, ... z } = { x: 1, y: 2, a: 3, b: 4 }; x // 1 y // 2 z // { a: 3, b: 4 }Copy the code
Since destructuring assignment requires an object to the right of the equals sign, an error is reported if the equals sign is undefined or null, because they cannot be converted to objects.
let{ x, y, ... z } = null; // Runtime errorlet{ x, y, ... z } = undefined; // Runtime errorCopy the code
The destruct assignment must be the last argument, or an error will be reported.
let{... x, y, z } = obj; // Syntax errorlet{ x, ... y, ... z } = obj; // Syntax errorCopy the code
Note that a copy of a deconstructed assignment is a shallow copy, meaning that if the value of a key is a value of a compound type (array, object, function), the deconstructed assignment copies a reference to that value, not a copy of that value.
let obj = { a: { b: 1 } };
let { ...x } = obj;
obj.a.b = 2;
x.a.b // 2
Copy the code
- Extended operator
Extended operators (…) Retrieves all traversable properties of the parameter object and copies them to the current object.
let z = { a: 3, b: 4 };
letn = { ... z }; n // { a: 3, b: 4 }Copy the code
This is equivalent to using the object. assign method.
letaClone = { ... a }; / / is equivalent tolet aClone = Object.assign({}, a);
Copy the code
Symbol ES6 new data types in Chapter 9
ES5 object property names are strings inside, if you need to use a others provide object, what do you attribute to the object is not very clear, but still want to add some of the properties for the object, then you add the property name will likely send conflict and original attribute names, obviously we don’t want that to happen. Therefore, we need to make sure that each property name is unique so that we can prevent property name conflicts. Thus, Symbol was introduced in ES6 to produce a unique value.
What is the Symbol
Symbol is actually a primitive data type introduced in ES6. In addition to Symbol, JavaScript has five other primitive data types, namely Undefined, Null, Boolean, String, Number, and object. These five data types are available in ES5.
How to generate a Symbol value
The Symbol value is generated by the Symbol function as follows:
let s = Symbol();
console.log(s); // Symbol()
typeof s; // "symbol"
Copy the code
We cannot use new before the Symbol function
The Symbol function is not a constructor and cannot be preceded by the new operator. So the Symbol value is not an object and cannot add any attributes. It is just a data type similar to a character. If we force the new operator before the Symbol function, we will get an error as follows:
lets = new Symbol(); // Uncaught TypeError: Symbol is not a constructor(...)Copy the code
Parameter to the Symbol function
String as argument
The Symbol value generated by the above method is not easy to distinguish. The Symbol function can also accept a string parameter to describe the generated Symbol value, which is convenient for us to distinguish different Symbol values.
let s1 = Symbol('s1');
let s2 = Symbol('s2');
console.log(s1); // Symbol(s1)
console.log(s2); // Symbol(s2)
s1 === s2; // false
let s3 = Symbol('s2');
s2 === s3; // false
Copy the code
- After adding parameters to the Symbol function, the console output can distinguish which value it is;
- The Symbol function parameter is only a description of the current Symbol value, so the Symbol function returns different values for the same parameter.
Object as a parameter
If the Symbol function takes an object as an argument, the object’s toString method is called to convert it to a string before a Symbol value is generated. So, after all, the Symbol function can only take a string.
Ymbol values cannot be evaluated
Since Symbol is a data type, we must wonder if the value of Symbol can be evaluated. The Symbol value cannot be operated on, not only with the Symbol value, but also with other types of values. Otherwise, an error will be reported. Symbol values can be explicitly converted to strings and booleans, but not to values.
var mysym1 = Symbol('my symbol');
mysym1.toString() // 'Symbol('my symbol') '
String(mysym1) // 'Symbol('my symbol') '
var mysym2 = Symbol();
Boolean(mysym2); // trueNumber(mysym2) // TypeError: Cannot convert a Symbol value to a Number(...) TypeError: Cannot convert a Symbol value to a Number(...)Copy the code
Symbol indicates the name of the property
Symbol is the property name of an object. There are several ways to write it:
let a = {};
lets4 = Symbol(); // a[s4] ='mySymbol'; // a = {[s4]:'mySymbol'} // Object.defineProperty(a, s4, {value:'mySymbol'});
a.s4; // undefined
a.s4 = 'mySymbol';
a[s4] // undefined
a['s4'] / /'mySymbol'
Copy the code
- When using the object’s Symbol value as the attribute name, the corresponding attribute value cannot be obtained using the dot operator.
- If we use the dot operator to assign a Symbol value to an attribute of an object, the attribute name is actually a string, not a Symbol value.
- When using the Symbol value to define an attribute inside an object, the Symbol value must be enclosed in square brackets, otherwise it is just a string.
Traversal of the Symbol value as the attribute name
Use for… In and for… Of can traverse to the Symbol value of the attribute, Symbol value attributes of the Object as a name, also unable to through the Object. The keys (), Object. GetOwnPropertyNames () to obtain. But, despite the worries, there must be a solution to this mundane need. We can use the Object. GetOwnPropertySymbols () method to get the Symbol on the properties of an Object. You can also use reflect.ownkeys () to return all types of attribute names, including regular and Symbol attribute names.
let s5 = Symbol('s5');
let s6 = Symbol('s6');
let a = {
[s5]: 's5',
[s6]: 's6'
}
Object.getOwnPropertySymbols(a); // [Symbol(s5), Symbol(s6)]
a.hello = 'hello'; Reflect.ownKeys(a); / / /"hello", Symbol(s5), Symbol(s6)]
Copy the code
Using the Symbol value as the name of an object property is not traversed by regular methods. You can define methods for objects that are not private but that you want to be available only internally.
Symbol. The for () and Symbol. KeyFor ()
The symbol.for () function can also be used to generate symbols, but it has a special use: it can reuse a Symbol.
let s1 = Symbol.for("s11");
let s2 = Symbol.for("s22");
console.log(s1===s2)//false
let s3 = Symbol("s33");
let s4 = Symbol("s33");
console.log(s3===s4)//false
console.log(Symbol.keyFor(s3))//undefined
console.log(Symbol.keyFor(s2))//"s22"
console.log(Symbol.keyFor(s1))//"s11"
Copy the code
The ** symbol.for ()** function takes a string as an argument, searches for a Symbol with the string as its name, returns the Symbol if there is one, or creates a new Symbol with the string as its name.
The **Symbol. KeyFor ()** function is used to find the registration information of a Symbol value. The symbol.for () function registers the generated Symbol value in the global environment, so the symbol.keyfor () function can find the Symbol value generated with the symbol.for () function.
Built-in Symbol value
ES6 provides 11 built-in Symbol values, Are respectively Symbol. HasInstance, Symbol. IsConcatSpreadable, Symbol. The species and Symbol. Match, Symbol. Replace, Symbol. The search Symbol. Split, Symbol. Iterator, Symbol. ToPrimitive, Symbol. ToStringTag, Symbol. Interested to know: address
[…new Set([1, 2, 3, 1])]
Set
- Basic usage
ES6 provides a new data structure, Set. It is similar to an array, but the values of the members are unique and there are no duplicate values. The Set itself is a constructor used to generate the Set data structure.
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (leti of s) { console.log(i); } // 2 3 5 4 // Add a member to the Set. The result shows that the Set does not add duplicate values.Copy the code
The Set function can take an array as an argument to initialize.
// 例一
const set= new Set([1, 2, 3, 4, 4]); Set [...] / [1, 2, 3, 4] / / two cases of const items = new set ([1, 2, 3, 4, 5, 5, 5, 5)); Items. size // 5 //function divs () {
return [...document.querySelectorAll('div')];
}
const set= new Set(divs()); Set.size // 56 // similar to divs().foreach (div => set.add(div)); set.size // 56Copy the code
Deduplicate an array
// Remove duplicate members from array [...new Set(array)]Copy the code
- When you add a value to a Set, no type conversion occurs, so 5 and “5” are two different values.
- The algorithm used inside a Set to determine whether two values are different is called “same-value equality.” It is similar to the exact equality operator (===), with the main difference being that NaN is equal to itself, whereas the exact equality operator assumes NaN is not equal to itself.
Note: Two objects are always not equal.
Properties and methods of a Set instance
- Instances of the Set structure have the following properties:
- Set. The prototype. The constructor: constructor, the default is Set function. - set.prototype. size: Returns the total number of Set instance members. The methods of a Set instance fall into two broad categories: operation methods (for manipulating data) and traversal methods (for traversing members).Copy the code
- Four operation methods:
-add (value) : Adds a value and returns the Set structure. -delete (value) : deletes a value and returns a Boolean value indicating whether the deletion is successful. - has(value) : Returns a Boolean value indicating whether the value is a member of Set. -clear () : clears all members without returning any value.Copy the code
- Array.from() converts the Set structure to an Array.
const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);
Copy the code
- Traversal operation
An instance of a Set structure has four traversal methods that can be used to traverse members. -keys () : returns a traverser for key names - values() : returns a traverser for key values - entries() : returns a traverser for key value pairs -forEach() : Uses the callback to iterate over Each member keys(), values(), and entries() keys, values, and entries return traverser objects. Because the Set structure has no key name, only the key value (or the key name and the key value are the same value), the keys and values methods behave exactly the same.let set = new Set(['red'.'green'.'blue']);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (letitem of set.entries()) { console.log(item); } / / /"red"."red"] / ["green"."green"] / ["blue"."blue"]
Copy the code
An instance of a Set structure is traversable by default, and its default traverser generator is its values method.
- forEach()
An instance of a Set structure, like an array, has a forEach method that performs some operation on each member and returns no value.
let set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ':' + value))
// 1 : 1
// 4 : 4
// 9 : 9
Copy the code
The forEach method can also take a second argument that represents the this object inside the binding handler.
WeakSet (I said I have not used it basically)
WeakSet structure is similar to Set, which is also a collection of non-repeating values. However, it differs from Set in two ways:
- WeakSet members can only be objects, not other types of values.
- WeakSet objects are weak references, that is, garbage collection mechanism does not consider WeakSet’s reference to the object, that is, if other objects no longer reference the object, then garbage collection mechanism will automatically recover the memory occupied by the object, not considering the object still exists in WeakSet.
Due to the above feature, a WeakSet member is not suitable for reference because it will disappear at any time. In addition, because the number of internal WeakSet members depends on whether garbage collection mechanism is running, the number of members may be different before and after operation, and when garbage collection mechanism is running is unpredictable, so ES6 stipulates that WeakSet cannot be traversed.
The usage is similar to set
const a = [[1, 2], [3, 4]]; const ws = new WeakSet(a); / / WeakSet {[1, 2], [3, 4]} / / the following is not written const b = [3, 4]; const ws = new WeakSet(b); // Uncaught TypeError: Invalid value usedin weak set(...).Copy the code
WeakSet structure has the following three methods.
- Weakset.prototype. add(value) : Adds a new member to the WeakSet instance.
- Weakset.prototype. delete(value) : Clears the specified member of a WeakSet instance.
- Weakset.prototype. has(value) : Returns a Boolean value indicating whether a value is in a WeakSet instance.
WeakSet has no size property, so there is no way to traverse its members.
Map (rarely used)
Meaning and basic usage
JavaScript objects are essentially collections of key-value pairs (Hash structures), but traditionally strings can only be used as keys. This puts a great limit on its use.
To solve this problem, ES6 provides Map data structures. It is a collection of key-value pairs similar to objects, but the range of “keys” is not limited to strings. Values of all types (including objects) can be used as keys. In other words, the Object structure provides string-value mapping, and the Map structure provides value-value mapping, which is a more complete Hash structure implementation. If you need key-value data structures, Map is better than Object.
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
Copy the code
As a constructor, a Map can also take an array as an argument. The members of this array are arrays representing key-value pairs.
const map = new Map([
['name'.'Joe'],
['title'.'Author']]); map.size // 2 map.has('name') / /true
map.get('name') / /"Zhang"
map.has('title') / /true
map.get('title') / /"Author"
Copy the code
Note that only references to the same object are treated as the same key by the Map structure. Be very careful about this.
const map = new Map();
map.set(['a'], 555);
map.get(['a']) // undefined
Copy the code
If the Map’s key is a value of a simple type (number, string, Boolean), the Map will treat the two values as one key as long as they are strictly equal, such as 0 and -0 being one key, and true and the string true being two different keys. Also, undefined and NULL are two different keys. Although NaN is not strictly equal to itself, Map treats it as the same key.
Instance properties and operation methods
1. The size attribute returns the total number of members 2.setGet (key) Reads the value corresponding to the key, which is undefined if it is not found. 4. Has (key) Returns a Boolean value indicating whether the key is in the Maptrue, return on failurefalse6. Clear () Clears all members. No value is returnedCopy the code
Traversal methods
The Map structure natively provides three traverser generating functions and one traversal method. -keys () : returns the traversal of the key name. - values() : returns a traverser for key values. - entries() : Returns a traverser for all members. -forEach() : traverses all members of the Map. Note in particular that the Map traversal order is the insertion order. The traversal behavior is basically the same assetIs the same.Copy the code
Interconversion with other data structures
- Map to array
const myMap = new Map()
.set(true, 7)
.set({foo: 3}, ['abc']);
[...myMap]
Copy the code
- Array to Map
new Map([
[true, 7],
[{foo: 3}, ['abc']]
])
// Map {
// true => 7,
// Object {foo: 3} => ['abc']
// }
Copy the code
- Map to object If all Map keys are strings, it can be turned into objects.
function strMapToObj(strMap) {
let obj = Object.create(null);
for (let [k,v] of strMap) {
obj[k] = v;
}
return obj;
}
const myMap = new Map()
.set('yes'.true)
.set('no'.false);
strMapToObj(myMap)
// { yes: true, no: false }
Copy the code
- Object to Map
function objToStrMap(obj) {
let strMap = new Map();
for (let k of Object.keys(obj)) {
strMap.set(k, obj[k]);
}
return strMap;
}
objToStrMap({yes: true, no: false})
// Map {"yes"= >true."no"= >false}
Copy the code
- Converting a Map to JSON There are two types of converting a Map to JSON. In one case, Map keys are all strings, and you can choose to convert to object JSON.
function strMapToJson(strMap) {
return JSON.stringify(strMapToObj(strMap));
}
let myMap = new Map().set('yes'.true).set('no'.false);
strMapToJson(myMap)
// '{"yes":true,"no":false}'
Copy the code
Alternatively, if the Map has a non-string key name, you can choose to convert it to an array JSON.
function mapToArrayJson(map) {
return JSON.stringify([...map]);
}
let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
mapToArrayJson(myMap)
// '[[true,7],[{"foo":3},["abc"]]]'
Copy the code
- JSON to Map When JSON is converted to Map, all key names are strings.
function jsonToStrMap(jsonStr) {
return objToStrMap(JSON.parse(jsonStr));
}
jsonToStrMap('{"yes": true, "no": false}')
// Map {'yes'= >true.'no'= >false}
Copy the code
However, there is a special case where the entire JSON is an array, and each array member itself is an array with two members. In this case, it can be converted into a Map. This is often the inverse of an array to JSON.
function jsonToMap(jsonStr) {
return new Map(JSON.parse(jsonStr));
}
jsonToMap('[[true,7],[{"foo":3},["abc"]]]')
// Map {true => 7, Object {foo: 3} => ['abc']}
Copy the code
WeakMap syntax (also not used)
WeakMap has only four methods available: get(), set(), has(), and delete().
Cannot be traversed because there is no size. Cannot be cleared because there is no clear(), similar to WeakSet.
A typical WeakMap application
let myElement = document.getElementById('logo');
let myWeakmap = new WeakMap();
myWeakmap.set(myElement, {timesClicked: 0});
myElement.addEventListener('click'.function() {
let logoData = myWeakmap.get(myElement);
logoData.timesClicked++;
}, false);
Copy the code
In the above code, myElement is a DOM node whose state is updated every time a click event occurs. We put this state in WeakMap as a key value, and the corresponding key name is myElement. Once the DOM node is removed, the state disappears automatically, with no risk of memory leaks.
Chapter 11 Proxy
Proxy is used to modify the default behavior of some operations, which is equivalent to making changes at the language level. Therefore, it is a kind of “metaprogramming”, that is, programming a programming language.
Proxy can be understood as a layer of “interception” before the target object. All external access to the object must pass this layer of interception. Therefore, Proxy provides a mechanism for filtering and rewriting external access. The word Proxy is used to mean that it acts as a Proxy for certain operations. Vue3.0 uses a proxy
var obj = new Proxy({}, {
get: function (target, key, receiver) {
console.log(`getting ${key}! `);return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
console.log(`setting ${key}! `);returnReflect.set(target, key, value, receiver); }}); // Target represents the data to be intercepted // key represents the property to be intercepted // value represents the value of the property to be intercepted // receiver represents Proxy{} //setObj. count = 1 // setting count! ++obj.count // getting count! // setting count! / / 2Copy the code
The code above shows that the Proxy actually overrides the point operator by overwriting the original definition of the language with its own definition.
ES6 natively provides a Proxy constructor to generate a Proxy instance.
letproxy = new Proxy(target, handler); All uses of Proxy objects are in this form, except for the handler arguments. The new Proxy() parameter represents the generation of a Proxy instance, the target parameter represents the target object to intercept, and the handler parameter is also an object used to customize the interception behavior.Copy the code
All uses of Proxy objects are in this form, except for the handler arguments. The new Proxy() parameter represents the generation of a Proxy instance, the target parameter represents the target object to intercept, and the handler parameter is also an object used to customize the interception behavior.
var proxy = new Proxy({}, {
get: function(target, property) {
return35. }}); Proxy. time = 10 proxy.time // 35 // Intercepts all the fetch attributes and returns 35 proxy.name // 35 proxy.title // 35Copy the code
If the handler does not set up any interceptions, that is equivalent to going straight to the original object.
var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
proxy.a = 'b';
target.a // "b"
Copy the code
In the code above, handler is an empty object with no interception effect, and accessing proxy is equivalent to accessing target.
The same interceptor function can be set to intercept more than one operation.
For actions that can be set, but no interception is set, they fall directly on the target object and produce the result as before.
The following is a list of 13 interception operations supported by Proxy:
- Get (target, propKey, receiver) : Intercepts reading of object properties, such as proxy.foo and proxy[‘foo’].
- Set (target, propKey, value, receiver) : Intercepts the setting of object properties, such as proxy.foo = v or proxy[‘foo’] = v, and returns a Boolean value.
- Has (target, propKey) : Intercepts the propKey in proxy operation and returns a Boolean value.
- DeleteProperty (target, propKey) : Intercepts the operation of delete Proxy [propKey] and returns a Boolean value.
- OwnKeys (target) : interception Object. GetOwnPropertyNames (proxy), Object. GetOwnPropertySymbols (proxy), the Object. The keys (proxy), and returns an array. This method returns the property names of all of the target Object’s own properties, whereas object.keys () returns only the traversable properties of the target Object itself.
- GetOwnPropertyDescriptor (target, propKey) : interception Object. GetOwnPropertyDescriptor (proxy, propKey), returns the attributes describe objects.
- DefineProperty (target, propKey propDesc) : Intercepts Object.defineProperty(proxy, propKey, propDesc), Object.defineProperties(proxy, propDescs), and returns a Boolean value.
- PreventExtensions (target) : Intercepts Object.preventExtensions(proxy), returns a Boolean.
- GetPrototypeOf (target) : Intercepts object.getProtoTypeof (proxy) and returns an Object.
- IsExtensible (Target) : Intercepts Object. IsExtensible (proxy), returning a Boolean value.
- SetPrototypeOf (target, proto) : Intercepts Object.setPrototypeOf(proxy, proto) and returns a Boolean value. If the target object is a function, there are two additional operations that can be intercepted.
- Apply (target, object, args) : intercepts operations called by Proxy instances as functions, such as Proxy (… The args), proxy. Call (object,… The args), proxy. Apply (…). .
- Construct (target, args) : intercepts operations called by Proxy instances as constructors, such as new Proxy (… The args).
Such as:
The deleteProperty method intercepts the DELETE operation. If the method throws an error or returns false, the current property cannot be deleted by the delete command.
The Apply method intercepts function calls, calls, and apply operations.
The get method is used to intercept a read of a property.
let obj2 = new Proxy(obj,{
get(target,property,a){
//return 35;
/*console.log(target)
console.log(property)*/
letNum = ++wkMap.get(obj).getPropertyNum; Console. log(' The current number of times to access object properties is:${Num}`)
return target[property]
},
deleteProperty(target,property){
return false;
},
apply(target,ctx,args){
return Reflect.apply(...[target,[],args]);;
}
})
Copy the code
Proxy.revocable()
The proxy. revocable method returns a cancelable Proxy instance.
let target = {};
let handler = {};
let {proxy, revoke} = Proxy.revocable(target, handler);
proxy.foo = 123;
proxy.foo // 123
revoke();
proxy.foo // TypeError: Revoked
Copy the code
The proxy. revocable method returns an object whose Proxy property is a Proxy instance. Revoke property is a function that cancels a Proxy instance. In the above code, when accessing the Proxy instance after executing revoke, an error is thrown.
One use scenario for proxy.revocable is where the target object is not allowed to be accessed directly, but must be accessed through a Proxy, and once the access is complete, the Proxy is revoked and no further access is allowed.
This problem
Although Proxy can Proxy the access of the target object, it is not a transparent Proxy of the target object, that is, without any interception, it cannot guarantee the consistency with the behavior of the target object. The main reason is that in the case of Proxy, the this keyword inside the target object points to Proxy.
const target = {
m: function() { console.log(this === proxy); }}; const handler = {}; const proxy = new Proxy(target, handler); target.m() //false
proxy.m() // true// Once the proxy represents target.m, this inside the latter refers to the proxy, not target.Copy the code
Chapter 12 Promise (Key Chapter)
concept
Promise is a solution to asynchronous programming that makes more sense and is more powerful than traditional solutions — callback functions and events.
A Promise, then, is simply a container that holds the result of some event (usually an asynchronous operation) that will end in the future.
The characteristics of
- The status of an object is not affected.
- Once the state changes, it never changes again, and you can get this result at any time.
state
The Promise object represents an asynchronous operation with three states:
This is a big pity (pending), which is postponed and rejected (failed).
Only the result of an asynchronous operation can determine the current state, and no other operation can change the state.
disadvantages
- There is no way to cancel a Promise, which is executed as soon as it is created and cannot be cancelled halfway through.
- If the callback function is not set, errors thrown inside a Promise are not reflected outside.
- When in a pending state, there is no way to know what stage of progress is currently in (just started or about to be completed)
usage
Writing JS is certainly no stranger to asynchronous events.
settimeout(()=>{
console.log("123")
},0)
console.log("abc") // Who is output first?Copy the code
I don’t think I have to say the answer, we all know
What if ABC needs to output after 123?
Sure, you can use callback, but it’s a hopeless thing to use.
Then: Promise, an object made for asynchronous programming, comes out….
letP = new Promise((resolve,reject)=>{// Some asynchronous actionssetTimeout(()=>{
console.log("123")
resolve("abc");
reject("I'm an error message.")
},0)
})
.then(function(data){//resolve state console.log(data)},function(err){//reject Status console.log(err)}) //'123'
//'abc'// I am an error messageCopy the code
You should have two questions:
1. What’s the use of wrapping such a function?
2.resolve(‘123’); Is this made of dry wool?
After the Promise instance is generated, you can use the THEN method to specify the resolved and Rejected state callback functions, respectively.
That is, the state is determined by the execution of the argument (function) at instantiation time, depending on the state, whether the first argument or the second one needs to go then.
The arguments to resolve() and reject() are passed to the corresponding callback’s data or ERR
Then returns a new Promise instance, which means you can continue then
The use of chain operation
Therefore, on the surface, Promise can only simplify the writing method of layer upon layer callback. In essence, the essence of Promise is “state”, which can be invoked in time by maintaining state and transferring state. It is much simpler and more flexible than passing callback function. So the correct scenario for using promises looks like this:
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return runAsync3();
})
.then(function(data){ console.log(data); }); Asynchronous task 1 executed completed // Random data 1 // Asynchronous task 2 executed completed // Random data 2 // Asynchronous task 3 executed completed // Random data 3Copy the code
RunAsync1, runAsync2, and runAsync3 look like this ↓
function runAsync1(){
var p = new Promise(functionResolve, reject){// Do some asynchronous operationssetTimeout(function(){
console.log('Asynchronous task 1 completed');
resolve('Whatever data 1');
}, 1000);
});
return p;
}
function runAsync2(){
var p = new Promise(functionResolve, reject){// Do some asynchronous operationssetTimeout(function(){
console.log('Asynchronous Task 2 completed');
resolve('Any data 2');
}, 2000);
});
return p;
}
function runAsync3(){
var p = new Promise(functionResolve, reject){// Do some asynchronous operationssetTimeout(function(){
console.log('Asynchronous Task 3 completed');
resolve('Any data 3');
}, 2000);
});
return p;
}
Copy the code
You can also return data directly in the THEN method instead of the Promise object, and you can also receive data in subsequent THEN methods:
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return 'Return data directly'; }).then(function(data){ console.log(data); }); Asynchronous task 1 completed // Random data 1 // asynchronous task 2 completed // random data 2 // directly return dataCopy the code
Reject the use of the
Reject sets the Promise state to Rejected, so we can catch the failed callback in THEN.
let num = 10;
let p1 = function() {
return new Promise((resolve,reject)=>{
if (num <= 5) {
resolve("< = 5, go resolce")
console.log('Resolce does not end Promise')}else{
reject("Go > 5, reject")
console.log('reject does not end Promise')
}
})
}
p1()
.then(function(data){
console.log(data)
},function(err){console.log(err)}) //reject Cannot end Promise //>5, rejectCopy the code
Resolve and reject are always executed at the end of the current environment, so subsequent synchronization code is executed first.
If there is code to execute after resolve and reject, it is best to put it in then.
Then write return before resolve and reject.
Promise.prototype.catch()
The promise.prototype. catch method is an alias for. Then (null, Rejection) that specifies the callback when an error occurs.
P1 ().then()function(data){
console.log(data)
})
.catch(function(err){console.log(err)}) //reject Cannot end Promise //>5, rejectCopy the code
Promise.all()
The promise.all method is used to wrap multiple Promise instances into a new Promise instance.
const p = Promise.all([p1, p2, p3]);
Copy the code
The state of P is determined by P1, P2 and P3, which can be divided into two cases.
- This is a big pity. Only when the states of P1, P2 and P3 become a big pity, the state of P will become a big pity. The return values of P1, P2, and p3 form an array and are passed to p’s callback function.
- If p1, P2, and P3 are rejected, P becomes rejected, and the return value of the first rejected instance is passed to p’s callback function.
Promises is an array of three Promise instances. Only when the states of these three instances become fulfilled or one of them becomes Rejected, will the callback function behind Promise.
If the promise.all () method is rejected, the promise.all () method will not be invoked. If the promise.all () method is rejected, the promise.all () method will be invoked.
Promise.race()
The promise.race method also wraps multiple Promise instances into a new Promise instance.
const p = Promise.race([p1, p2, p3]); // If the first instance of p1, p2, p3 changes state, the state of p will change. // The return value of the first changed Promise instance is passed to p's callback.Copy the code
Promise.resolve()
Sometimes you need to turn an existing object into a Promise object, and the promise.resolve method does this.
const jsPromise = Promise.resolve('123');
Copy the code
The code above turns 123 into a Promise object.
Promise. Resolve is equivalent to the following
Promise.resolve('123'New Promise(resolve => resolve();'123'))
Copy the code
The parameters of the promise.resolve method are divided into four cases.
- The argument is an instance of Promise
- The argument is a Thenable object
Thenable objects refer to objects that have then methods, such as this one.
let thenable = {
then: function(resolve, reject) { resolve(42); }};Copy the code
The promise. resolve method converts this object to a Promise, and then immediately executes the thenable object’s then method.
let thenable = {
then: function(resolve, reject) { resolve(42); }};let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});
Copy the code
As soon as the thenable object’s THEN method executes, object P1 will be in the resolved state and the callback specified by the last THEN method will execute immediately, printing 42.
- Parameters are not objects with THEN methods, or are not objects at all
If the parameter is a raw value, or an object that does not have a then method, the promise.resolve method returns a new Promise object with the state Resolved.
const p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});
// Hello
Copy the code
The code above generates a new instance P of the Promise object. Since the string Hello is not an asynchronous operation (the string object does not have the then method), the return Promise instance state from lifetime achievement is Resolved, so the callback will execute immediately. The arguments to the promise. resolve method are also passed to the callback function.
- It takes no parameters
The Promise. Resolve method allows you to call an Resolved Promise object with no arguments.
So, if you want to get a Promise object, it’s convenient to call the promise.resolve method directly.
const p = Promise.resolve();
p.then(function() {/ /... });Copy the code
The variable P in the code above is a Promise object.
Note that the Promise object for Resolve now is at the end of this event loop, not at the beginning of the next.
setTimeout(function () {
console.log('three');
}, 0);
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
// one
// two
// three
Copy the code
In the code above, setTimeout(fn, 0) is executed at the start of the next event loop, promise.resolve () is executed at the end of the current event loop, and console.log(‘one’) is executed immediately, so it is printed first.
Promise.reject()
The promise.Reject (Reason) method also returns a new Promise instance with a state of Rejected.
const p = Promise.reject('Wrong'); // const p = new Promise((resolve, reject) => reject('Wrong'))
p.then(null, function(s) { console.log(s) }); / / make a mistakeCopy the code
The above code generates an instance P of the Promise object in the rejected state, and the callback is executed immediately.
Note that the arguments to the promise.reject () method are left as reject arguments to subsequent methods. This is inconsistent with the promise.resolve method.
const thenable = {
then(resolve, reject) {
reject('Wrong'); }}; Promise.reject(thenable) .catch(e => { console.log(e === thenable) }) //true
Copy the code
Reject takes a Thenable object as the argument to the promise. reject method, which takes a Thenable object instead of the “something’s wrong” string thrown by Reject.
Chapter 13 Iterator
#### Concept iterators are interfaces and mechanisms.
Provides a unified access mechanism for various data structures. The Iterator interface can be deployed on any data structure to complete traversal (that is, processing all members of the data structure in turn).
Iterator does three things:
- To provide a unified and simple access interface for various data structures;
- To enable the members of a data structure to be arranged in some order;
- Mainly used for the for… Of consumption.
Iterator is essentially a pointer object.
Here’s how it works:
(1) Create a pointer object that points to the starting position of the current data structure.
(2) The first call to the next method of the pointer object can point to the first member of the data structure.
(3) The next call to the pointer object points to the second member of the data structure.
(4) Keep calling the next method of the pointer object until it points to the end of the data structure.
- Ordinary functions implement Iterator
function myIter(obj){
let i = 0;
return {
next() {let done = (i>=obj.length);
let value = !done ? obj[i++] : undefined;
return {
value,
done,}}}}Copy the code
The data structures with the native Iterator interface are as follows.
- Array
- Map
- Set
- String
- The arguments object for the function
- The NodeList object
The following example is the Symbol. Iterator property of an array.
let arr = ['a'.'b'.'c'];
let iter = arr[Symbol.iterator]();
iter.next() // { value: 'a'.done: false }
iter.next() // { value: 'b'.done: false }
iter.next() // { value: 'c'.done: false }
iter.next() // { value: undefined, done: true }
Copy the code
Here is another example of an array-like object calling the symbol. iterator method of an array.
let iterable = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
console.log(item); // 'a'.'b'.'c'
}
Copy the code
Note that a normal object deploys the symbol. iterator method of an array with no effect.
let iterable = {
a: 'a',
b: 'b',
c: 'c',
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
console.log(item); // undefined, undefined, undefined
}
Copy the code
A string is an array-like object that also natively has an Iterator interface.
var someString = "hi";
typeof someString[Symbol.iterator]
// "function"
var iterator = someString[Symbol.iterator]();
iterator.next() // { value: "h".done: false }
iterator.next() // { value: "i".done: false }
iterator.next() // { value: undefined, done: true }
Copy the code
Chapter 14 Generator
The basic concept
Generator functions are an asynchronous programming solution provided by ES6 with completely different syntactic behavior from traditional functions.
Execution of Generator returns an traverser object, that is, Generator is also an traverser object Generator. A traverser object that iterates through each state within the Generator in turn.
- The difference with ordinary functions
- There is an asterisk between the function keyword and the function name;
- Inside the function body, yield expressions are used to define different internal states.
- Generator functions cannot be used with new and an error is reported.
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
Copy the code
The above code defines a Generator function helloWorldGenerator with two yield expressions (hello and world) inside, that is, the function has three states: hello, world, and return statements (end execution).
When a Generator function is called, it does not execute and returns not the result of the function’s execution, but a pointer object to the internal state, the traverser object.
Next, the next method of the traverser object must be called to move the pointer to the next state. That is, each time the next method is called, the internal pointer executes from the head of the function or where it was last stopped until the next yield expression (or return statement) is encountered. In other words, Generator functions are executed piecewise, yield expressions are paused tokens, and the next method can resume execution.
ES6 does not specify where the asterisk between the function keyword and the function name should be placed. This leads to the passage of all of the following.
functionFoo (x, y) {···}functionFoo (x, y) {···}functionFoo (x, y) {···}functionFoo (x, y) {···}Copy the code
Yield expression
Because the Generator returns a traverser object that only calls to the next method iterate over the next internal state, it actually provides a function to pause execution. The yield expression is the pause flag.
The next method of the traverser object runs as follows.
(1) When a yield expression is encountered, the following operation is paused, and the value immediately following the yield expression is used as the value of the returned object’s value property.
(2) The next time the next method is called, the execution continues until the next yield expression is encountered.
(3) If no new yield expression is encountered, the function is run until the end of the return statement, and the value of the expression following the return statement is used as the value of the returned object’s value property.
(4) If the function does not have a return statement, the value attribute of the returned object is undefined.
Yield expressions are similar to return statements
Can return the value of the expression immediately following the statement.
The difference
At each yield, the function suspends execution and continues backward from that location the next time, while the return statement has no location memory. A function can execute only once (or one) return statements, but can execute multiple (or multiple) yield expressions. A normal function can only return one value because it can only return once; Generator functions can return a series of values because there can be any number of yields.
Note:
Yield expressions can only be used in Generator functions and will generate an error if used elsewhere.
In addition, if the yield expression is used in another expression, it must be enclosed in parentheses.
console.log('Hello' + yield 123); // SyntaxError
console.log('Hello' + (yield 123)); // OK
Copy the code
Relationship to the Iterator interface
Since a Generator function is an iterator Generator function, you can assign Generator to the Symbol. Iterator property of an object so that the object has an Iterator interface.
Object.prototype[Symbol.iterator] = function* () {for(let i inthis){ yield this[i]; }} / / -- -- -- -- -- -- -- -- -- -- -- -- -- -function* iterEntries(obj) {
let keys = Object.keys(obj);
for (let i=0; i < keys.length; i++) {
letkey = keys[i]; yield [key, obj[key]]; }}let myObj = { foo: 3, bar: 7 };
for (let [key, value] of iterEntries(myObj)) {
console.log(key, value);
}
Copy the code
Parameters to the next method
function* f() {
for(var i = 0; true; i++) {
var reset = yield i;
if(reset) { i = -1; }
}
}
var g = f();
g.next() // { value: 0, done: false }
g.next() // { value: 1, done: false }
g.next(true) // { value: 0, done: false }
Copy the code
This feature has important syntactic implications.
The context state of a Generator function does not change from its paused state to its resumed state. With the parameters of the next method, there is a way to continue injecting values into the function body after the Generator function has started running.
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}
var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }
Copy the code
for… Of circulation
for… The of loop automatically iterates over the Iterator generated by the Generator function, and there is no need to call the next method.
function *foo() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return 6;
}
for (let v of foo()) {
console.log(v);
}
// 1 2 3 4 5
Copy the code
function* fibonacci() {
let [prev, curr] = [1, 1];
while(true){ [prev, curr] = [curr, prev + curr]; yield curr; }}for (let n of fibonacci()) {
if (n > 10000000) break;
console.log(n);
}
Copy the code
Generator.prototype.return()
The traverser object returned by the Generator function, and a return method that returns the given value and terminates the traversal of the Generator function.
function* gen() {
yield 1;
yield 2;
yield 3;
}
var g = gen();
g.next() // { value: 1, done: false }
g.return('foo') // { value: "foo".done: true }
g.next() // { value: undefined, done: true }
Copy the code
yield*
By default, calling another Generator function from within a Generator function has no effect.
function* foo() {
yield 'a';
yield 'b';
}
function* bar() {
yield 'x';
foo();
yield 'y';
}
for (letv of bar()){ console.log(v); } / /"x"
// "y"
Copy the code
Foo and bar are both Generator functions, and calling foo from inside bar has no effect.
This requires the yield* expression to execute a Generator function within another Generator function.
function* bar() {
yield 'x';
yield* foo();
yield 'y'; } // is equivalent tofunction* bar() {
yield 'x';
yield 'a';
yield 'b';
yield 'y'; } // is equivalent tofunction* bar() {
yield 'x';
for (let v of foo()) {
yield v;
}
yield 'y';
}
for (letv of bar()){ console.log(v); } / /"x"
// "a"
// "b"
// "y"
Copy the code
Let’s look at another example of comparison.
function* inner() {
yield 'hello! ';
}
function* outer1() {
yield 'open';
yield inner();
yield 'close';
}
var gen = outer1()
gen.next().value // "open"Gen.next ().value // Returns a traverser object gen.next().value //"close"
function* outer2() {
yield 'open'
yield* inner()
yield 'close'
}
var gen = outer2()
gen.next().value // "open"
gen.next().value // "hello!"
gen.next().value // "close"
Copy the code
In the preceding example, yield* is used for outer2, but outer1 is not. As a result, outer1 returns a traverser object, and outer2 returns the internal value of that traverser object.
From a syntactic point of view, if a yield expression is followed by an traverser object, you need to add an asterisk after the yield expression to indicate that it returns an traverser object. This is called the yield* expression.
A Generator function as an object property
If the property of an object is a Generator function, you can abbreviate it as follows.
let obj = {
* myGeneratorMethod() {···}};Copy the code
To be honest, after learning async await, Generator functions are basically useless
Chapter 15 Async functions
The ES2017 standard introduces async functions to make asynchronous operations more convenient.
Async functions are syntactic sugar for Generator functions.
What is grammar sugar?
It refers to grammars that don’t add new features to computer languages, but are just sweeter for humans. Syntactic sugar often gives programmers a more practical way to code, makes for a better coding style, and makes it easier to read. But it doesn’t add anything new to the language.
There is syntax salt in reverse:
The main goal is to make writing code more painful by using antihuman syntax. While it does the same thing in terms of avoiding errors, it offsets the intensity of programming because it sets the threshold for learning much lower.
Async functions are used by replacing the asterisk (*) of Generator functions with async and yield with await, and nothing more.
Async functions differ from Generator functions:
(1) Built-in actuators.
Generator functions must be executed by an executor, whereas async functions come with an executor. In other words, async functions are executed exactly like normal functions, with only one line.
(2) Better semantics.
Async and await are semantic clearer than asterisks and yield. Async means that there is an asynchronous operation in a function, and await means that the following expression needs to wait for the result.
(3) Normally, an await command is followed by a Promise object. If not, it is cast into an immediately resolve Promise object.
(4) Return the Promise.
Async functions return a Promise object, which is much more convenient than Generator functions returning an Iterator. You can specify what to do next using the then method.
Further, async functions can be thought of as multiple asynchronous operations wrapped as a Promise object, and await commands are syntactic sugar for internal THEN commands.
Error handling
If an asynchronous operation following await fails, the Promise object returned by the async function is rejected.
async function f() {
await new Promise(function (resolve, reject) {
throw new Error('Wrong'); }); } f().then(v => console.log(v)).catch(e => console.log(e)) // Error: an Error occurredCopy the code
In the above code, after async f is executed, the Promise object behind await will throw an error object, causing the callback of the catch method to be called with the argument of the thrown error object. For specific execution mechanism, please refer to “Implementation Principle of Async Function” later.
The way to prevent errors is to put it in a try… Inside the catch block.
async function f() {
try {
await new Promise(function (resolve, reject) {
throw new Error('Wrong');
});
} catch(e) {
}
return await('hello world');
}
Copy the code
If you have more than one await command, you can put the try… Catch structure.
async function main() {
try {
const val1 = await firstStep();
const val2 = await secondStep(val1);
const val3 = await thirdStep(val1, val2);
console.log('Final: ', val3); } catch (err) { console.error(err); }}Copy the code
application
var fn = function (time) {
console.log("Start handling asynchrony");
setTimeout(function () {
console.log(time);
console.log("Asynchronous processing complete");
iter.next();
}, time);
};
function* g(){
console.log("start");
yield fn(3000)
yield fn(500)
yield fn(1000)
console.log("end");
}
let iter = g();
iter.next();
Copy the code
Here is how async functions are written
var fn = function (time) {
return new Promise(function (resolve, reject) {
console.log("Start handling asynchrony");
setTimeout(function () {
resolve();
console.log(time);
console.log("Asynchronous processing complete"); }, time); })}; var start = asyncfunction() {// Console.log () {// Console.log () {// Console.log ('start');
await fn(3000);
await fn(500);
await fn(1000);
console.log('end');
};
start();
Copy the code
Chapter 16 Class (focus on this, easy to use thief, MY project mostly use this)
Class is the same as let and const: there is no variable promotion, no repeated declaration…
Es5’s object-oriented writing is so different from traditional object-oriented languages such as C++ and Java that it can easily confuse programmers new to the language.
ES6 provides a more traditional language approach, introducing the concept of classes as templates for objects. With the class keyword, you can define a class.
ES6 classes can be seen as just a syntactic candy. ES5 can do most of the things it does. The new class writing method just makes object prototype writing clearer and more like object-oriented programming syntax.
//es5
function Fn(x, y) {
this.x = x;
this.y = y;
}
Fn.prototype.add = function () {
returnthis.x + this.y; }; Constructor (x,y){this.x = x; this.y = y; }add() {return this.x + this.y;
}
}
var F = new Fn(1, 2);
console.log(F.add()) //3
Copy the code
The prototype property of the constructor continues on the ES6 “class”. In fact, all methods of a class are defined on the prototype property of the class.
class Fn {
constructor() {/ /... }add() {/ /... }sub() {/ /... }} // Fn. Prototype = {constructor() {},
add() {},
sub() {}};Copy the code
All methods defined inside a class are non-enumerable, unlike in ES5.
//es5
var Fn = function (x, y) {
// ...
};
Point.prototype.add = function() {/ /... }; Object.keys(Fn.prototype) // ["toString"]
Object.getOwnPropertyNames(Fn.prototype)
// ["constructor"."add"]
//es6
class Fn {
constructor(x, y) {
// ...
}
add() {/ /... } } Object.keys(Fn.prototype) // [] Object.getOwnPropertyNames(Fn.prototype) // ["constructor"."add"]
Copy the code
Strict mode
Inside classes and modules, the default is strict mode, so there is no need to use strict to specify the runtime mode. As long as your code is written in a class or module, only strict mode is available.
Considering that all future code will actually run in modules, ES6 actually upgrades the entire language to strict mode.
constructor
The onstructor method is the default method of the class and is automatically called when an object instance is generated using the new command. A class must have a constructor method; if it is not explicitly defined, an empty constructor method is added by default.
Class Fn {} // same as class Fn {constructor() {}}Copy the code
The constructor method returns the instance object (that is, this) by default; you can specify to return another object at all.
class Foo {
constructor() {
return Object.create(null);
}
}
new Foo() instanceof Foo
// false// The constructor function returns a brand new object, resulting in the instance object not being an instance of class Foo.Copy the code
Classes must be called with new
The class must be called with new or an error will be reported. This is a major difference from normal constructors, which can be executed without new.
class Foo {
constructor() {
return Object.create(null);
}
}
Foo()
// TypeError: Class constructor Foo cannot be invoked without 'new'
Copy the code
The Class expressions
Like functions, classes can be defined in the form of expressions.
const MyClass = class Me {
getClassName() {
returnMe.name; }};Copy the code
The above code defines a class using an expression. Note that the name of this Class is MyClass, not Me, which is only available inside the Class code and refers to the current Class.
let inst = new MyClass();
inst.getClassName() // Me
Me.name // ReferenceError: Me is not defined
Copy the code
If the inner part of the class is not useful, you can omit Me, which can be written as follows.
const MyClass = class { /* ... */ };
Copy the code
Using Class expressions, you can write classes that are executed immediately.
let person = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}('Joe');
person.sayName(); // "Zhang"
Copy the code
In the code above, Person is an instance of a class that executes immediately.
Private methods and private properties
Private methods/private properties are common requirements that ES6 does not provide and can only be simulated through workarounds. (It will come later)
It is usually distinguished by nomenclature.
Class Fn {// Public methodfoo() {/ /... } // pretend to be private._bar() {/ /... }}Copy the code
Attributes of stereotypes
When class defines a class, attributes can only be defined in constructor; errors are reported elsewhere.
If you need to define methods on a prototype, you can use:
- Fn.prototype.prop = value;
- Object.getprototypeof () gets the prototype and extends it
- Object.assign(Fn. Prototype,{write extended properties/methods in here})
Static method of Class
A class is the prototype of an instance, and all methods defined in a class are inherited by the instance.
If you prefix a method with the static keyword, it means that the method is not inherited by the instance, but is called directly from the class. This is called a “static method”.
ES6 explicitly states that there are only static methods inside a Class and no static attributes.
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function// Static properties can only be set manually class Foo {} foo.prop = 1; Foo.prop // 1Copy the code
The get and set
class Fn{
constructor(){
this.arr = []
}
get bar() {return this.arr;
}
set bar(value){
this.arr.push(value)
}
}
letobj = new Fn(); obj.menu = 1; obj.menu = 2; The console. The log (obj. Menu) / / [1, 2] the console. The log (obj. Arr) / / [1, 2]Copy the code
inheritance
- usage
class Fn {
}
class Fn2 extends Fn {
}
Copy the code
- Pay attention to
Subclasses must call the super method from the constructor method or they will get an error when creating a new instance. This is because the subclass does not have its own This object, but inherits the parent’s This object and then processes it. If you don’t call super, your subclasses don’t get this.
class Point { /* ... */ }
class ColorPoint extends Point {
constructor() {super()// must call}}let cp = new ColorPoint(); // ReferenceError
Copy the code
Static methods of the parent class are also inherited.
Object.getPrototypeOf()
The object.getProtoTypeof method can be used to get a parent class from a subclass.
Object.getPrototypeOf(Fn2) === Fn
// true
Copy the code
Therefore, you can use this method to determine whether a class inherits from another class.
The super keyword
The super keyword can be used as either a function or an object. In both cases, it’s used quite differently.
In the first case, super, when called as a function, represents the constructor of the parent class. ES6 requires that the constructor of a subclass must execute the super function once.
When used as a function, super() can only be used in the constructor of a subclass, otherwise an error will be reported.
class A {}
class B extends A {
constructor() { super(); }}Copy the code
In the code above, super() in the constructor of subclass B stands for calling the constructor of the parent class. This is necessary, otherwise the JavaScript engine will report an error.
Note that super although represents the parent class constructor, but returns the instance of the subclass B, namely the inside of the super this refers to B, so super () is equivalent to Amy polumbo rototype here. The constructor. Call (this).
In the second case, super as an object, in a normal method, points to a prototype object of the parent class; In static methods, point to the parent class.
class A {
p() {
return 2;
}
}
class B extends A {
constructor() { super(); console.log(super.p()); / / 2}}let b = new B();
Copy the code
In the code above, super.p() in subclass B uses super as an object. In this case, super refers to a.prototype in normal methods, so super.p() equals a.prototype.p ().
Since this refers to a subclass, if you assign a value to a property via super, which is this, the assigned property becomes the property of the subclass instance.
class A {
constructor() {
this.x = 1;
}
}
class B extends A {
constructor() { super(); this.x = 2; super.x = 3; console.log(super.x); // undefined console.log(this.x); / / 3}}let b = new B();
Copy the code
In the code above, assigning super.x to 3 is the same as assigning this.x to 3. When super.x is read, a.prototype. x is read, so return undefined.
Chapter 17 Module
ES6 modules automatically adopt strict mode, whether or not you add “Use strict” to the module header; .
The export command
The function of the module consists of two commands: export and import.
The export command is used to specify the external interface of a module.
The import command is used to input functions provided by other modules.
A module is an independent file. All variables inside the file are not available externally. If you want outsiders to be able to read a variable inside a module, you must use the export keyword to output that variable.
Export output variable
// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
Copy the code
You can also:
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export{firstName, lastName, year}; // This is equivalent to the above.Copy the code
The export command can output functions or classes as well as variables.
export function multiply(x, y) {
return x * y;
};
Copy the code
Normally, the variable output by export is the original name, but can be renamed using the AS keyword.
function v1() {... }function v2() {... }export {
v1 as streamV1,
v2 as streamV2,
v2 as streamLatestVersion
};
Copy the code
It should be noted that the export command specifies the external interface and must establish a one-to-one correspondence with variables inside the module.
/ / an errorexport1; Var m = 1;exportm; // Write it correctly // write it correctlyexportvar m = 1; Var m = 1;export{m}; Var n = 1;export {n as m};
Copy the code
Similarly, the output of function and class must be written this way.
/ / an errorfunction f() {}
exportf; / / rightexport function f() {}; / / rightfunction f() {}
export {f};
Copy the code
The interface output by the export statement is dynamically bound to its corresponding value, that is, the real-time value inside the module can be obtained through this interface.
export var foo = 'bar';
setTimeout(() => foo = 'baz', 500);
Copy the code
The above code outputs a variable foo with a value of bar, which becomes baz after 500 milliseconds. The export command can appear anywhere in the module, as long as it is at the top of the module. If you are in block-level scope, an error is reported, as is the import command.
The import command
After the module’s external interface is defined using the export command, other JS files can load the module using the import command.
// main.js
import {firstName, lastName, year} from './profile';
function setName(element) {
element.textContent = firstName + ' ' + lastName;
}
Copy the code
The import command of the above code is used to load the profile.js file and enter variables from it. The import command accepts a pair of curly braces specifying the name of a variable to import from another module. The variable name inside the braces must be the same as the name of the external interface of the imported module (profile.js).
If you want to rename the input variable, the import command uses the as keyword to rename the input variable
import { lastName as surname } from './profile';
Copy the code
The from after import specifies the location of the module file, which can be relative or absolute, and the.js suffix can be omitted.
Note that the import command is promoted to the top of the module and executed first.
foo();
import { foo } from 'my_module'; // Import is executed before foo is called. The essence of this behavior is that the import command is executed at compile time, before the code runs.Copy the code
Because import is executed statically, you cannot use expressions and variables, which are syntactic structures that are only available at run time.
// error: import {'f' + 'oo' } from 'my_module'; / / an errorlet module = 'my_module'; import { foo } from module; / / an errorif (x === 1) {
import { foo } from 'module1';
} else {
import { foo } from 'module2';
}
Copy the code
import { foo } from 'my_module';
import { bar } from 'my_module'; // Equivalent to import {foo, bar} from'my_module';
Copy the code
Overall loading of modules
In addition to specifying that an output value is loaded, you can also use global loading, where you specify an object with an asterisk (*) on which all output values are loaded.
Note that the object from which the module is loaded as a whole is not allowed to change at runtime. None of the following is allowed.
import * as circle from './circle'; // The following two lines are disallowed circles.foo ='hello';
circle.area = function () {};
Copy the code
export default
When using the import command, the user needs to know the name of the variable or function to be loaded; otherwise, the command cannot be loaded.
To make it easier for users to load the module without reading the documentation, use the export default command to specify the default output for the module.
// export-default.js
export default function () {
console.log('foo');
}
Copy the code
The import command can specify any name for the anonymous function when the module is loaded by other modules.
// import-default.js
import customName from './export-default';
customName(); // 'foo'
Copy the code
Note that curly braces are not used after the import command. The export default command can also be used in front of non-anonymous functions.
// export-default.js
export default function foo() {
console.log('foo'); } // or writefunction foo() {
console.log('foo');
}
export default foo;
Copy the code
In the above code, the function name foo of function foo is invalid outside the module. When loaded, it is treated as if an anonymous function is loaded.
Let’s compare the default output to the normal output.
/ / the first groupexport default function crc32() {// output //... } import crc32 from'crc32'; // Input // second groupexport function crc32() {// output //... }; import {crc32} from'crc32'; / / inputCopy the code
The above code is written in two groups. In the first group, when export default is used, the corresponding import statement does not need to use curly braces. The second group is when export default is not used, the corresponding import statement needs to use curly braces.
The export default command is used to specify the default output of the module. Obviously, a module can have only one default output, so the export default command can only be used once. Therefore, the import command is not followed by parentheses, because it can only correspond to the export default command.
Essentially, export default simply prints a variable or method called default, and the system allows you to call it whatever you want. So, the following is valid.
// modules.js
function add(x, y) {
return x * y;
}
export{add as default}; // is the same as //export default add;
// app.js
import { default as foo } from 'modules'; // is the same as // import foo from'modules';
Copy the code
Because the export default command simply prints a variable named default, it cannot be followed by a variable declaration statement.
/ / rightexportvar a = 1; Var a = 1;exportdefault a; / / errorexport default var a = 1;
Copy the code
In the above code, export default A means to assign the value of variable A to variable default. So, the last one will give you an error.
Similarly, because the essence of the export default command is to assign the following value to the default variable, you can write a value directly after export Default.
/ / rightexportdefault 42; / / an errorexport42.Copy the code
A compound of export and import
If the same module is input and then output within a module, the import statement can be written together with the export statement.
export { foo, bar } from 'my_module'; // Equivalent to import {foo, bar} from'my_module';
export { foo, bar };
Copy the code
The interface name and overall output of a module can also be written in this way.
// The interface was renamedexport { foo as myFoo } from 'my_module'; // Total outputexport * from 'my_module';
Copy the code
Above is my summary of ES6 notes, off and on for almost a week’s time, because I am in the project also use less than a lot of code, will be their first knock again see usage in the summary, a little busy in work overtime, and of course my notes is a summary of very fine, project many of them can be used to optimize the content of the code above, I hope my notes can also be helpful to you, please point out in the comments section if there is any mistake, thank you very much, if you feel useful to you, please pay attention to praise ha!!