Explain the variable lift?

The way a JavaScript engine works is it parses the code, gets all the declared variables, and then runs them line by line. As a result, all variable declarations will be promoted to the header of the code, which is called variable promotion.

console.log(a) // undefined
var a = 1
function b() {
    console.log(a)
}
b() / / 1
Copy the code

The actual order of execution of the above code looks like this:

Var a = undefined; var a = undefined; var a = undefined

So the code looks like this:

var a = undefined
console.log(a) // undefined
a = 1
function b() {
    console.log(a)
}
b() / / 1
Copy the code

The second step is execution, so the JS engine runs the current result line by line from top to bottom, which is called variable promotion.

Understand closures?

The question really asks:

  1. What is a closure?
  2. What do closures do?

What is a closure

A closure is a combination of a function and the lexical environment in which it is declared.

Closures = “function” and “sum of variables accessible in function body”

Here’s a simple example:

(function() {
    var a = 1;
    function add() {
        var b = 2
        var sum = b + a
        console.log(sum); / / 3
    }
    add()
})()
Copy the code

The Add function itself, and its internal accessible variable, a = 1, together are called closures, and nothing more.

The role of closures

The best use of closures is to hide variables. One feature of closures is that an inner function can always access parameters and variables declared in its outer function, even after the outer function has been returned (end-of-life)

With this feature, JavaScript can implement private variables, privileged variables, stored variables, and so on

Let’s take private variables as an example. Private variables can be implemented in many ways, including by convention (variable names preceded by _), by Proxy, and by Symbol, a new data type. But what’s really popular is the use of closures.

function Person(){
    var name = 'cxk';
    this.getName = function(){
        return name;
    }
    this.setName = function(value){ name = value; }}const cxk = new Person()
console.log(cxk.getName()) //cxk
cxk.setName('jntm')
console.log(cxk.getName()) //jntm
console.log(name) //name is not defined
Copy the code

Var name = ‘CXK’ only getName and setName can be accessed externally, as opposed to privatizing variables.

Does JavaScript’s scope chain understand?

JavaScript is static scoped, meaning that the declared scope is determined at compile time based on the body of the program, sometimes called lexical scope. Essentially JavaScript to create executable context in the process of execution, the executable in the context of external environment of lexical reference is contained in lexical environment, we can through the reference for the external environment of lexical variable, statement, etc., these references together always point to the global environment of lexical, thus formed the scope chain.

What is the difference between ES6 modules and CommonJS modules?

ES6 Module and CommonJS Module

  • CommonJS is a shallow copy of a Module. ES6 Module is a reference to a Module. ES6 Module is read-only and cannot change its value
  • The interface for import is read-only and cannot modify its variable value. That is, you cannot change the pointer pointer to a variable, but you can change the internal pointer pointer to a variable. You can reassign to commonJS pairs, but assigning to ES6 Module will compile an error. ES6 Modules and CommonJS modules have in common:
  • Both CommonJS and ES6 Modules can assign values to imported objects, that is, change the values of the internal properties of the object.

What are the types of JS?

JavaScript types fall into two broad categories: primitive types and complex (reference) types.

Original type:

  • boolean
  • null
  • undefined
  • number
  • string
  • symbol

Complex types:

  • Object also has a primitive type, BigInt, that has not been officially released but will soon be added to the standard.

Why BigInt’s proposal?

In JavaScript, number. MAX_SAFE_INTEGER stands for the maximum safe Number, and the result is 9007199254740991, that is, there is no loss of precision (except for decimals) within this Number range. However, once the range is exceeded, JS will have inaccurate calculation, which has to rely on some third-party libraries to solve the problem when large numbers are calculated. Therefore, BigInt is officially proposed to solve this problem.

What is the difference between null and undefined?

Null means null, which means there should be no value. An object can be NULL, which means it’s an empty object, and NULL itself is an object. Undefined means “does not exist”, JavaScript is a dynamically typed language, and members may not exist at all (because existence is only known at runtime), which is why undefined is so important.

Why doesn’t 0.1+0.2 equal 0.3?

Number
value = sign x exponent x fraction





  1. Convert 0.1 to a binary representation
  2. The converted binary is represented by scientific notation
  3. Converts the binary represented by scientific notation to the IEEE 754 standard representation

Convert 0.1 to a binary representation

Review how the decimal part of a number is converted to binary. Multiply the decimal part of a number by 2, then take the result of the integer part, and repeat the calculation with the decimal part until the decimal part is 0. So the process of converting 0.1 to binary representation is as follows:

The decimal The results of the x2 The integer part
0.1 0.2 0
0.2 0.4 0
0.4 0.8 0
0.8 1.6 1
0.6 1.2 1
0.2 0.4 0
0.4 0.8 0
0.8 1.6 1
0.6 1.2 1
. . .

The binary representation of 0.1 is 0.00011… (Repeat 0011 indefinitely)

In scientific notation

0.00011… (infinite repetition 0011) by scientific notation is 1.10011001… (Wireless repeat 1001)*2

Convert to IEEE 754 standard representation

And when you do it scientifically, you have the exponent bias and the fraction. Exponent bias is equal to the 11-bit binary representation of the fixed offset of a double floating-point number (2-1) plus the actual value of the exponent (that is, -4 of 2). Why 11? Because the exponent bias is 11 bits out of 64. So 0.1 exponent bias is equal to 1023 + (-4) = the 11-bit binary representation of 1019, i.e. 011 1111 1011. The fraction is 1.10011001… Number of decimal places (1001), number of decimal places (1001), number of decimal places (1001), number of decimal places (1001) (There are 11 1001 in the middle)… 1010 (please note that the last four digits are 1010, not 1001, because the rounding is carried, which is why 0.1 + 0.2 does not equal 0.3)

0 011 1111 1011 1001... ( 11 x 1001)... 1010 (sign bit) (exponent bias) (fraction)Copy the code

At this time if the number is converted to a decimal, can find value has become 0.100000000000000005551115123126 rather than 0.1, so the calculation accuracy is a problem.

What are the rules for type conversion?

Hermit-type conversions can occur in if statements, logical statements, mathematical operation logic, ==, and so on.

What is the principle behind type conversions?

Casting refers to converting one type to another, for example:

var b = 2;
var a = String(b);
console.log(typeof a); //string
Copy the code

Type conversion, of course, divided into explicit and implicit, but either implicitly or explicitly convert, follow the certain principle, because JavaScript is a dynamically typed language, can be given any value at any time, but all sorts of operator or conditions requires a specific type of judgment, so the JavaScript engine will be set as a variable in the operation Type. This looks nice, the JavaScript engine takes care of the type problem, but the engine is not ASI, and many of its actions will be far from what we expect, so we can start with the interview question.

{} + []/ / 0
Copy the code

The answer is zero. What could be causing this? So let’s start with the transformation rules and abstractions mentioned in ECMA-262. If you are interested, you can peruse the extensive language specification. If you don’t have the patience, please read on. This is JavaScript type conversion from a primitive type to a reference type. You can also convert a reference type to a primitive type. The abstract operation to a primitive type is ToPrimitive, followed by ToNumber ToString ToBoolean. To further explore how JavaScript engines handle type conversions in code, we need to look at the detailed specification of ECMA-262 to explore its internals. We’ll start with this internals code.

Ecma-262, Section 9.1, Page 30. Use null/undefined for no hint,
// (1) for number hint, and (2) for string hint.
function ToPrimitive(x, hint) {  
  // Fast case check.
  if (IS_STRING(x)) return x;
  // Normal behavior.
  if(! IS_SPEC_OBJECT(x))return x;
  if (IS_SYMBOL_WRAPPER(x)) throw MakeTypeError(kSymbolToPrimitive);
  if (hint == NO_HINT) hint = (IS_DATE(x)) ? STRING_HINT : NUMBER_HINT;
  return (hint == NUMBER_HINT) ? DefaultNumber(x) : DefaultString(x);
}
// ECMA-262, section 8.6.2.6, page 28.
function DefaultNumber(x) {  
  if(! IS_SYMBOL_WRAPPER(x)) {var valueOf = x.valueOf;
    if (IS_SPEC_FUNCTION(valueOf)) {
      var v = %_CallFunction(x, valueOf);
      if (IsPrimitive(v)) return v;
    }
    var toString = x.toString;
    if (IS_SPEC_FUNCTION(toString)) {
      var s = %_CallFunction(x, toString);
      if (IsPrimitive(s)) returns; }}throw MakeTypeError(kCannotConvertToPrimitive);
}
// ECMA-262, section 8.6.2.6, page 28.
function DefaultString(x) {  
  if(! IS_SYMBOL_WRAPPER(x)) {var toString = x.toString;
    if (IS_SPEC_FUNCTION(toString)) {
      var s = %_CallFunction(x, toString);
      if (IsPrimitive(s)) return s;
    }
    var valueOf = x.valueOf;
    if (IS_SPEC_FUNCTION(valueOf)) {
      var v = %_CallFunction(x, valueOf);
      if (IsPrimitive(v)) returnv; }}throw MakeTypeError(kCannotConvertToPrimitive);
}
Copy the code

The logic of the above code looks like this:

  1. If the variable is a string, return it.
  2. if! IS_SPEC_OBJECT(x), return directly.
  3. ifIS_SYMBOL_WRAPPER(x), throws an exception.
  4. Otherwise it will be based on incominghintTo invoke theDefaultNumberandDefaultString, for example, ifDateObject is calledDefaultString.
  5. DefaultNumber: the firstFirst x.v alueOfIf forprimitive, the returnvalueOfAfter the value, otherwise continue the callx.toStringIf forprimitive, the returntoStringAfter the value, otherwise an exception is thrown
  6. DefaultStringAnd:DefaultNumberOn the contrary, call firsttoStringIf notprimitiveCall againvalueOfThat’s how it works. ThisToPrimitiveWhat’s the use? In fact many operations will be calledToPrimitive, such as add, equal, or compare operations. The left and right operands are converted toprimitiveAnd then add them up. For example, what does ({}) + 1 ({} is put in parentheses so that the kernel treats it as a code block) output? You probably wouldn’t write code like this, but there are interview questions like this online. The add operation only has the left and right operatorsA String or NumberWill execute the corresponding% % _StringAdd or NumberAddLet’s see({}) + 1What steps will be taken internally:{}and1ToPrimitive is called first{}Go to theDefaultNumber, is called firstvalueOfIs returnedObject {}Not quite primitive type, so go on totoStringTo return to[object Object], it isStringType finally adds the operation, resulting in[object Object]1Or let’s say someone asks you[] + 1When you output something, you probably know how to calculate it, right[]callToPrimitive, returns an empty string, resulting in “1”.

What is your understanding of the prototype chain?

The key to this question is two points, one is what is the prototype object, and the other is how is the prototype chain formed

A prototype object

Most functions (with a few exceptions built in) have a Prototype property that the prototype object uses to create new instances of the object. All created objects share the prototype object, so they have access to its properties. For example, the hasOwnProperty() method exists in the Obejct prototype object, which can be used by any object as its own method.

The return value of the hasOwnProperty() function is a Boolean. Returns true if the object has a property named propertyName, false otherwise.

 var person = {
    name: "Messi".age: 29.profession: "football player"
  };
console.log(person.hasOwnProperty("name")); //true
console.log(person.hasOwnProperty("hasOwnProperty")); //false
console.log(Object.prototype.hasOwnProperty("hasOwnProperty")); //true
Copy the code

As you can see from the code above,hasOwnProperty() does not exist in a Person object, but a Person can still have this method. So how does a Person Object find a method in an Object Object? It’s the prototype chain.

Prototype chain

The reason is that every object has a __proto__ attribute, which points to the prototype of that object’s constructor. An object can be connected to the prototype object of an upstream constructor by __proto__, which also has a __proto__, thus forming a prototype chain.

Classic prototype chain diagram

How to determine if it is an array?

New judgment methods have been added in ES6

if(Array. IsArray (value)) {return true;
}
Copy the code

The toString method can be used for compatibility purposes

if(!Array.isArray){
    Array.isArray = function(arg){
        return Object.prototype.toString.call(arg)==='[object Array]'}}Copy the code

What do you know about this?

The direction of this is determined not at writing time, but at execution time, and the different direction of this is that it follows certain rules.

First, by default, this points to a global object, such as a window in a browser.

name = "Bale";
function sayName () {
    console.log(this.name);
};
sayName(); //"Bale"
Copy the code

Second, functions are implicitly bound if there is a context object where they are called.

function f() {
    console.log( this.name );
}
var obj = {
    name: "Messi".f: f
};
obj.f(); // The location being called happens to be owned by the object obj, so Messi is the result
Copy the code

Again, to change this, the common methods are call, apply, and bind

Take bind as an example:

function f() {
    console.log( this.name );
}
var obj = {
    name: "Messi"};var obj1 = {
     name: "Bale"
};
f.bind(obj)(); //Messi, because bind returns a new function after binding obj to f, it needs to be executed with parentheses. This is the difference between bind and apply and call
Copy the code

Finally, the new binding, which has the highest priority.

Calling a constructor with new creates a new object, which is automatically bound to this of the Person object during creation, so this naturally refers to the new object.

function Person(name) {
  this.name = name;
  console.log(name);
}
var person1 = new Person('Messi'); //Messi
Copy the code

Binding priority: New binding > Explicit binding > Implicit binding > Default binding

So where does the arrow function’s this point to?

Unlike traditional JavaScript functions, arrow functions do not have their own this. Their so-called this captures the value of this in their context as their own this, and since they do not have their own this, arrow functions are not called by new. This so-called this is not going to change.

We can use Babel to understand the arrow function:

// ES6
const obj = {
    getArrow() {
        return (a)= > {
            console.log(this=== obj); }; }}Copy the code

After the transformation

// ES5, translated by Babel
var obj = {
    getArrow: function getArrow() {
        var _this = this;
        return function () {
            console.log(_this === obj); }; }};Copy the code

What is async/await?

The async function, which is the synsyntax candy for Generator functions, is built on Promises and is compatible with all existing Promise based apis.

  1. Async — Declare an asynchronous function (Async function someName(){… })
  • Automatically converts a regular function to a Promise, and the return value is also a Promise object
  • The callback function specified by the THEN method is executed only after the asynchronous operation inside the async function is complete
  • Async functions can use await internally
  1. Await – suspend asynchronous function execution (var result = Await someAsyncCall();)
  • Placed before a Promise call, await forces the rest of the code to wait until the Promise completes and returns the result
  • Can only be used with promises, not with callbacks
  • Can only be used inside async functions

Advantages of async/await over Promise?

  • The code reads more synchronously, and Promise gets rid of callback hell, but the chained calls to THEN carry an additional reading burden
  • Promise passing intermediate values is cumbersome, while async/await is almost synchronous and elegant
  • Error handling is friendly, async/await can be used with mature try/catch, and Promise error catching is very redundant
  • Debug-friendly, Promise debugs badly. Because there’s no code block, you can’t set a breakpoint in an arrow function that returns an expression. If you use the debugger’s step-over function in a.then block, the debugger doesn’t go into subsequent.then blocks. Because the debugger can only track “every step” of the synchronized code.

How are JavaScript parameters passed?

Base type delivery

Because there are complex types and primitive types in JS, for primitive types, they are passed by value.

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

Although a is modified in the test function, it does not affect the value of the external A, the base type is passed by value.

Complex types passed by reference?

We pass the outer a as an object to the test function.

var a = {
  a: 1.b: 2
};
function test(x) {
  x.a = 10;
  console.log(x);
}
test(a); // { a: 10, b: 2 }
console.log(a); // { a: 10, b: 2 }
Copy the code

As you can see, the a object being modified inside the function also affects the external A object, so complex types are passed by reference.

But if we do another experiment:

var a = {
  a: 1.b: 2
};
function test(x) {
  x = 10;
  console.log(x);
}
test(a); / / 10
console.log(a); // { a: 1, b: 2 }
Copy the code

The external a is not modified. If passed by reference,a will also appear as 10 externally because it shares the same heap memory. Complex types at this point exhibit both value – and reference-passing properties.

How to implement immutable objects in JavaScript?

There are three main approaches to immutable data

  1. Deep cloning, but the performance of deep cloning is very poor, not suitable for large-scale use
  2. Immutable. Js, Immutable. Js is a self-contained data structure that performs well, but requires additional API learning
  3. Immer, using the Proxy feature, does not need to learn additional API, good performance