let and const
In JavaScript, we used to use key var to define variables. After ES6, we added two keywords to define variables, let and const.
For variables, variables defined by var in ES5 will be raised to all functions and statements in scope, while variables defined by let in ES6 will not. Variables declared by let will establish a temporary dead zone in their corresponding code blocks until variables are declared.
Both let and const can declare block-level scopes, similar to var in usage, and let is characterized by being locked in the current block instead of raising variables.
A very simple example:
function test() { if(true) { console.log(a)//TDZ, commonly known as temporary dead zone, is used to describe the phenomenon of non-elevation of variables. let a = 1 } } test() // a is not defined function test() { if(true) { let a = 1 } console.log(a) } test() // a is not defined
The only correct way to use it is to declare it first and then visit it.
function test() { if(true) { let a = 1 console.log(a) } } test() // 1
const
Declare constants, once declared, unchangeable, and constants must initialize assignments.
Although const is a constant, it is not allowed to modify the default assignment, but if the Object is defined, then the attribute values inside the Object can be modified.
const type = { a: 1 } type.a = 2 //Instead of directly modifying the value of type, it is allowed to modify the attribute value of type.a. console.log(type) // {a: 2}
The similarities and differences between const and let
The same point: const and let are valid in the current block, and will be destroyed when executed out of the block. There is no variable escalation (TDZ) and cannot be repeated.
The difference: const can no longer be assigned, let declared variables can be assigned repeatedly.
const actually guarantees that the value of a variable cannot be changed, but that the data stored at the memory address to which the variable points must not be changed. For simple types of data (numeric, string, boolean), the value is stored at the memory address to which the variable points, so it is equivalent to a constant. But for complex data (mainly objects and arrays), the memory address of the variable points to is only a pointer to the actual data. const can only guarantee that the pointer is fixed (that is, it always points to another fixed address). As for whether the data structure it points to is changeable, it can not be controlled at all. Therefore, you must be very careful to declare an object as a constant.
Block-level scopes usage scenarios
In addition to the usual declarations mentioned above, we can also use one of the most famous interview questions in the loop: the timer closure test questions in the loop.
Using var declared loop variables in for loops will jump out of the loop body to pollute the current function.
for(var i = 0; i < 5; i++) { setTimeout(() => { console.log(i) //5, 5, 5, 5, 5 }, 0) } console.log(i) //5. i Jump Out of the External Function of Cyclic Body Pollution //After changing var to let for(let i = 0; i < 5; i++) { setTimeout(() => { console.log(i) // 0,1,2,3,4 }, 0) } console.log(i)//i. is not defined i cannot contaminate external functions
In actual development, we choose to use var, let or const, depending on whether our variables need to be updated. Usually we want to ensure that variables are not maliciously modified, but use a lot of const. Const declaration is also recommended when declaring an object. When you need to modify the declared variable value, let and VaR can be used instead of let.
symbol
Before ES6, we knew that five basic data types were Undefined, Null, Boolean, Number and String, and then added a reference type Object to make up all data types in JavaScript. But after ES6 came out, a new data type named symbol was added, which, like its name, means unique, meaning that every Symbol type is all. Unique, not duplicated with other Symbols.
You can create a new Symbol type value by calling the Symbol() method, which is unique and does not equal any value.
var mySymbol=Symbol(); console.log(typeof mySymbol) //"symbol"
2. Strings
New Method for ES6 String
UTF-16 bits: ES6 enforces UTF-16 string encoding. For the explanation of UTF-16, please understand Baidu by yourself.
codePointAt(): This method supports UTF-16, accepts the position of the encoding unit as a parameter, instead of the position of the string, and returns a bit corresponding to the given position in the string, that is, an integer value.
String.fromCodePoiont(): Contrary to codePointAt s, it retrieves the bits of a character in a string or generates a character based on the specified bits.
normalize(): Provides a standard form of Unicode, accepts an optional string parameter, and specifies the application of a standard form of Unicode.
In ES6, three new methods have been added. Each method receives two parameters, a substring to be detected, and an index position to start matching.
Template string
String is one of the basic types in JavaScript. It should be considered as the most frequently used type besides objects. String contains many methods such as substr, replace, indexOf,slice and so on. ES6 introduces the characteristics of template strings, which can be expressed by inverse quotation marks to represent multi-line strings and achieve text interpolation (using template placeholders).
//Previous multi-line strings we wrote as follows: console.log("hello world 1\n\ hello cala"); // "hello world // hello cala" //After you have the template string console.log(`hello world string text line 2`); // "hello world // hello cala"
Template placeholders can be represented by ${} and variables that you have defined can be passed into brackets, such as:
var name="cala"; var age=22; console.log(`hello,I'am ${name},my age is ${age}`) //hello,I'am cala,my age is 22
Include (str, index): If the specified text is detected in the string, return true, otherwise false.
let t = 'abcdefg' if(t.includes('cde')) { console.log(2) } //true
startsWith(str, index): If the specified text is detected at the beginning of the string, return true or false.
let t = 'abcdefg' if(t.startsWith('ab')) { console.log(2) } //true
endsWith(str, index): If the specified text is detected at the end of the string, return true or false.
let t = 'abcdefg' if(t.endsWith('fg')) { console.log(2) } //true
If you just need to match whether or not a substring is included in the string, it is recommended to use the new method. If you need to find the location of the matching string, use indexOf().
Three, function
Default parameters of functions
In ES5, we pass parameters to the function and then set default values in the body of the function, as follows.
function a(num, callback) { num = num || 6 callback = callback || function (data) {console.log('ES5: ', data)} callback(num * num) } a() //ES5: 36, no parameter output default value //You can also use callback like this a(10, function(data) { console.log(data * 10) //1000, parameter output new value })
In ES6, we use the new default value notation
function a(num = 6, callback = function (data) {console.log('ES6: ', data)}) { callback(num * num) } a() //ES6: 36, no parameter output default a(10, function(data) { console.log(data * 10) //1000, parameter output new value })
4. Arrow function (=>)
(Arrow function is more important. Now, just to mention it briefly, I'll write a special article about arrow function later.)
const arr = [5, 10] const s = arr.reduce((sum, item) => sum + item) console.log(s) // 15
The use of this in arrow functions is different from that of ordinary functions. In common functions of JavaScript, there will be a value of this of its own, which is mainly divided into:
Ordinary function:
1. When a function is called as a global function, this points to the global object.
2. When a function is called as a method in an object, this points to the object.
3. When a function is used as a constructor, this points to a new object from the constructor new.
4. You can also change the direction of this through call, apply, and bind
Arrow function:
1. The arrow function does not have this. This inside the function comes from the nearest non-arrow function of the parent and cannot change the direction of this.
2. The arrow function does not have super
3. Arrow function without arguments
4. The arrow function has no new.target binding.
5. Can't use new
6. No prototype
7. Duplicate naming parameters are not supported.
A Simple Understanding of Arrow Function
1. The left side of the arrow function represents the input parameters and the right side represents the output results.
const s = a => a console.log(s(2)) // 2
2. In the arrow function, this belongs to the lexical scope, which is determined directly from the context. For this which is pointed to indefinite in the ordinary function, the processing of this in the arrow function is undoubtedly simpler, as follows:
//ES5 General Function function Man(){ this.age=22; return function(){ this.age+1; } } var cala=new Man(); console.log(cala())//undefined //ES6 arrow function function Man(){ this.age=22; return () => this.age+1; } var cala=new Man(); console.log(cala())//23
3. There are no arguments in the arrow function (we can replace them with rest parameters), no prototypes, and no new keywords, for example:
//No arguments var foo=(a,b)=>{return arguments[0]*arguments[1]} console.log(foo(3,5)) //arguments is not defined //No prototype var Obj = () => {}; console.log(Obj.prototype); // undefined //Cannot use new keywords var Obj = () => {"hello world"}; var o = new Obj(); // TypeError: Obj is not a constructor
4. Arrow Function Sorts Arrays
const arr = [10, 50, 30, 40, 20] const s = arr.sort((a, b) => a - b) console.log(s) // [10,20,30,40,50]
Tail call optimization
Tail call refers to calling a new function when the function return s. Because the implementation of tail call needs to be stored in memory, in a loop body, if there is a tail call of the function, your memory may be full or overflowed.
In ES6, the engine will help you optimize the tail call. You don't need to optimize yourself, but you need to meet the following three requirements:
1. Functions are not closures
2. Tail call is the last statement of a function
3. Return the result of tail call as a function
Real Use of Tail Call-Recursive Function Optimization
In the ES5 era, recursion is not recommended because it affects performance.
But with tail call optimization, the performance of recursive functions has improved.
//A New Tail-optimized Writing Method "use strict"; function a(n, p = 1) { if(n <= 1) { return 1 * p } let s = n * p return a(n - 1, s) } //Find the factorial of 1 x 2 x 3 let sum = a(3) console.log(sum) // 6
5. New Method for ES6 Objects
Object.assign()
The Object.assign() method is used to copy the values of all enumerable attributes from one or more source objects to the target object. It will return the target object.
The Object.assign method only copies the source object's own enumerable attributes to the target object. This method uses the source object [[Get]] and the target object [[Set], so it calls related getters and setter s. Therefore, it assigns attributes, not just copying or defining new attributes. If the merge source contains getter, this may make it unsuitable for merging new attributes into the prototype. In order to copy attribute definitions (including enumerability) to prototypes, Object. getOwnProperty Descriptor () and Object.defineProperty() should be used.
Attributes of String and Symbol types are copied.
Merge object
var o1 = { a: 1 }; var o2 = { b: 2 }; var o3 = { c: 3 }; var obj = Object.assign(o1, o2, o3); console.log(obj); // { a: 1, b: 2, c: 3 } console.log(o1); //{a: 1, b: 2, c: 3} Note that the target object itself will change.
Merge objects with the same attributes
var o1 = { a: 1, b: 1, c: 1 }; var o2 = { b: 2, c: 2 }; var o3 = { c: 3 }; var obj = Object.assign({}, o1, o2, o3); console.log(obj); // { a: 1, b: 2, c: 3 }
Map and Set
Maps and Sets are called collections, but they are also different. Sets are often used to check whether a key name exists in an object, and Map collections are often used to retrieve existing information.
Set is a sequential list with independent non-repetitive values.
Array vs. Set
Both are containers that store multiple values, and they can be converted to each other, but there are differences in usage scenarios. As follows:
Array's indexOf method is less efficient than Set's has h method.
Set does not contain duplicate values (you can use this feature to de-weigh an array)
Set deletes a value through the delete method, while Array can only use splice. The former is more convenient to use.
Many of Array's new methods, such as map, filter, some, every etc., are not available in Set (but they can be used by converting each other)
Object versus Map
Object is a string-value, Map is a Value-Value
Object keys are of string type and Map keys are of any type
Manual calculation of Object size, Map.size can get size
Map sorting is insertion order
Object has prototypes, so there are some default keys in the mapping. Map=Object.create(null)
Set operation set
let set = new Set() //Set into arrays let arr = Array.from(set) let arr = [...set] //Instance attributes (inherited from Set) set.constructor === Set set.size //Method of operation set.add(1) //Add a value set.delete(1) //Delete a value set.has(1) //Determine whether there is this value (indexOf in Array) set.clear() //Clear all values //Get member methods for traversal (the traversal order of a Set is the insertion order) set.keys() //A traversal that returns the key name set.values() //Return key is worth traversal set.entries() //Returns a traversal of key-value pairs set.forEach() //Loop through each value (consistent with Array's method) for (let key of set.keys()){} for (let val of set.values()){} for (let entry of set.entries()){} //Use an array method to process set values set = new Set(arr) set = new Set([...set].map((x) => x = x * 2)) set = new Set([...set].filter((x) => x > 2))
Method Set of Map
let map = new Map() //Instance attributes (inherited from Map) map.constructor === Map map.size //Method of operation map.set(1,2) map.get(1) map.delete(1) map.has(1) map.clear() //Traversal method map.keys() map.values() map.entries() map.forEach() //Map and array conversion map = new Map([['key','val'],[2,1]]) //Require a two-member array let arr = [...map] //Note that Map keys are memory-bound map.set([1], 's') map.get([1]) let arr = [1] let arr1 = [1] map.set(arr, 's') map.get(arr) map.set(arr1, 's') map.get(arr1)
Iterator
entries() return iterator: return key-value pairs
//array const arr = ['a', 'b', 'c']; for(let v of arr.entries()) { console.log(v) } // [0, 'a'] [1, 'b'] [2, 'c'] //Set const arr = new Set(['a', 'b', 'c']); for(let v of arr.entries()) { console.log(v) } // ['a', 'a'] ['b', 'b'] ['c', 'c'] //Map const arr = new Map(); arr.set('a', 'a'); arr.set('b', 'b'); for(let v of arr.entries()) { console.log(v) } // ['a', 'a'] ['b', 'b']
2. values() returns the iterator: returns the value of the key-value pair
//array const arr = ['a', 'b', 'c']; for(let v of arr.values()) { console.log(v) } //'a' 'b' 'c' //Set const arr = new Set(['a', 'b', 'c']); for(let v of arr.values()) { console.log(v) } // 'a' 'b' 'c' //Map const arr = new Map(); arr.set('a', 'a'); arr.set('b', 'b'); for(let v of arr.values()) { console.log(v) } // 'a' 'b'
3. keys() returns the iterator: returns the key of the key-value pair
//array const arr = ['a', 'b', 'c']; for(let v of arr.keys()) { console.log(v) } // 0 1 2 //Set const arr = new Set(['a', 'b', 'c']); for(let v of arr.keys()) { console.log(v) } // 'a' 'b' 'c' //Map const arr = new Map(); arr.set('a', 'a'); arr.set('b', 'b'); for(let v of arr.keys()) { console.log(v) } // 'a' 'b'
Although the three built-in iterator methods are listed above, there are also default iterators for different sets. In for of, the default iterators for arrays and sets are values(), and the default iterators for Map s are entries().
for of cyclic deconstruction
The object itself does not support iteration, but we can add a generator ourselves, return an iterator of key and value, and then deconstruct key and value with a for of loop.
const obj = { a: 1, b: 2, *[Symbol.iterator]() { for(let i in obj) { yield [i, obj[i]] } } } for(let [key, value] of obj) { console.log(key, value) } // 'a' 1, 'b' 2
String iterator
const str = 'abc'; for(let v of str) { console.log(v) } // 'a' 'b' 'c'
ES6 adds several new methods to the array: find(), find index (), fill(), copyWithin().
1. find(): Pass in a callback function, find the first element in the array that meets the current search rule, return it, and terminate the search.
const arr = [1, "2", 3, 3, "2"] console.log(arr.find(n => typeof n === "number")) // 1
2. findIndex(): Pass in a callback function, find the first element in the array that meets the current search rules, return its subscript, and terminate the search.
const arr = [1, "2", 3, 3, "2"] console.log(arr.findIndex(n => typeof n === "number")) // 0
3. fill(): Replace elements in an array with new elements, specifying the range of substitution subscripts.
arr.fill(value, start, end)
4. copyWithin(): Select a subscript of the array, copy the elements of the array from that location, and start copying from 0 by default. You can also specify the range of elements to be copied.
arr.copyWithin(target, start, end) const arr = [1, 2, 3, 4, 5] console.log(arr.copyWithin(3)) //[1,2,3,1,2] Starting with the element subscribed to 3, the array is copied, so 4,5 is replaced by 1,2. const arr1 = [1, 2, 3, 4, 5] console.log(arr1.copyWithin(3, 1)) //[1,2,3,2,3] Starting with the element subscribed to 3, replicate the array, specifying that the first element to be replicated is subscribed to 1, so 4,5 is replaced by 2,3 const arr2 = [1, 2, 3, 4, 5] console.log(arr2.copyWithin(3, 1, 2)) //[1,2,3,2,5] Starting with the element with subscript 3, replicate the array, specifying that the subscript of the first element for replication is 1 and the end position is 2, so 4 is replaced by 2.
class, Promise and asynchronous programming, Proxy and Reflection API s in ES6 are more complex, and they will be written in detail later.
PS: It was written in such a hurry that there were inevitably some mistakes and omissions.