One, foreword

Say job-hopping good season, gold three silver four, gold nine silver ten. Last week, I also succeeded in job-hopping and joined a new company. The road of job-hopping was bumpy, and I met meituan and Byte outsourcing posts, as well as some strange interviews.

In the last article, I promised to compile some handwritten interview questions for you. It’s coming now!

Most of the topics in this article came across when I went for an interview myself. If you have any mistakes or better answers, please leave them in the comments section

Second, the subject

1. Anti-shake and throttling

This is also a classic topic, first of all to know what is anti – shake, what is throttling.

  • Buffeting: Events will only trigger one last time over a period of time.

  • Throttling: Events that are triggered at intervals of time.

    If you really don’t understand, you can go to this big guy’s Demo address to play anti-shake throttling Demo

     / / image stabilization
    function debounce(fn) {
      let timeout = null; 
      return function () {
        // If the event is triggered again, clear the timer and restart the timer
        clearTimeout(timeout);
        timeout = setTimeout(() = > {
          fn.apply(this);
        }, 500);
      };
    }
    
    / / throttling
    function throttle(fn) {
      let flag = null; // Save a tag through a closure
      return function () {
        if (flag) return; // The flag is always false when the timer is not executed and is returned at the beginning
        flag = setTimeout(() = > {
          fn.apply(this.arguments);
           // Set the flag to true after setTimeout (critical)
           // Indicates that the next loop is ready to execute.
          flag = null;
        }, 500);
      };
    }
    
Copy the code

This question is mainly to check the understanding of anti – shake throttling bar, do not remember the reverse!

2. A regular question

Write the area code and 8-digit digits, or the area code and special number: 10010/110 separated by a short line. The area code starts with three digits.

For example 010-12345678

 let reg = /^\d{3}-(\d{8}|10010|110)/g
Copy the code

This is relatively simple, familiar with the basic use of re can be done.

3. How to implement the functions of a label without using a label

 // The window.open and location.href methods do this.
 // Correspond to the blank and self attributes of the A tag, respectively
Copy the code

4. Do not use the loop API to delete elements at specified positions in the array (e.g., delete the third bit). Write as much as possible

A non-looping API (for filter, for example).


var arr = [1.2.3.4.5.6.7.8.9.10]

// Method 1: splice operates on an array and changes the original array
arr.splice(2.1)


// Method 2: Slice returns a new array without changing the original array
arr.slice(0.2).concat(arr.slice(3)),// Delete the element in the array and delete the element
delete arr[2]
arr.join("").replace("empty"."").split("")
Copy the code

5. Deep copy

That’s the difference between a deep copy and a shallow copy

  • Shallow copy: For complex data types, shallow copy simply assigns the reference address to the new object. Changing the value of the new object changes the value of the original object.
  • Deep copy: For complex data types, the address references are all new after the copy. Changing the value of the new object does not affect the value of the original object.

So the key is to deal with complex data types, and HERE I’ve written two ways, the second of which has a partial performance improvement over the first

const isObj = (val) = > typeof val === "object"&& val ! = =null;

/ / write 1
function deepClone(obj) {
    // Use instanceof to determine if the variable you want to copy is an array (if not an array, then an object).

    // 1. Prepare the variable you want to return (the new address).
    const newObj = obj instanceof Array ? [] : {}; // Core code.

    // 2. The simple data type only needs to be assigned, and if it encounters a complex data type, it goes back into deep copy until it finds a simple data type.
    for (const key in obj) {
        const item = obj[key];
        newObj[key] = isObj(item) ? deepClone(item) : item;
    }

    // 3. Return the copied variables.
    return newObj;
}




WeakMap has better weak reference performance and supports Symbol
function deepClone2(obj, wMap = new WeakMap(a)) {
  if (isObj(obj)) {
    // Check whether it is an object or an array
    let target = Array.isArray(obj) ? [] : {};

    // If this exists, return it directly
    if (wMap.has(obj)) {
      return wMap.get(obj);
    }

    wMap.set(obj, target);

    // Iterate over the object
    Reflect.ownKeys(obj).forEach((item) = > {
      // If it is a complex data type, continue the recursive call
      target[item] = isObj(obj[item]) ? deepClone2(obj[item], wMap) : obj[item];
    });

    return target;
  } else {
    returnobj; }}Copy the code

The main solution to this problem is recursion plus data type judgment.

For complex data types, recursively call your copy method again until it can be directly assigned to a simple data type

6. Call bind apply by hand

All call bind apply does is you can modify this point

  • The difference between Call and apply is the way arguments are passed
  • Bind differs in that it returns a function at the end.

    // call
    Function.prototype.MyCall = function (context) {
      if (typeof this! = ="function") {
        throw new Error('type error')}if (context === null || context === undefined) {
        // This with null and undefined will automatically point to the global object (window in browsers)
        context = window
      } else {
        // This with a primitive value (number, string, Boolean) refers to an instance object of that primitive value
        context = Object(context)
      }

      // Use Symbol to determine uniqueness
      const fnSym = Symbol(a)// Simulate the object's this pointer
      context[fnSym] = this

      // Get parameters
      const args = [...arguments].slice(1)

      // Bind the parameters and execute the function
      constresult = context[fnSym](... args)// Clear the defined this
      delete context[fnSym]

      // Return the result
      return result
    } 
    
    
    // call apply is a matter of changing parameters
    // apply
    Function.prototype.MyApply = function (context) {
      if (typeof this! = ="function") {
        throw new Error('type error')}if (context === null || context === undefined) {
        // This with null and undefined will automatically point to the global object (window in browsers)
        context = window
      } else {
        // This with a primitive value (number, string, Boolean) refers to an instance object of that primitive value
        context = Object(context) 
      }


      // Use Symbol to determine uniqueness
      const fnSym = Symbol(a)// Simulate the object's this pointer
      context[fnSym] = this

      // Get parameters
      const args = [...arguments][1]

      // Bind parameters and execute functions. Since apply passes in an array, it needs to be destructed
      const result = arguments.length > 1? context[fnSym](... args) : context[fnSym]()// Clear the defined this
      delete context[fnSym]

      // Returns the result // clears the defined this
      return result
    }
    
    
    
    // bind
    Function.prototype.MyBind = function (context) {
      if (typeof this! = ="function") {
        throw new Error('type error')}if (context === null || context === undefined) {
        // This with null and undefined will automatically point to the global object (window in browsers)
        context = window
      } else {
        // This with a primitive value (number, string, Boolean) refers to an instance object of that primitive value
        context = Object(context) 
      }

      // Simulate the object's this pointer
      const self = this

      // Get parameters
      const args = [...arguments].slice(1)
        
      // Finally return a function and bind this, taking into account the new call and the fact that bind can pass arguments
      return function Fn(. newFnArgs) {
        if (this instanceof Fn) {
            return newself(... args, ... newFnArgs) }return self.apply(context, [...args, ...newFnArgs])
        }
    }
Copy the code

7. Handwriting implementation inheritance

I’ll only implement two methods here, parasitic combinatorial inheritance before ES6 and class inheritance after ES6.

    /** * es6 before the parasitic combination inheritance */
    {
      function Parent(name) {
        this.name = name
        this.arr = [1.2.3]
      }

      Parent.prototype.say = () = > {
        console.log('Hi');
      }

      function Child(name, age) {
        Parent.call(this, name)
        this.age = age
      }

      // The core code creates new Object subclasses and superclasses through object.create
      // object. create: Creates a new Object, using an existing Object to provide the __proto__ of the newly created Object
      Child.prototype = Object.create(Parent.prototype)
      Child.prototype.constructor = Child
    }
    
    
    
    /** * es6 inherits the keyword class */
     {
      class Parent {
        constructor(name) {
          this.name = name
          this.arr = [1.2.3]}}class Child extends Parent {
        constructor(name, age) {
          super(name)
          this.age = age
        }
      }
    }
Copy the code

As a side note, ES6 Class inheritance uses parasitic combinatorial inheritance when converted to ES5 code via Babel.

There are many methods of inheritance, remember the above two basic can!

Write the new operator by hand

First we need to know what happens to an object when it’s new.

You’re essentially creating an object internally, attaching your properties and all that to that object, and then returning that object.

function myNew(fn, ... args) {
  // Create a new object based on the prototype chain
  let newObj = Object.create(fn.prototype)

  // Add attributes to the new object and get the result of the obj function
  letres = fn.call(newObj, ... args)If the result of the execution has a return value and is an object, the result of the execution is returned; otherwise, the newly created object is returned
  return res && typeof res === 'object' ? res : newObj;
}
Copy the code

9. The js implementation mechanism says the result and says why

Js task execution process, the understanding of macro task and micro task

console.log("start");

setTimeout(() = > {
  console.log("setTimeout1");
}, 0);

(async function foo() {
  console.log("async 1");

  await asyncFunction();

  console.log("async2");

})().then(console.log("foo.then"));

async function asyncFunction() {
  console.log("asyncFunction");

  setTimeout(() = > {
    console.log("setTimeout2");
  }, 0);

  new Promise((res) = > {
    console.log("promise1");

    res("promise2");
  }).then(console.log);
}

console.log("end");
Copy the code

Tip:

  1. The script tag counts as a macro task and is executed initially
  2. Async await code after await will be placed in the microtask queue

Start execution:

  • Log (“start”); Execute it and print it outstart
  • If you go down, if you hit setTimeout1, put itMacro task queue
  • When it hits the execute function foo, print outasync 1
  • Await blocked queue, firstThe function that executes await
  • Execute asyncFunction to print outasyncFunction
  • On the second setTimeout2,Put it into the macro task queue
  • The new Promise is executed immediately and printed outpromise1
  • Call res(“promise2”), promise.then.Put it on the microtask queue
  • The asyncFunction function is finished, and the following printout async2 will be placedMicrotask queue
  • Then print out the then methods that execute the function immediatelyfoo.then
  • And finally printend
  • The queue that starts the microtask prints out the firstpromise2
  • And then print the second oneasync2
  • When the micro task is finished, execute the macro task to print the first onesetTimeout1
  • Perform the second macro task printsetTimeout2,
  • At this point, the function completes

Painting is not good, can understand the meaning of the line 😭. See if your ideas and answers are consistent with the process

10. How to block global Promise Reject without assigning the Reject handler

I didn’t write this one, I was thinking trycatch but it’s not global.

Later, I checked the data and found that it was a window method

// Use a Try catch to intercept only those inside a Try block
try {
  new Promise((resolve, reject) = > {
    reject("WTF 123");
  });
} catch (e) {
  console.log("e", e);
  throw e;
}

// Use unhandledrejection to intercept global errors (this is correct)
window.addEventListener("unhandledrejection".(event) = > {
  event && event.preventDefault();
  console.log("event", event);
});
Copy the code

11. Sleep by hand

There’s only one way I can do this, and that’s what I mentioned in the JS execution flow above. Await means to block asynchronously

There’s another way to do this that I found on the Internet, which is to completely block the process

    // Implement sleep with async methods of promise and await{(async() = > {console.log('start');
        await sleep(3000)
        console.log('end');

        function sleep(timer) {
          return new Promise(res= > {
            setTimeout(() = >{ res() }, timer); })}}) (); }// Method two is to completely block the process to achieve sleep{(async() = > {console.log('start');
        await sleep(3000)
        console.log('end');

        function sleep(delay) {
          let t = Date.now();
          while (Date.now() - t <= delay) {
            continue; }}; }}) ()Copy the code

Add (1)(2) =3

With that, you can do it in a closure

I’m putting a little bit of a strain on this, how do you keep calling

    // The answer to the question
   const add = (num1) = > (num2) = > num2 + num1;
   
   
   Add (1)(2)(3)(4)(5)....
   function add(x) {
      / / store and
      let sum = x;
       
      // Function calls are added, and the function itself is returned each time
      let tmp = function (y) {
        sum = sum + y;
        return tmp;
      };
      
      // The object's toString must be a method in which the sum is returned
      tmp.toString = () = > sum
      return tmp;
   }
   
   alert(add(1) (2) (3) (4) (5))
Copy the code

The key to the implementation of an infinite chain call is the object’s toString method: each object has a toString() method, which is called automatically when the object is represented as a text value or when an object is referenced as an expected string.

That is, after I call them many times, their results will be stored in the sum variable in add, and when I alert add will automatically call toString and print out sum, which is the final result

13. Completely separate data in two arrays

Is to find data that appears only once in both arrays

var a = [1.2.4], b = [1.3.8.4]
const newArr = a.concat(b).filter((item, _, arr) = > {
  return arr.indexOf(item) === arr.lastIndexOf(item)
})
Copy the code

The final result is [2,3,8]. The principle is very simple: merge two arrays, and then check whether the first occurrence index of the array is the same as the last occurrence index to determine whether it is independent.

14. Determine perfect squares

It’s a question of whether a number can be squared, like if the square root of 9 is 3. 5 is wrong if you can’t square it.

var fn = function (num) {
  return num ** 0.5 % 1= =0
};
Copy the code

And the idea is, you take the square root of it and you check if it’s a positive integer

15. Function execution says the result and says why

function Foo() {
  getName = function () {
    console.log(1);
  };
  return this;
}

Foo.getName = function () {
  console.log(2);
}

Foo.prototype.getName = function () {
  console.log(3);
}

var getName = function () { 
  console.log(4);
}

function getName() {
  console.log(5)
}

Foo.getName();

getName();

Foo().getName()

getName();

new Foo.getName(); 

new Foo().getName()

new new Foo().getName()
Copy the code

This problem actually depends on your understanding of the scope of the relationship

Execution Result:

  • GetName () executes a static method on the Foo function object. Print out 2

  • Execute getName(), which is the function that executes the getName variable. Print 4

    • Why is the variable getName executed instead of the function getName? Thanks toJs precompilation
    • Js is precompiled before executionFunction increaseVariable ascension
    • So both functions and variables get promoted, butFunction declarations have the highest precedence, will be promoted toThe top of the current scope
    • GetName will be reassigned at the end of the execution, and the result will be4The function assigned to the variable
  • Call Foo().getName() and call getName on the value returned by Foo. The Foo function is executed, which reassigns the outer getName function and returns this. This. GetName is executed. So it prints out a 1

  • Execute getName(), because of the previous step, the function is reassigned. So this is going to be the same thing as last time, which is 1 again

  • Call new foo.getName (), which is actually new to the static method getName on Foo so it’s 2. And of course if you print this inside of this function, you’ll see that it’s referring to a new object that’s coming out of new

    • You can think of foo.getName () as a whole becauseHere. Has a higher priority than new
  • Call new Foo().getName(), where new Foo() returns an object and then calls getName on that object’s prototype, so the result is 3

  • Call new new Foo().getName(), this is the same as the last call, the last call did not return a value, so it does not make sense to do new. It’s also going to be 3

16. The prototype calls the interview question to state the result and say why

function Foo() {
  Foo.a = function () {
    console.log(1);
  };
  this.a = function () {
    console.log(2);
  };
}

Foo.prototype.a = function () {
  console.log(4);
};

Function.prototype.a = function () {
  console.log(3);
};


Foo.a();

let obj = new Foo();
obj.a();
Foo.a();
Copy the code

Execution Result:

  • Executing Foo (a).Foo itself does not currently have a value, will pass__proto__I’m looking it up, but, so the output is3

  • New instantiates Foo to generate object obj, and then calls obj.a(), but attaches an A function to the obj object inside Foo. So it’s going to be 2. If there is no internal assignment of a to this object, it will go to the prototype chain and look for the a function, and it will print 4.

  • Execute foo.a (), which Foo did in the previous step, internally assigning function A to Foo itself, so print 1 this time

17. Change array grouping to subtraction

The meaning of this topic is [5, [/ 4, 3, 2, 1]] into (5 – ((4-3-2-1)) and executed. And you can’t use eval()

Method 1: Since eval is not possible, we can use new Function 🤭

Method two: of course method one is a bit against the meaning of the question, so there is a second method

var newArr = [5The [[4.3].2.1]]

    / / 1
    // Convert to a string
    let newStringArr = `The ${JSON.stringify(newArr)}`
    // Loop through the parentheses and minus signs
    let fn = newStringArr.split("").map((el) = > {
      switch (el) {
        case "[":
          return '('
        case "]":
          return ') '
        case ",":
          return The '-'
        default:
          return el
      }
    }).join("")
    // Finally call new Function is ok!
    new Function("return " + fn)()
    
    
    // 2
    function run(arr) {
      return arr.reduce((pre, cur) = > {
        let first = Array.isArray(pre) ? run(pre) : pre
        let last = Array.isArray(cur) ? run(cur) : cur
        return first - last
      })
    }
    run(nweArr)
Copy the code
  • The principle of method one is very simple, turn it into a string and loop through the concatenation of parentheses and minus signs. Finally, a new Function call will do

  • Method 2 means a recursive call through reduce. If the left-hand side is not an array you can subtract the right-hand side, but if the right-hand side is an array you subtract the right-hand side first. Which is the priority of the subtraction parenthesis operation.

18. Handwriting array Flat

    const flat = function (arr, deep = 1) {
      // Declare a new array
      let result = []
      
      arr.forEach(item= > {
        if (Array.isArray(item) && deep > 0) {
          // Hierarchy decrement
          dep--
          // Use concat to link arrays
          result = result.concat(flat(item, deep))
        } else {
          result.push(item)
        }
      })
      return result
    }
Copy the code
  • The idea is to generate a new array internally and iterate over the original array

  • Recurse when there is an array in the original array and deep is greater than or equal to 1. If this condition is not met, the data can be pushed directly into the new array

  • Recursion also involves reducing levels and concatenating the recursive array

  • I’m just going to return this array

19. Convert the array to tree

The topmost parent is -1, and the remaining parents are the ids of nodes in the upper layer

    let arr = [
      { id: 0.name: '1'.parent: -1.childNode: []}, {id: 1.name: '1'.parent: 0.childNode: []}, {id: 99.name: 1-1 ' '.parent: 1.childNode: []}, {id: 111.name: '1-1-1'.parent: 99.childNode: []}, {id: 66.name: '1-1-2'.parent: 99.childNode: []}, {id: 1121.name: '1-1-2'.parent: 112.childNode: []}, {id: 12.name: '2'.parent: 1.childNode: []}, {id: 2.name: '2'.parent: 0.childNode: []}, {id: 21.name: '2-1'.parent: 2.childNode: []}, {id: 22.name: '2-2'.parent: 2.childNode: []}, {id: 221.name: '2-2-1'.parent: 22.childNode: []}, {id: 3.name: '3'.parent: 0.childNode: []}, {id: 31.name: '3-1'.parent: 3.childNode: []}, {id: 32.name: '3-2'.parent: 3.childNode: []}]function arrToTree(arr, parentId) {
       // Determine if it is a top-level node, and return if it is. If not, determine if it is the child node you are looking for
      const filterArr = arr.filter(item= > {
        return parentId === undefined ? item.parent === -1 : item.parent === parentId
      })
       
      // make a recursive call to add the childNode to the childNode's childNode
      filterArr.map(item= > {
        item.childNode = arrToTree(arr, item.id)
        return item
      })
       
      return filterArr
    }
    
    arrToTree(arr)
Copy the code
  • This problem is also done recursively, and it starts with a judgment about whether it’s a top-level node or not

  • If it is, it returns it directly. If it is not, it determines whether it is the child node to be added to the parent node

  • And then add nodes layer by layer

  • Finally, this object is returned

20. Merge arrays and sort to deduplicate

I have two arrays, and I merge them. And then de-weighting, the logic of de-weighting is that I leave the side that has more repetition.

For example, the following array has two 5’s on one side and three 5’s on the other. I need to keep three 5’s and drop two 5’s. Over and over again, the results are being sorted.

  • Array 1: [1, 100, 0, 5, 1, 5]

  • Array 2: [2, 5, 5, 5, 1, 3]

  • Final result: [0, 1, 1, 2, 3, 5, 5, 5, 100]

  // Determine the maximum number of occurrences
    function maxNum(item, arr) {
      let num = 0;
      arr.forEach(val= > {
        item === val && num++
      })

      return num
    }

    function fn(arr1, arr2) {
      // Use the Map data type to record times
      let obj = new Map(a);// Merge the arrays and find the most times, and store them as key-value pairs into the Map data type
      [...arr1, ...arr2].forEach(item= > {
        let hasNum = obj.get(item)
        let num = 1
        if (hasNum) {
          num = hasNum + 1
        }
        obj.set(item, num)
      })

      // Store the merged array
      let arr = []
      // Iterate over the Map data types and push the most times directly into the new array
      for (const key of obj.keys()) {
        if (obj.get(key) > 1) {
          for (let index = 0; index < Math.max(maxNum(key, arr1), maxNum(key, arr2)); index++) {
            arr.push(key)
          }
        } else {
          arr.push(key)
        }
      }

    // Finally sort
      return arr.sort((a, b) = > a - b)
    }
Copy the code
  • So the idea is, I’m going to merge two arrays

  • The Map data type is stored as key-value pairs, where the key is the data and the value is the number of occurrences of the data

  • Generates a new array to hold the merged array

  • Iterate over the Map data type, and if the number of occurrences is greater than one, find which of the two arrays has the most occurrences, and push the one that has the most occurrences into the new array. If the number of occurrences is equal to one, you just push it into the new array.

  • Finally, sort the array and return the new array.

Three, endnotes

Finally, I hope you can understand these questions and know why. Some questions are not just for you. It’s a way for you to understand what it means.

The above questions, my answer is not the optimal answer. If you have a better solution, don’t be afraid to hit the comments! I’ll add your solution to the article.

If the content of the article is helpful to you, remember the three link ~ 🎉🎉🎉 if there is an error in the article, also welcome to correct the correction!