“This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!”

preface

I only want to meet a CV engineer, but the interviewer asked me to challenge a rocket engineer. However, I still have to continue to live a difficult life, and I still have to continue to find a job. Did not notice to accumulate at ordinary times, the interview always answers not comprehensive.

1. What are the JS primitive data types? What are the reference data types?

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

Reference data type: Object Object (contains normal object-object, Array object-array, regular object-regexp, Date Object, Math function-math, Function object-function)

2. Say the result of the following operation and explain why.

function test(person) { person.age = 26 person = { name: 'hzj', age: 18 } return person } const p1 = { name: 'fyq', age: 19 } const p2 = test(p1) console.log(p1) // -> ? console.log(p2) // -> ? P1: {name: 'fyq', age: 26} p2: {name: 'HZJ', age: 18}Copy the code

The reason: Age = 26. The person argument in test is the memory address of p1. Calling person.age = 26 does change the value of p1, but then person becomes the address of another chunk of memory. At the end, the address of this other memory space is returned and assigned to P2.

3. Is NULL an object? Why is that?

Conclusion: NULL is not an object. Note: Although Typeof null will print object, this is a long-standing Bug in JS. In the original version of JS, the 32-bit system was used. For performance purposes, the type information of variables was stored in low order. The beginning of 000 represents an object, while null represents all zeros, so it was wrongly identified as object.

4. Why can ‘1’.toString() be called?

While this statement is running, it does a few things:

var s = new Object('1');
s.toString();
s = null;
Copy the code

Step 1: Create an Object class instance. Notice why it’s not a String? Because of the presence of Symbol and BigInt, calling new on both of them will cause errors, and the current ES6 specification does not recommend using new to create wrapper classes for basic types. Step 2: Call the instance method. Step 3: Destroy the instance immediately after executing the method. The whole process reflects the nature of the basic wrapper types, which belong to the basic data types, including Boolean, Number, and String.

Why does 5.0.1+0.2 not equal 0.3?

0.1 and 0.2 will loop indefinitely after conversion to binary. Due to the limit of standard bits, the excess bits will be cut off after the conversion. At this time, the loss of precision has already occurred.

6. What is BigInt?

BigInt is a new data type used when integer values are greater than the range supported by the Number data type. This data type allows us to safely perform arithmetic operations on large integers, represent high-resolution timestamps, use large integer ids, and so on, without the need for libraries.

7. Why do you need BigInt?

In JS, all numbers are represented in double precision 64 bit floating point format. What’s the problem with that? This causes the Number in JS not to accurately represent very large integers, it will round very large integers, to be exact, The Number type in JS can only safely represent -9007199254740991(-(2^53-1)) and 9007199254740991((2^53-1)), any integer value outside this range may lose accuracy.

console.log(999999999999999); / / = > 10000000000000000Copy the code

At the same time, there will be certain security issues:

9007199254740992 = = = 9007199254740993; // → true!Copy the code

How to create and use BigInt?

To create a BigInt, simply append n to the end of the number

console.log( 9007199254740995n ); // → 9007199254740995n console.log(9007199254740995); / / - 9007199254740996Copy the code

Another way to create a BigInt is to use the BigInt() constructor

BigInt("9007199254740995"); / / - > 9007199254740995 nCopy the code

Simple use is as follows:

10n + 20n; // → 30n 10n-20n; / / - - 10 n + n; // → TypeError: Cannot convert a BigInt value to a number-10n; // → -10n 10n * 20n; // → 200n 20n / 10n; // → 2n 23n % 10n; // → 3n 10n ** 3n; // → const x = 10n; ++x; / / - n - x 11; // → 9n console.log(typeof x); //"bigint"Copy the code

A point of caution

BigInt does not support the unary plus operator, which may be because some programs may rely on + to always generate an invariant of Number, or to throw an exception. In addition, changing the behavior of + can also break the asm.js code.

(2) Do not allow mixing between bigint and Number because implicit conversions may lose information. When mixing large integers and floating point numbers, the resulting value may not be accurately represented by BigInt or Number.

10 + 10n; / / > TypeErrorCopy the code

(3) You cannot pass BigInt to Web API and built-in JS functions, which require a Number of type Number. Attempting to do so will result in TypeError.

(4) When a Boolean type meets a BigInt type, BigInt is treated like a Number. In other words, as long as it is not 0n, BigInt is treated as a Truthy value.

(5) An array with BigInt elements can be sorted.

(6) BigInt can be a normal operation, such as |, &, < <, > > and ^

9. Can typeof correctly determine the type?

For primitive types, except for NULL, typeof can be called to display the correct type.

typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
Copy the code

For reference data types, however, “object” is displayed except for functions.

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

Therefore, it is not appropriate to use typeof to judge the data typeof the object, and it is better to use instanceof. The principle of instanceof is based on the query of the prototype chain, and the judgment is always true as long as it is in the prototype chain

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

10. Can instanceof determine the basic data type?

Can. Like this:

class PrimitiveNumber {
    static [Symbol.hasInstance](x) {
        return typeof x === 'number'
    }
}
console.log(111 instanceof PrimitiveNumber) // true
Copy the code

In fact, it is a way to customize the behavior of instanceof. In this case, the original instanceof method is redefined and replaced with typeof, so that the basic data type can be determined.

11. Could you manually implement the function of Instanceof?

Function myInstanceof(left, right) {return false if(typeof left! == 'object' || left === null) return false; // Let proto = object.getPrototypeof (left); // Let proto = object.getPrototypeof (left); While (true) {if(proto == null) return false; If (proto == right.prototype) return true; proto = Object.getPrototypeOf(proto); }}Copy the code
console.log(myInstanceof("111", String)); //false console.log(myInstanceof(new String("111"), String)); //trueCopy the code

12. What is the difference between object. is and ===?

– Object fixes some special cases of errors based on strict equals, specifically +0 and -0, NaN and NaN. Source code is as follows:

Function is(x, y) {if (x === y) {if (x === y) {return x! == 0 || y ! == 0 || 1 / x === 1 / y; } else {//NaN===NaN is false, this is wrong, let's do an intercept here, x! == x, y == NaN, y == NaN == x && y ! == y; }Copy the code

13. [] = =! [] What was the result? Why is that?

==, both sides need to be converted to numbers and then compared.

[] converts the number to 0.

! [] is first converted to a Boolean value, since [] is converted to true as a reference type, so! [] is false and is then converted to a number, which is 0. 0 == 0, the result is true

14. According to what process is the transfer of an object to its original type run?

When an object is converted to its original type, it calls the built-in [ToPrimitive] function, for which the logic is as follows:

  1. If the Symbol.toprimitive () method is called first and then returned
  2. Call valueOf(), which returns if converted to the original type
  3. Call toString(), which returns if converted to a primitive type
  4. If neither returns the original type, an error will be reported
var obj = { value: 3, valueOf() { return 4; }, toString() { return '5' }, [Symbol.toPrimitive]() { return 6 } } console.log(obj + 1); / / output 7Copy the code

If (a == 1 && a == 2)

var a = {
    value: 0,
    valueOf: function() {
    this.value++;
    return this.value;
   }
};
console.log(a == 1 && a == 2);//true
Copy the code

16. What are closures?

In the Little Red Book, closure is defined as a function that has access to variables in the scope of another function. MDN defines closure as a function that has access to free variables. (A free variable is a variable used in a function that is neither arguments nor local variables to the function, but is actually a variable in another function scope.)

17. What causes closures?

Must first understand the concept of the scope chain, actually very simple, there are only two kinds of scope in the ES5 — – the global scope and function scope, when to access a variable, the interpreter will first in the current scope find identifier, if not found, will go to the parent scope, straight to find the variable’s identifier or not in the parent scope, This is the scope chain. Note that each subfunction copies its parent scope, forming a chain of scopes. Such as:

var a = 1; function f1() { var a = 2 function f2() { var a = 3; console.log(a); / / 3}}Copy the code

In this code, the scope of F1 points to the global scope (window) and itself, and the scope of F2 points to the global scope (window), F1, and itself. The scope is searched from the bottom up until the global scope window is found. If the global scope is not found, an error will be reported. It’s that simple! The essence of closure generation is that there is a reference to the parent scope in the current environment. Again, take the example above:

function f1() { var a = 2 function f2() { console.log(a); //2 } return f2; } var x = f1(); x();Copy the code

Here x is going to get the variable in the parent scope, and it’s going to print 2. Because in the current environment, there is a reference to F2, and F2 refers exactly to the scope of window, f1, and F2. So F2 can access variables in the scope of F1. Does that mean that only the return function generates a closure? Going back to the nature of closures, we only need to make references to the parent scope exist, so we can also do this:

var f3;
function f1() {
var a = 2
f3 = function() {
console.log(a);
}
}
f1();
f3();

Copy the code

Let f1 execute, assign a value to f3, which means that now F3 has access to window, f1, and f3 itself, again looking from the bottom up, and recently found a in F1, so output 2. In this case, there is a reference to the parent scope of the outer variable F3, hence the closure. The form has changed, but the essence has not.

18. How to solve the following loop output problem?

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

Why do all 6’s come out? How can I improve it so that it outputs 1,2,3,4,5? (The more methods, the better) because setTimeout is a macro task, due to the single-threaded eventLoop mechanism in JS, the macro task is not executed until the main step task is finished, so the callback in setTimeout is executed in turn after the end of the loop, but the current scope is not found when the output I, so look for the next level. I found I, and now the loop is over, and I becomes 6. So it’s going to print out all 6’s.

Solution:

1. Use IIFE(execute function expression immediately) to pass the variable I to the timer each time the for loop is executed

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

Pass the third parameter to the timer as the first parameter of the timer function

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

3, use ES6 let

for(let i = 1; i <= 5; i++){
setTimeout(function timer(){
console.log(i)
},0)
}

Copy the code

Let has revolutionized JS by changing the scope of JS functions into block-level scope. With let, scope chain no longer exists. The scope of code is block-level. Take the above code as an example:

// i = 1
{
setTimeout(function timer(){
console.log(1)
},0)
}
// i = 2
{
setTimeout(function timer(){
console.log(2)
},0)
}
// i = 3
...
Copy the code

How to implement inheritance in JS?

One: with call

function Parent1(){
this.name = 'parent1';
}
function Child1(){
Parent1.call(this);
this.type = 'child1'
}
console.log(new Child1);
Copy the code

In this case, the subclass can get the property value of the parent class, but the problem is that if there is a method in the parent class prototype object, the subclass cannot inherit it. So this leads to the following method.

Second: with prototype chains

function Parent2() {
this.name = 'parent2';
this.play = [1, 2, 3]
}
function Child2() {
this.type = 'child2';
}
Child2.prototype = new Parent2();
console.log(new Child2());
Copy the code

Third: Combine the first two

function Parent3 () {
this.name = 'parent3';
this.play = [1, 2, 3];
}
function Child3() {
Parent3.call(this);
this.type = 'child3';
}
Child3.prototype = new Parent3();
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play);
Copy the code

Why is arguments not an array? How do I convert to an array?

Arguments does not call array methods by itself. It is a different object type, but the properties are sorted from 0, 0, 1, 2… Finally, we have the Callee and length properties. We also refer to such objects as class arrays. Common class arrays include:

  • Using getElementsByTagName/ClassName HTMLCollection ()
  • The nodeList obtained by querySelector

So that makes a lot of the array methods unavailable, and we need to convert them to an array if we need to. What are the methods?

Array.prototype.slice.call()

function sum(a, b) { let args = Array.prototype.slice.call(arguments); console.log(args.reduce((sum, cur) => sum + cur)); } sum(1, 2); / / 3Copy the code

Array.from()

function sum(a, b) { let args = Array.from(arguments); console.log(args.reduce((sum, cur) => sum + cur)); } sum(1, 2); / / 3Copy the code

ES6 expansion operator

function sum(a, b) { let args = [...arguments]; console.log(args.reduce((sum, cur) => sum + cur)); } sum(1, 2); / / 3Copy the code

Using the concat + the apply

function sum(a, b) { let args = Array.prototype.concat.apply([], arguments); Log (args.reduce((sum, cur) => sum + cur))); } sum(1, 2); / / 3Copy the code

21. Does return work in forEach? How do I break a forEach loop?

Using a return in forEach does not return, and the function continues.

let nums = [1, 2, 3]; nums.forEach((item, index) => { return; / / null})Copy the code

Interrupt methods: 1) Monitor a block of code with a try and throw an exception where an interrupt is needed. 2) The official recommended method (substitution method) : Replace forEach functions with every and some. Every terminates the loop when it hits return false. Some terminates the loop when it hits return true

22. Is it possible to simulate a new effect?

New does three things when it is called:

  1. Make instances accessible to private properties
  2. Gives the instance access to properties on the stereotype chain where the constructor. Prototype resides
  3. If the constructor returns a result that is not a reference data type
function newOperator(ctor, ... args) { if(typeof ctor ! == 'function'){ throw 'newOperator function the first param must be a function'; } let obj = Object.create(ctor.prototype); let res = ctor.apply(obj, args); let isObject = typeof res === 'object' && res ! == null; let isFunction = typoof res === 'function'; return isObect || isFunction ? res : obj; };Copy the code

23. Is it possible to simulate a bind effect?

Before we can implement bind, we first need to know what it does.

  1. For normal functions, bind this to
  2. For constructors, ensure that the properties on the prototype object of the original function are not lost
Function.prototype.bind = function (context, ... Args) {// If (typeof this! == "function") { throw new Error("Function.prototype.bind - what is trying to be bound is not callable"); } // Hold the value of this, which represents the call to bind var self = this; var fNOP = function () {}; var fbound = function () { self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments))); } fNOP.prototype = this.prototype; fbound.prototype = new fNOP(); return fbound; }Copy the code

You can also use object.create to handle prototypes this way:

Function.prototype.bind = function (context, ... args) { if (typeof this ! == "function") { throw new Error("Function.prototype.bind - what is trying to be bound is not callable"); } var self = this; var fbound = function () { self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments))); } fbound.prototype = Object.create(self.prototype); return fbound; }Copy the code

24. Javascript garbage collection mechanism talk about

Definition: A block of allocated memory that cannot be used or reclaimed until the browser process ends. Programming languages such as C have low-level memory management primitives such as malloc() and free(). Developers use these primitives to explicitly allocate and free memory for the operating system. JavaScript, on the other hand, allocates memory for objects (objects, strings, etc.) when they are created and “automatically” frees memory when the pair is no longer used, a process known as garbage collection.

Each stage in the memory life cycle:

Allocate memory – Memory is allocated by the operating system, which allows your programs to use it. In low-level languages, such as C, this is an explicitly performed operation that the developer needs to handle on his own. However, in high-level languages, the system automatically assigns you internals. Use memory – This is when the program actually uses previously allocated memory, and when the allocated variables are used in the code, read and write operations occur. Free memory – Free all memory that is no longer used so that it is free and can be reused. Like the memory allocation operation, this operation needs to be performed explicitly in low-level languages.

25. Anti-shaking and throttling

1. If you

Shaking, as the name suggests, to prevent shaking, so as not to mistake one event for many times, tapping the keyboard is a daily exposure to the shaking operation.

  • To understand a concept, you must first understand the scenario in which the concept is applied. In the JS world, what is the scene of anti – shaking
  • Buttons such as login and texting need to be stabilised to prevent users from clicking too quickly and sending multiple requests
  • When resizing the browser window, the number of resize times is too frequent, resulting in excessive calculation. At this time, it needs to be in place once, which is used to stabilize
  • The text editor saves in real time, when no changes are performed for one second
function debounce (f, wait) { let timer return (... args) => { clearTimeout(timer) timer = setTimeout(() => { f(... args) }, wait) } }Copy the code
2. The throttle

Throttling, as the name suggests, controls the flow of water. Control the frequency of events. For example, once every 1s, or even once every minute. This is similar to the Rate Limit controlled by the server and gateway.

  • Scroll event, the position information is calculated every second
  • The browser plays events, calculates progress every second, and so on
  • Input box real-time search and send requests display a drop-down list, every second to send a request (can also do anti-shake)
function throttle (f, wait) { let timer return (... args) => { if (timer) { return } timer = setTimeout(() => { f(... args) timer = null }, wait) } }Copy the code

26. Bubble sort algorithm

var array = [5, 4, 3, 2, 1]; var temp = 0; for (var i = 0; i <array.length; i++){ for (var j = 0; j <array.length - i; j++){ if (array[j] > array[j + 1]){ temp = array[j + 1]; array[j + 1] = array[j]; array[j] = temp; }}Copy the code

27. Write a function that clears the Spaces before and after the string. (Compatible with all browsers)

function trim(str) { if (str & typeof str === "string") { return str.replace(/(^\s*)|(\s*)$/g,""); }}Copy the code

28. Manually implement the array.prototype. map method

Function map(arr, mapCallback) {// First, check whether the parameters passed are correct. if (! Array.isArray(arr) || ! arr.length || typeof mapCallback ! == 'function') { return []; } else { let result = []; // Each time we call this function, we will create a result array because we don't want to change the original array. for (let i = 0, len = arr.length; i < len; i++) { result.push(mapCallback(arr[i], i, arr)); // Push the result returned by mapCallback into the result array; }}Copy the code

29. Manually implement the array.prototype. filter method

Function filter(arr, filterCallback) {// First, check whether the parameters passed are correct. if (! Array.isArray(arr) || ! arr.length || typeof filterCallback ! == 'function') { return []; } else { let result = []; // Each time we call this function, we will create a result array because we don't want to change the original array. for (let i = 0, len = arr.length; i < len; I ++) {// Check whether the return value of filterCallback is true if (filterCallback(arr[I], I, arr)) {// If the condition is true, Result. Push (arr[I]); } } return result; // return the result array } }Copy the code

30. Manually implement the array.prototype. reduce method

Function reduce(arr, reduceCallback, initialValue) {// First, check whether the parameters passed are correct. if (! Array.isArray(arr) || ! arr.length || typeof reduceCallback ! == 'function') { return []; } else {// If we did not pass an initialValue to this function, we will use the first array item as initialValue. Let hasInitialValue = initialValue! == undefined; let value = hasInitialValue ? initialValue : arr[0]; For (let I = hasInitialValue? Let I = hasInitialValue? 1 : 0, len = arr.length; i < len; i++) { value = reduceCallback(value, arr[i], i, arr); } return value; }}Copy the code

Previous excellent article

  • HTML + CSS interview: the front end of the interview in June 2021 | HTML + CSS
  • Vue Interview questions: Answer these VUE interview questions correctly, am I a qualified intermediate front-end development engineer?