1, typeof judgment

Does Typeof correctly determine the type? How does instanceof correctly identify objects

  • For primitive types, the correct type can be displayed except for null
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol(a)// 'symbol'
Copy the code

Typeof displays objects except functions, so typeof does not accurately determine what type a variable is

typeof [] // 'object'
typeof {} // 'object'
typeof console.log // 'function'
Copy the code

If we want to determine the correct type of an object, we can consider using Instanceof because the internal mechanism is to determine through the prototype chain

const Person = function() {}
const p1 = new Person()
p1 instanceof Person // true
var str = 'hello world'
str instanceof String // false
var str1 = new String('hello world')
str1 instanceof String // true
Copy the code

For primitive types, you can’t use instanceof to determine the type directly

2. Type conversion

There are only three types of cast in JS. They are:

  • Convert to a Boolean value
  • Convert to number
  • Convert to string

Turns a Boolean

In a condition, all values except undefined, null, false, NaN, “, 0, -0 are converted to true, including all objects

Object to primitive type

For the built-in ToPrimitive function that is called when an object converts its type, the algorithmic logic is generally as follows

  • If it is already a primitive type, there is no need to cast
  • The call x.valueof () returns the converted value if converted to the underlying type
  • Call x.tostring () and return the converted value if converted to the base type
  • If neither returns the original type, an error will be reported

Of course, you can override Symbol. ToPrimitive, which has the highest priority to call when converting to the original type.

var a = {
  valueOf() {
    return 0;
  },
  toString() {
    return '1'; },Symbol.toPrimitive]() {
    return 2; }};console.log(1 + a); / / 3

var b = {
  valueOf() {
    return 11;
  },
  toString() {
    return 10; }};console.log(1 + b); / / 12

var c = {
  toString() {
    return 5; }};console.log(1 + c); / / 6
    
Copy the code

Four operators

It has the following characteristics:

  • If one side of the operation is a string, then the other side will be converted to a string
  • If a side is not a string or a number, it is converted to either
1 + '1' / / '11'
true + true / / 2
4 + [1.2.3] 41, 2, 3 "/ /"
Copy the code
  • For the first line of code, it fires feature one, so it converts the number 1 to a string, resulting in ’11’
  • For the second line of code, feature two fires, so convert true to the number 1
  • In the case of the third line of code, characteristic two is triggered, so the array is toString into strings 1,2,3, resulting in 41,2,3

The other thing to notice about addition is this expression ‘a’ + + ‘b’.

'a' + + 'b' // -> "aNaN"
Copy the code
  • Because +’ b’ equals NaN, the result is “aNaN”, and you may also see the form +’1′ in some code to quickly get the number type.
  • So for operators other than addition, as long as one side is a number, then the other side is converted to a number
4 * '3' / / 12
4 * [] / / 0
4 * [1.2] // NaN
Copy the code

Comparison operator

  • If it is an object, the object is converted through toPrimitive
  • If it is a string, the comparison is made using the Unicode character index
let a = {
  valueOf() {
    return 0;
  },
  toString() {
    return '1'; }}; a > -1; // true
Copy the code

In the above code, because A is an object, the value is converted to the original type by valueOf and then compared.

3, This

Let’s start with a few function call scenarios

function foo() {
  console.log(this.a);
}
var a = 1;
foo();
const obj = {
  a: 2.foo: foo,
};
obj.foo();
const c = new foo();
Copy the code
  • For calling foo directly, no matter where foo is placed, this must be window, right
  • For obj.foo(), we just need to remember that whoever called the function is this, so this in the foo function is an obj object in this scenario
  • In the case of new, this is always bound to C and will not be changed in any way

So with that said, in a lot of code this should be fine, so let’s look at this in the arrow function, okay

function a() {
  return () = > {
    return () = > {
      console.log(this);
    };
  };
}
console.log(a()()());
Copy the code
  • First of all, there is no “this” in the arrow function. The “this” in the arrow function depends only on the “this” in the first normal function that wraps the arrow function. In this case, because the first normal function wrapping the arrow function is a, this is window. In addition, using a function such as bind for the arrow function is invalid.
  • The last case is a context-changing API like bind. For these functions, this depends on the first argument. If the first argument is null, it is window.
  • So speaking of bind, have you thought about, if you bind a function multiple times, what’s the context?
let a = {}
let fn = function () { console.log(this) }
fn.bind().bind(a)() / / = >?
Copy the code

If you think the output is a, you’re wrong. We can actually convert this code to another form

/ / fn. The bind (). The bind is equal to (a)
let fn2 = function fn1() {
  return function () {
    return fn.apply();
  }.apply(a);
};
fn2();
Copy the code

As you can see from the code above, no matter how many times we bind the function, this in fn is always determined by the first bind, so the result is always window

let a = { name: 'poetries' };
function foo() {
  console.log(this.name);
}
foo.bind(a)(); // => 'poetries'
Copy the code

This is the rule for this, but it is possible for more than one rule to occur at the same time. In this case, the different rules will determine where this ends according to the highest priority.

First, new takes precedence, then bind, then obj.foo(), and finally foo. Once this is bound to the arrow function, it is not changed in any way.

What’s the difference between == and ===

In the case of ==, a cast is performed if the two types of the comparison are different

If we need to compare whether x and y are the same, the following judgment process occurs

  1. First, it determines whether the two types are the same. The same is the ratio size
  2. If the type is not the same, then the cast will be performed
  3. It will first determine whether null and undefined are being compared, and if so, it will return true
  4. Determine if the two types are string and number, and if so, convert the string to number
1= ='1'  ->  1= =1
Copy the code
  1. Determine whether one of the parties is Boolean, and if it is, convert Boolean to number
'1'= =true  ->  '1'= =1   ->  1= =1
Copy the code
  1. If one of them is an object and the other is a string, number, or symbol, then convert the object to its original type
'1'= = {name: 'yck'} - >'1'= ='[object Object]'
Copy the code

It’s much simpler for ===, which is to determine whether the two types and values are the same

5, closures

The definition of A closure is simple: function A has A function B inside it, and function B can access variables in function A, so function B is A closure

function A() {
  let a = 1;
  window.B = function () {
    console.log(a);
  };
}
A();
B(); / / 1
Copy the code

Closures exist to give you indirect access to variables inside a function

Use closures in a loop to solve the problem of var defining functions

for (var i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i);
  }, i * 1000);
}
Copy the code

First of all, since setTimeout is an asynchronous function, it will execute the entire loop first, and then I will be 6, so it will print a bunch of 6’s

There are three solutions

  1. The first is using closures
for (var i = 1; i <= 5; i++) {
  (function (j) {
    setTimeout(function timer() {
      console.log(j);
    }, j * 1000);
  })(i);
}
Copy the code

In the above code, we first use the immediate execution function to pass I into the function. At this time, the value is fixed on the parameter j and will not change. The next time the timer closure is executed, we can use the variable j of the external function to achieve this goal

  1. The second is to use the third argument of setTimeout, which is passed in as an argument to the timer function
for (var i = 1; i <= 5; i++) {
  setTimeout(
    function timer(j) {
      console.log(j);
    },
    i * 1000,
    i
  );
}
Copy the code
  1. The third way is to use let definition I to solve the problem, and this is the most recommended way
for (let i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i);
  }, i * 1000);
}
Copy the code

6, deep copy

Shallow copy

The first way to solve this problem is with object.assign, which many people think is for deep copies. No, object. assign will only copy all property values to the new Object. If the property value is an Object, it will copy the address, so it is not a deep copy

let a = {
  age: 1};let b = Object.assign({}, a);
a.age = 2;
console.log(b.age); / / 1
Copy the code

We can also use the expansion operator… To implement a shallow copy

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

In general, shallow copies will solve most problems, but deep copies may be needed in the following situations

let a = {
  age: 1.jobs: {
    first: 'FE',}};letb = { ... a }; a.jobs.first ='native';
console.log(b.jobs.first); // native
Copy the code

The shallow copy only solves the first layer of the problem, and if the next value has an object in it, then it’s back to the beginning, both of which have the same address. To solve this problem, we need to use deep copy.

Deep copy

This problem can usually be solved with json.parse (json.stringify (object)).

let a = {
  age: 1.jobs: {
    first: 'FE',}};let b = JSON.parse(JSON.stringify(a));
a.jobs.first = 'native';
console.log(b.jobs.first); // FE
Copy the code

But there are limitations to this approach:

  • Ignores undefined
  • Ignore the symbol
  • Cannot serialize function
  • Objects with circular references cannot be resolved

7, the prototype

A prototype chain is a chain of objects that are __proto__ together. Why does Obj have access to valueOf, because obj found valueOf through the prototype chain

  • ObjectIs the father of all objects, all objects can pass__proto__To find it
  • FunctionIt’s the father of all functions, and all functions can pass__proto__To find it
  • Function of theprototypeIt’s an object
  • The object’s__proto__Property points to the stereotype,__proto__Connecting objects and stereotypes forms a prototype chain

Var, let, and const differences

What is promotion? What is a temporary dead zone? Var, let, and const?

  • Function promotion takes precedence over variable promotion. Function promotion moves the entire function to the top of the scope. Variable promotion only moves the declaration to the top of the scope
  • varThere are enhancements that we can use before the declaration. let constCannot be used before declaration due to temporary dead zone
  • varDeclaring a variable in the global scope causes it to be mounted on window. The other two do not
  • letconstBasically the same effect, but the latter declaration variable cannot be assigned again