Regular study notes, including ES6, Promise, Node.js, Webpack, HTTP Principles, Vue buckets, and possibly more Typescript, Vue3, and common interview questions.

Let, const, and var

Let, const, and var are all used to define variables. What’s the difference?

The characteristics of the var

  1. Contaminate global variables

    var a = 1;
    console.log(window.a); / / 1
    Copy the code
  2. There is a variable promotion mechanism

    console.log(name); // undefined
    var name = "a";
    Copy the code
  3. Var can be declared repeatedly

    var a = 1;
    var a = 2;
    var a = 3;
    Copy the code
  4. Var has only global scope and function scope

    {
      var a = 1;
    }
    console.log(a); / / 1
    Copy the code

The characteristics of the let

  1. Cannot be repeated

    let a = 1;
    let a = 2;
    let a = 3;
    // Identifier 'a' has already been declared
    Copy the code
  2. There are block-level scopes

    for(let i = 0; i <10; i++){
      setTimeout(function(){
        console.log(i) // 0 1 2... 8 and 9})}Copy the code

    If you are usingvarDefinition, all 10 is printed

    for(var i = 0; i <10; i++){
      setTimeout(function(){
        console.log(i) / / 10})}Copy the code
  3. Temporary dead zone

    let a = 1;
    {
      console.log(a); // Cannot access 'a' before initialization
      let a = 2;
    }
    Copy the code

    When defining a variable, ES6 defines a variable with the same name as two variables (as shown below).

The characteristics of the const

Const is an immutable quantity, that is, a constant.

  1. The value of a variable defined by const cannot be modified.

    const PI = 3.14;
    PI = 3.15; // Assignment to constant variable.
    Copy the code
  2. Const can modify values in the same address (heap memory).

    const a = { b: 1 };
    a.b = 2;
    console.log(a); // {b: 2}
    Copy the code

Deconstruction assignment

In deconstruction, the following two parts are involved:

Source of deconstruction: Deconstruct the right side of the assignment expression.

Deconstruction goal: Deconstruct the left side of the assignment expression.

Array deconstruction

  1. The basic use

    let [a, b, c] = [1.2.3];
    // a = 1
    // b = 2
    // c = 3
    Copy the code
  2. Use nested

    let [a, [[b], c]] = [1The [[2].3]].// a = 1
    // b = 2
    // c = 3
    Copy the code
  3. You can ignore undefined variables

    let [a, , b] = [1.2.3];
    // a = 1
    // b = 3
    Copy the code
  4. Incomplete deconstruction

    let [a = 1, b] = []; // a = 1, b = undefined
    Copy the code
  5. String deconstruction and so on

    In array deconstruction, the target of deconstruction isTraversable objects, can be destructively assigned.

    let [a, b, c, d, e] = 'hello';
    // a = 'h'
    // b = 'e'
    // c = 'l'
    // d = 'l'
    // e = 'o'
    Copy the code
  6. Deconstructing defaults

    let [a = 2] = [undefined]; // a = 2
    Copy the code

    When the deconstruction pattern has a match, and the match isundefinedThe default value is triggered as the return result.

    let [a = 3, b = a] = [];     // a = 3, b = 3
    let [a = 3, b = a] = [1];    // a = 1, b = 1
    let [a = 3, b = a] = [1.2]; // a = 1, b = 2
    Copy the code
    • The matching result of A and B isundefinedTrigger default value:a = 3; b = a =3
    • A is normally deconstructed, and the matching result isa = 1B Match resultundefinedTrigger default value:b = a =1
    • A and B are normally deconstructed and assigned, the matching result is A = 1, b = 2
  7. Extended operator

    let [a, ...b] = [1.2.3];
    // a = 1
    // b = [2, 3]
    Copy the code

    Expansion operators, also known as expansion operators or residual operators. Arrays can be merged using extension operators. (As shown below)

Object deconstruction

  1. The basic use

    let { foo, bar } = { foo: 'aaa'.bar: 'bbb' };
    // foo = 'aaa'
    // bar = 'bbb'
     
    let { baz : foo } = { baz : 'ddd' };
    // foo = 'ddd'
    Copy the code
  2. It can be nested/ignored

    let obj = {p: ['hello', {y: 'world'}};let {p: [x, { y }] } = obj;
    // x = 'hello'
    // y = 'world'
    let obj = {p: ['hello', {y: 'world'}};let {p: [x, {  }] } = obj;
    // x = 'hello'
    Copy the code
  3. Incomplete deconstruction

    let obj = {p: [{y: 'world'}};let {p: [{ y }, x ] } = obj;
    // x = undefined
    // y = 'world'
    Copy the code
  4. Deconstructing defaults

    let {a = 10, b = 5} = {a: 3};
    // a = 3; b = 5;
    let {a: aa = 10.b: bb = 5} = {a: 3};
    // aa = 3; bb = 5;
    Copy the code
  5. Extended operator

    let{a, b, ... rest} = {a: 10.b: 20.c: 30.d: 40};
    // a = 10
    // b = 20
    // rest = {c: 30, d: 40}
    Copy the code

    In ES6, we can extend operators for many applications, such as deep copy and shallow copy.

References ES6 deconstruction assignment | novice tutorial

Deep copy and shallow copy

Deep copy: The copied array points to a new memory space in the heap, regardless of the original one. Shallow copy: The new array points to the same heap memory as the original array.

Shallow copy

Object.assign()

The object.assign () method copies the enumerable properties of any number of source objects to the target Object and returns the target Object. But object.assign () makes a shallow copy, copying references to the Object’s attributes rather than the Object itself.

let obj = {a: {name: "mxs".age: 26}};
let obj2 = Object.assign({}, obj);
obj2.a.name = "zd";
console.log(obj.a.name); // zd
Copy the code

Array.prototype.concat()

let arr = [1.2, {
  name: 'mxs'
}];
let arr2 = arr.concat();    
arr2[2].name = 'zd';
console.log(arr); // [1, 2, {name:'zd'}]
Copy the code

Array.prototype.slice()

let arr = [1.2, {
  name: 'mxs'
}];
let arr2 = arr.slice();
arr2[2].name = 'zd'
console.log(arr); // [1, 2, {name:'zd'}]
Copy the code

Extended operator

The extension operator copies only one layer of objects/arrays

let obj1 = {name:'zd'};
let obj2 = {age: {count:26}};
letallObj = {... school,... my}; obj2.age.count =100;
console.log(allObj); // {{name: "zd", age: {count: 100}}}
console.log(obj2); // {age: {count: 100}}
Copy the code

You can see that both objects have changed, so only a shallow copy has been implemented.

If you want to implement deep copy, it will be very troublesome.

let obj1 = {name:'zd'};
let obj2 = {age: {count:26},name:'mxs'};
// Place the original my in the new object and copy the original age with the new object age
letnewObj2 = {... obj2,age: {... obj2.age}}letallObj = {... obj1,... newObj2}; obj2.age.count =100;
console.log(allObj); // {{name: "mxs", age: {count: 26}}}
console.log(obj2); // {{name: "mxs", age: {count: 100}}}
Copy the code

Deep copy

JSON.parse(JSON.stringify())

Convert the object into a JSON string using json.stringify, and parse the string into an object using json.parse ().

let obj1 = {name:'zd'};
let obj2 = {age: {count:26}};
let allObj = JSON.parse(JSON.stringify({... obj1,... obj2})); obj2.age.count =100;
console.log(allObj); // {name: 'zd', age: { count: 26 }}
Copy the code

(json.stringify ([value])) does not copy Function, undefined, and Symbol.

let obj = {name:'zd'.age: {},count:26.a:function(){}, b:null.c:undefined.d:Symbol('zd')}
let allObj = JSON.parse(JSON.stringify(obj));
console.log(allObj); // {name: 'zd', age: {}, count: 26, b: null}
Copy the code

As you can see, the only data types that are copied are String, Object, Number, and Null.

Lodash library

We can do this with the cloneDeep method in the Loadash library.

const _ = require('lodash');
let obj = {
   a: 1.b: { a: { b: 1}},c: [1.2.3]};var cloneObj = _.cloneDeep(obj1);
console.log(obj.a.b === cloneObj.a.b); // false
Copy the code

Deep copy is realized by handwriting

Let’s look at the complete code first

function deepClone(obj,hash = new WeakMap(a)) {
  if (obj == null) return obj;
  if (typeofobj ! = ='object') return obj;
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  if (hash.has(obj)) return hash.get(obj);
  let cloneObj = new obj.constructor;
  hash.set(obj, cloneObj);
  for (const key in obj) {
    if (Object.hasOwnProperty.call(obj, key)) { cloneObj[key] = deepClone(obj[key], hash); }}return cloneObj;
}

let obj = {name:'zd'.age: {},count:26.a:function(){}, b:null.c:undefined.d:Symbol('zd')}
let allObj = deepClone(obj);
console.log(allObj); // {name: 'zd', age: {}, count: 26, a: [Function: a], b: null, c: undefined, d: Symbol(zd)}
Copy the code

If we want to write a deep-clone function method, we need to understand how to do it first.

To put it simply, its implementation idea is type judgment, clone data type, traverse the loop, and finally output the result.

  1. Let’s start by thinking, why do we make type judgments?

    Before we do that, we need to know how to determine the data type.

    • typeof
    • instanceof / constructor
    • Object.prototype.toString.call([value])

    And then we’ll look at the code

    // if obj is null or undefined, the result is returned directly
    if (obj == null) return obj;
    // If obj is an underlying data type or function, the result is returned directly (i.e., the function does not need any processing).
    if (typeofobj ! = ='object') return obj;
    // If obj is not an object or array, return the result directly
    if (obj instanceof Date) return new Date(obj);
    if (obj instanceof RegExp) return new RegExp(obj);
    Copy the code

    From the above code, we can see that there are only two data types left: arrays and objects.

    Then it is clear that the purpose of type judgment is to filter out the data types that need to be deep-cloned.

  2. And then you think, how do you clone the data type of the object you pass in?

    The most commonly used schemes are as follows

    let cloneObj = Object.prototype.toString.call(obj) === ['Object Array']? [] : {};Copy the code

    But that’s too cumbersome to write, and we have a simpler implementation.

    // obj is either an array or object, clone it
    let cloneObj = new obj.constructor;
    Copy the code

    According to the pointing principle of prototype chain, we can use the above scheme to create a new datatype object. (As shown below)

    The purpose of cloning data types is to perform the next iteration of the loop.

  3. And then, we’re going to go through the loop.

    for (const key in obj) {
      if (Object.hasOwnProperty.call(obj, key)) {
        // Perform recursion to implement deep clonecloneObj[key] = deepClone(obj[key]); }}Copy the code

    With Forin loops, before the object is copied, we recurse the value and execute the current method again to see if there are deep attributes. Until there are no deeper properties.

    It then assigns the result to cloneObj, and finally outputs the result.

    return cloneObj
    Copy the code

    One problem with this approach is that it does not allow circular references.

    If a circular reference is made, an overflow of stack memory occurs.

    let obj = {a: {name:'mxs'}}
    obj.b = obj;
    let allObj = deepClone(obj);
    obj.a.name = 'zd';
    console.log(obj); // Maximum call stack size exceeded
    Copy the code

    In order to handle the occurrence of this problem, we need to do one more step.

  4. Finally, we need to handle exceptions

    hash = new WeakMap(a)Copy the code

    Set a WeakMap data types (about WeakMap, JavaScript can reference WeakMap – | MDN, or check my another blog ES6 | WeakMap)

    if(hash.has(obj)) return hash.get(obj);
    hash.set(obj, cloneObj);
    Copy the code

    If it is Object, we put it in weakMap. If the Object existed before the copy, we return it directly.

    At this point, our deep copy is complete.

We can write many kinds of procloning schemes through this idea.

This article was created by Mo Xiaoshang. If there are any problems or omissions in the article, welcome your correction and communication. You can also follow my personal site, blog park and nuggets, AND I will upload the article to these platforms after it is produced. Thank you for your support!