As we all know, JS is in the position of front-end development. It’s really important to learn it well.

Let’s take a look at some common es6 syntax.

deconstruction

An array of deconstruction

  • Normal deconstruction value
    var names = ["zh"."llm"."hz"]
    // var item1 = names[0]
    // var item2 = names[1]
    // var item3 = names[2]

    // Array: []
    var [item1, item2, item3] = names
    console.log(item1, item2, item3)
Copy the code
  • Optional value. Must be separated by commas.
    // Deconstruct the following elements
    var [, , item3] = names
    console.log(item3)
Copy the code
  • Place the remaining elements in an array, using the remaining parameters. The remaining arguments are similar to those passed to a function.
    // Unpack an element and place the following elements in a new array
    var [itemx, ...newNames] = names
    console.log(itemx, newNames)
Copy the code
  • Assign default values to deconstructed elements
    // Default value for deconstruction
    var [item1, item2, item3, item4 = "love"] = names
    console.log(item4)
Copy the code

Object structure

  • Object structures are more flexible than array structures and can be evaluated out of order
    var obj = {
      name: "zh".age: 22
    }

    // Object deconstruction: {}
    var { age, name} = obj
    console.log(name, age)
Copy the code
  • Place the deconstructed residual values in a new object. Again, we need the rest of the parameters.
    var{age, ... newObj} = objconsole.log(newObj)
Copy the code
  • Renames deconstructed properties. You take the name property from obj and assign it to newName.
    var { name: newName } = obj
    console.log(newName)
Copy the code
  • Assign default values to deconstructed properties. Since obj has no sex attribute, its value will be undefined if it is not assigned.
    var { sex: newSex = "Male" } = obj
    console.log(newSex)
Copy the code

Deconstruction is now used a lot in development:

  • For example, when a variable is given in development, it is automatically deconstructed and used.
  • For example, destruct the parameters of a function.

let,const

  • Let definition variable
  • Const defines constant
  • In the same scope, let variables defined by const cannot be redefined and an error will be reported, but variables defined by var can be redefined.
let foo = "zh"
// SyntaxError: Identifier 'foo' has already been declared
let foo = "llm"
Copy the code
  • We know that var is scoped, but let const is scoped.

First let’s look at the definition of scoping, okay?

In the scope of a declared variable, if the variable can be accessed before the declaration, then we can call it scope-raising

    // Reference()Error: Cannot access 'foo' before initialization
    // let/const they are not scoped
    // Foo is created but cannot be accessed
    // scope promotion: can be accessed ahead of time
    console.log(foo)
    let foo = "foo"
Copy the code

From the above code, error reported. So a variable defined by let const is not scoped. So let, const is not scoped but is created during parsing.

  • We know that the variable defined by var ends up being a property of the window, but is let const bound to the window?

They are stored in a VariableMap and are not bound to Windows.

Block-level scope

The scope that exists in ES5. There is no block-level scope.

  • Global scope
  • Function scope
    // ES5 has no block-level scope
    // Block code
    {
      // Declare a variable
      var foo = "foo"
    }
    // This is accessible
    console.log(foo) / / foo.
Copy the code

Block-level scope was added in ES6, and identifiers declared via lets, const, function, and class have block-level scope limitations. But different browsers have different implementations (most browsers leave function without block-level scope in order to be compatible with previous code)

    // ES6 code block-level scope
    // Let /const/function/class declaration type is valid
    {
      let foo = "why"
      function demo() {
        console.log("demo function")}class Person {}}// console.log(foo) // foo is not defined
    // Different browsers have different implementations (most browsers have no block-level scope for function to be compatible with previous code)
    // demo()
    var p = new Person() // Person is not defined
Copy the code

And the {} of the if, switch, and for loops all have block-level scope.

    The code for the if statement is block-level scope
    if (true) {
      var foo = "foo"
      let bar = "bar"
    }

    console.log(foo)
    console.log(bar)// bar is not defined

    // Switch statement code is also block-scoped
    var color = "red"

    switch (color) {
      case "red":
        var foo = "foo"
        let bar = "bar"
    }

    console.log(foo)
    console.log(bar) //bar is not defined

    The code for the // statement is also block-scoped

    for (let i = 0; i < 10; i++) {
    }

    console.log(i) //i is not defined
Copy the code

Let,const defines variable differences in for loop, okay?

    const names = ["zh"."llm"."zl"]

    // Const cannot be used
    for (let i = 0; i < names.length; i++) {
      console.log(names[i])
    }

    // The principle of the for loop above
    {
      let i = 0
      console.log(names[i])
    }

    {
      // This I is the sum of the last time and then assigned.
      let i = 1
      console.log(names[i])
    }

    {
      // This I is the sum of the last time and then assigned.
      let i = 2
      console.log(names[i])
    }
Copy the code

We know that a variable defined by const in a for loop is block-scoped. Moreover, each time through the for loop it accumulates the last variable and assigns it to the next block-scoped variable. We cannot use const to define a variable in a for loop because a const cannot be changed.

Template string

Use is very simple, is to write strings by ‘ ‘. Variables are then identified by ${}. And all kinds of expressions can be written inside.

    const name = "zh"
    const age = 22

    // ES6 provides template strings
    const message = `my name is ${name}, age is ${age}`
    console.log(message)

    const info = `age double is ${age * 2}`
    console.log(info)

    function doubleAge() {
      return age * 2
    }

    const info2 = `double age is ${doubleAge()}`
    console.log(info2)
Copy the code

But there is a special use called tag template strings. It can be used to call functions.

  • A normal string in a string will beThe ${}The identified variable is split into an array and passed the first argument to the function.The ${}Is passed in turn to other arguments in the function.
    function foo(m, n, x) {
      console.log(m, n, x)
    }
    const i = 'zh';
    const you = 'llm'
    foo`I love you` // [ 'I love you' ] undefined undefined
    foo`${i} love ${you}` // [ '', ' love ', '' ] zh llm
Copy the code

function

The default value for the function

  • We can assign default values to function parameters when we define them. And he uses the default value only when passing undefined.
function foo(m = "aaa", n = "bbb") {
  console.log(m, n)
}
foo(0."")
Copy the code
  • Object parameter Default value
    // Object parameters and default values and deconstruction
    function printInfo({name, age} = {name: "zh", age: 22}) {
      console.log(name, age)
    }

    printInfo({name: "llm".age: 21})

    // Another way to write it
    function printInfo1({name = "zh", age = 22} = {}) {
      console.log(name, age)
    }

    printInfo1()
Copy the code
  • The default parameter is best left behind. Otherwise, using default parameter values is inconvenient.
    // Parameters with default values are best left last
    function bar(x, y, z = 30) {
      console.log(x, y, z)
    }

    // bar(10, 20)
    bar(undefined.10.20)
Copy the code
  • Getting the number of function arguments will be a problem if you don’t write the default parameter values at the end. He can only get the number of arguments before the default argument.
    // The length attribute of the function with a default value
    function baz(x, y, z, m, n) {
      console.log(x, y, z, m, n)
    }

    console.log(baz.length) / / 5
    
    function bar(x, y, z = 10, m, n) {
      console.log(x, y, z, m, n)
    }

    console.log(bar.length) / / 2
Copy the code

The remaining arguments to the function

Rest Parameters are referenced in ES6, which can put an indefinite number of parameters into an array:

If the last argument is… Is prefixed, then it puts the remaining arguments in that argument as an array.

So what’s the difference between arguments and residual arguments?

  • The remaining arguments contain only arguments that have no corresponding parameters, whereas the Arguments object contains all arguments passed to the function.
  • The Arguments object is not a real array, while the REST argument is a real array and can do all the operations on an array.
  • Arguments is an early data structure provided in ECMAScript to make it easy to get all arguments, whereas REST arguments are provided in ES6 and are expected to replace arguments.
  • The remaining parameters must be placed in the last position; otherwise, an error will be reported.

Numerical representation

    const num1 = 100 / / decimal

    // b -> binary
    const num2 = 0b100 / / binary
    // o -> octonary
    const num3 = 0o100 / / octal
    // x -> hexadecimal
    const num4 = 0x100 // Hexadecimal

    console.log(num1, num2, num3, num4)

    // Large numeric concatenation (ES2021 ES12)
    const num = 10 _000_000_000_000_000
    console.log(num)
Copy the code

Symbol

What is Symbol?

Symbol is a new basic data type in ES6 that translates to symbols.

So why do we need Symbol?

  • Prior to ES6, object attribute names were strings, and it was easy to create attribute name conflicts.

  • For example, we have an object and we want to add a new attribute and value to it, but we are not sure what the original internal content of the object is, so it is easy to cause conflicts and overwrite one of its internal attributes.

  • For example, when we wrote the apply, call, and bind implementations, we added a FN attribute to them. What if they already had fn attributes inside them?

  • For example, if we use mixin during development, one of the attributes with the same name will be overwritten.

Symbol solves this problem by generating a unique value. A Symbol is a Symbol of human nature.

  • The Symbol value is generated by the Symbol function and can be used as the property name.
    const s1 = Symbol(a)const s2 = Symbol(a)const s3 = Symbol(a)Copy the code
  • That is, in ES6, object attribute names can be strings or Symbol values.
// Symbol value as key
// used when defining object literals
const obj = {
  [s1]: "zh"
}
// Add attributes
obj[s2] = "llm"

/ / Object. DefineProperty way
Object.defineProperty(obj, s3, {
  enumerable: true.configurable: true.writable: true.value: "zl"
})
Copy the code
  • Symbol creates values even if they are created multiple times: each value created after the Symbol function is executed is unique.
  • We can also pass in a description when creating a Symbol value: this is a new feature in ES2019 (ES10).
    // In ES2019(ES10), Symbol also has a description.
    const s3 = Symbol("aaa")
    console.log(s3.description)// aaa
Copy the code
  • If you use Symbol as the property name for the key, you will not get the Symbol values in the traversal of object. keys, etc. Need Object. GetOwnPropertySymbols to get all the key Symbol.
    console.log(Object.keys(obj))
    console.log(Object.getOwnPropertyNames(obj))
    console.log(Object.getOwnPropertySymbols(obj))
    const sKeys = Object.getOwnPropertySymbols(obj)
    for (const sKey of sKeys) {
      console.log(obj[sKey])
    }
Copy the code
  • throughSymbol.for()Create equal Symbol values. If the parameter is passed, the same parameter must be passed. throughkeyForGets the parameter passed, undefined.
    const a = Symbol.for()
    const b = Symbol.for()
    console.log(a === b)
    const key = Symbol.keyFor(a)
    console.log(key) / / undefined.
Copy the code

Set, WeakSet

Before ES6, we stored data in two main structures: arrays and objects. Two additional data structures have been added to ES6: Set, Map, and their other forms, WeakSet, WeakMap.

A Set is a new data structure that can be used to hold data, similar to an array except that the elements cannot be duplicated.

To create a Set we need to use the Set constructor (no literal creation yet) :

    const set = new Set()
    set.add(10)
    set.add(10)
    console.log(set)
Copy the code

We can see that the elements in a Set are never duplicated, so a very common function of a Set is to deduplicate an array.

    const arr = [33.10.26.30.33.26]
    const arrSet = new Set(arr)
    console.log(arrSet)
Copy the code

Here are some instance methods and properties of set

  • size: Returns the number of elements in the Set.
  • add(value): Adds an element and returns the Set object itself.
  • delete(value): Removes the element equal to this value from the set, returning Boolean.
  • has(value): Checks whether an element exists in a set, returning Boolean.
  • clear(): Clears all elements of the set, with no return value.
  • forEach(callback, [thisArg]): traverses a set through forEach.
  • keys(): returns a traverser for key names.The key name and key value in a set are the same.
  • values(): returns a traverser for key values
  • entries(): returns a traverser for key-value pairs

Another data structure similar to Set is called WeakSet, which is also a data structure in which internal elements cannot be repeated.

So what’s the difference from Set?

  • WeakSet can only store object types, not basic data types.
const weakSet = new WeakSet(a)// Only object types can be stored
weakSet.add(10)// TypeError: Invalid value used in weak set
Copy the code
  • A WeakSet reference to an element is a weak reference, and if there are no other references to an object, then GC can reclaim that object.

Weak reference means that when the object itself is assigned to null, even if the object is included in weakSet, the object will be garbage collected by JS.

Below is an analysis of bothCommon methods of WeakSet:

  • add(value): Adds an element and returns the WeakSet object itself.
  • delete(value): Removes the element equal to the value from WeakSet, returning Boolean.
  • has(value): Judge whether there is an element in WeakSet and return Boolean type.

Note: WeakSet cannot traverse.

Because WeakSet is only a weak reference to the object, if we traverse and obtain the elements, it may cause that the object cannot be destroyed normally. Therefore, objects stored in WeakSet cannot be obtained.

An application scenario of weakSet

The following code does not allow non-type objects to call the class’s methods.

    const personSet = new WeakSet(a)class Person {
      constructor() {
        personSet.add(this)}running() {
        if(! personSet.has(this)) {
          throw new Error("Running method cannot be called from objects not created by constructors")}console.log("running~".this)}}let p = new Person()
    p.running() // It is running properly

    p.running.call({name: "zh"}) / / an error
Copy the code

Map, WeakMap

Map stores mappings.

But we might wonder, before we could use objects to store mappings, what’s the difference between them?

  • In fact, our object store mapping can only use strings (Symbol added in ES6) as attribute names (keys).
  • In some cases we might want to use a different type of key, such as an object, which is automatically converted to a string as the key. Then we can use Map.

Here are some instance methods and properties of Map

  • size: Returns the number of elements in the Map.
  • set(key, value): Adds a key and value to the Map to return the Map object.
  • get(key): Obtains the value in the Map based on the key.
  • has(key): Checks whether a key is included. Returns Boolean.
  • delete(key): Deletes a key-value pair based on key, returning Boolean.
  • clear(): Clears all elements.
  • forEach(callback, [, thisArg]): Traverses the Map through forEach.Note in particular that the Map traversal order is the insertion order.
  • keys(): returns a traverser for key names.
  • values(): returns a traverser for key values.
  • entries(): returns a traverser for key-value pairs.

A Map can also be traversed by for of.

    // item is an entry. An array containing key names and key values.
    for (const item of map) {
      console.log(item[0], item[1])}Copy the code

WeakMap, another data structure of the Map type, also exists in the form of key-value pairs.

So what’s the difference with Map?

  • WeakMap keys can only use objects and do not accept other types as keys.
  • WeakMap’s key refers to an object as a weak reference, and if no other reference refers to the object, GC can reclaim the object.
    let obj = {
      name: 'zh'
    }
    const wmap = new WeakMap(a); wmap.set(obj,"= = = =")

    console.log(wmap.get(obj))/ / = = = =

    obj = null;

    console.log(wmap.get(obj))// undefined
Copy the code

Common methods of WeakMap:

  • Set (key, value) : Adds keys and values to the Map and returns the entire Map.
  • Get (key) : obtains the value in the Map based on the key.
  • Has (key) : Checks whether a key is included. Returns Boolean.
  • Delete (key) : Deletes a key-value pair based on the key, returning Boolean type.

Note: WeakMap cannot traverse.

So what is the role of our WeakMap?

Implementing the responsive principle of VUE (pseudocode)

    // Application scenario (vuE3 responsive principle)
    const obj1 = {
      name: "zh".age: 22
    }

    function obj1NameFn1() {
      console.log("Obj1NameFn1 executed")}function obj1NameFn2() {
      console.log("Obj1NameFn2 executed")}function obj1AgeFn1() {
      console.log("obj1AgeFn1")}function obj1AgeFn2() {
      console.log("obj1AgeFn2")}const obj2 = {
      name: "llm".age: 21
    }

    function obj2NameFn1() {
      console.log("Obj1NameFn1 executed")}function obj2NameFn2() {
      console.log("Obj1NameFn2 executed")}// 1. Create WeakMap
    const weakMap = new WeakMap(a)// 2. Collect dependency structures
    // 2.1. Data structures collected for obj1
    const obj1Map = new Map()
    obj1Map.set("name", [obj1NameFn1, obj1NameFn2])
    obj1Map.set("age", [obj1AgeFn1, obj1AgeFn2])
    weakMap.set(obj1, obj1Map)

    // 2.2. Data structures collected for obj2
    const obj2Map = new Map()
    obj2Map.set("name", [obj2NameFn1, obj2NameFn2])
    weakMap.set(obj2, obj2Map)

    // 3. If obj1.name is changed
    // Proxy/Object.defineProperty
    obj1.name = "lz"
    const targetMap = weakMap.get(obj1)
    const fns = targetMap.get("name")
    fns.forEach(item= > item())
Copy the code