I want to stronger

Remember, just work that time, have not experienced the cruelty of society. While playing while slipping, so muddled muddled more than a year. All the students around them are entering dachang. I was still a quiet little program. Poverty forced me to change, to learn, to absorb. Although it is still a small program, but indeed, he has obviously felt the change.

In this process, I also participated in many interviews, including many big Internet companies. A lot of it is handwritten code, handwritten algorithms and so on. This kind of js understanding and application ability is essential, so here is the collection of front-end code handwriting problems encountered or not encountered, can give other students and their own further impression, more experience is good.

👌 began

The form will be title + code + analysis, to describe the problem and understand the process and code ideas as easily as possible. Of course, some problems will involve a lot of JS principles, need to cooperate with the reading of MDN Web documents or some great blog, more can understand thoroughly.

Hu Yu’s blog

Getting started with ECMAScript 6

The code is not reasonable, there are supplements, I hope to point out, but also their own learning and absorption process.

Anti-shake & throttling

The principle is to use the characteristics of closure functions to save variables. And then we have to distinguish between anti-shake and throttling.

  • If a task is triggered frequently, the task is executed only after the task triggering interval exceeds the specified time. Otherwise, the task is initialized again. The common scenario is the search box. The search content changes frequently and the search function is triggered only after the content reaches the interval. (In plain English, “crazy trigger task” is basically the last task that can be performed properly. It can be understood as only one.)

  • Throttling: A task executes only once in a specified time interval. The common scenario is to scroll listening. (Plain English: ** crazy scrolling, set the interval of 200ms, the scroll event is executed every 200ms, will be executed many times, the ** frequency is controlled)

👇

Function debounce (fn, time) {let timer = null return function (... If (timer) {clearTimeout(timer)} timer = setTimeout(() => {fn. Call (this,... }} // throttle function throttle (fn, time) {let isRun = false return function (... Rest) {if (isRun) {return} isRun = true setTimeout(() => {fn. Call (this,... Rest) isRun = false}, time)}}Copy the code

Deep copy

Another classic problem appears.

JS basic data type and reference data type difference and shallow copy

JS has two types of data:

  • Basic data types: **Number, String, Boolean, Null, Undefined, Symbol (ES6). ** These types directly manipulate the actual values stored in variables.
  • Reference data types: Object (In JS everything except basic data types are objects, arrays are objects, functions are objects, and regular expressions are objects)

The vernacular understanding is that we need to make a copy of a variable, whatever it is inside, and when we change the copy, we don’t want to affect the original variable. We need to cut the link between the replica and the original variable.

// WeakMap is used to store copied variables. // WeakMap is used to store copied variables. Function deepClone (obj, cache = new WeakMap()) {// If (typeof obj! = = 'object' | | obj = = = null) return obj / / ever copied the if (cache) from the (obj)) return the cache, the get (obj) let the result = Array.isArray(obj) ? [] : Cache.set (obj, result) for (let key in obj) { If (obj. HasOwnProperty (key)) {result[key] = deepClone(obj[key], cache)}} return result}Copy the code

An array of random sequence

Array questions are also highly likely to come up in an interview.

Js implementation of shuffle algorithm

Function sort(arr) {return arr.sort(() => math.random () -0.5)} // Famous Fisher -- Yates shuffle Function shuffle(arr){let m = arr.length while(m > 0){let index = parseInt(math.random () * m--) function shuffle(arr){let m = arr.length while(m > 0){let index = parseInt(math.random () * m--) [arr[index],arr[m]] = [arr[m],arr[index]] } return arr }Copy the code

Array to heavy

The simplest array problem, do not say, on the code.

Function setArr (arr) {return array. from(new Set(arr)) // or [...new Set(arr)] If you don't want to use filter, you can use a normal loop, Function removeDup (arr) {const hashMap = new Map() return arr.filter((item) => {if (hashmap.has (item)) {return false } hashMap.set(item) return true }) }Copy the code

An array of flat

The array Flat method is a new feature in ES6 to flatten groups of multidimensional numbers into low-dimensional arrays. If the parameter is not passed, it flattens one layer by default. Passing the parameter can specify the flattening level.

function myFlat (arr, deep = 2) { let result = [] for (let i = 0; i < arr.length; I++) {// if the target is still an array and the expansion number is greater than 0, continue to expand, If (array.isarray (arr[I]) && deep > 0) {result = [...result,...myFlat(arr[I], deep - 1)] // result = result.concat(myFlat(array[i],deep -1)) } else { result.push(arr[i]) } } return result }Copy the code

An array of the filter

The filter method is often used and is simple to implement. Filter takes the current element, then the array index, then the entire array, and returns the element whose value is true.

Array.prototype.filter = function (fn, context) { if (typeof fn ! = 'function') { throw new TypeError(`${fn} is not a function`) } const arr = this const reuslt = [] for (let i = 0; i < arr.length; i++) { const temp = fn.call(context, arr[i], i, arr) if (temp) result.push(arr[i]) } return result }Copy the code

call&apply

“Call” and “apply” are cliches. Must test, delimit the key point.

Function.prototype.call() MDN

The call() method calls a function with a specified this value and one or more arguments given separately. Note: The syntax and actions of this method are similar to those of apply(), except that call() accepts a list of arguments, while apply() accepts an array of arguments.

Function.prototype.myCall = function (ctx, ... Rest) {/ / get the execution context const CTX = CTX | | the window / / variables are defined with the symbol, CTX [fn] = this const result = rest && rest.length > 0? ctx[fn](... rest) : CTX [fn]() Delete the mount Function delete CTX (fn) return result} Function. The prototype. MyApply = Function (CTX, rest = null) { const ctx = ctx || window const fn = Symbol('fn') ctx[fn] = this const result = rest ? ctx[fn](... rest) : ctx[fn]() delete ctx[fn] return result }Copy the code

bind

Focus, focus, focus. Compulsory, covering prototype, prototype chain.

The **bind()** method creates a new function. When bind() is called, this of the new function is specified as the first argument to bind(), and the remaining arguments will be used as arguments to the new function.

Binding functions can also be constructed using the new operator, which behaves as if the target function has already been constructed. The supplied this value is ignored, but the leading argument is still supplied to the simulation function.

Vernacular understanding

  • Bind simply takes a list of arguments and returns a new function.
  • When the new function isnew Fn()Passed in when the constructor executesThe first argument, this, is invalid.

There are two points of knowledge

  • The new function prototype needs to be connected to the original function prototype on the prototype chain. (This allows us to determine if the new function is instantiated as a constructor.)
  • **instanceof, ** used to detect a constructorprototypeProperty appears on the stereotype chain of an instance object.

Function.prototype.bind() MDN

JavaScript in-depth simulation of bind implementation

Function.prototype.myBind = function (ctx, ... Arr) {/ / to bind this const CTX = CTX | | the window / / the function itself const love = this / / intermediate function, Const middle = function () {} // New const newF = function (... Const _this = this instanceof _self; // Check whether the new function's this can be found in the prototype chain. _self : ctx const arg = [...arr, ...rest] arg.length > 0 ? _self.call(_this, ... arg) : Prototype = _self.prototype newf.prototype = new middle() // // newf.prototype = new _self(); // newf.prototype = _self. Prototype return newF}Copy the code

It doesn’t matter if you don’t understand it, we combine the 👇👇 graph with the subsequent instanceof, new operator code, and just understand it again and again.

  • thinking: Why do you inheritnewF.prototype = new _self()* * ornewF.prototype = _self.prototype

Through the inheritance of learning, you can solve your confusion.

The instanceof operator

The instanceof operator is used to check whether the constructor’s prototype property appears on the prototype chain of an instance object.

The vernacular

The operator has the instance object to the left and the constructor to the right. I’m going to follow the prototype chain of the instance object and see if I can find the prototype of the constructor.

// Left object, Function myInstanceof(left,right){const protoT = right. Prototype let proto = left.__proto__ while(proto! ==null){if(proto === protoT){return true}else{// Proto = proto.__proto__}} return false}Copy the code

New operator

The new operator, the instanceof operator, and bind must be understood. Key!!

The new operator MDN

JavaScript deep new simulation implementation

The new operator creates an instance of a user-defined object type or of a built-in object with a constructor.

New constructor[([arguments])]new keyword does the following:

  • Create an empty simple JavaScript object (that is, **{}* *);
  • Linking this object (setting its constructor) to another object;
  • Use the newly created object in Step 1 as **this** context;
  • If the function does not return an object, it returns **this**
function myNew (fn, ... Const obj = new Object(); // Const obj = new Object(); Prototype let result = fn.call(obj,... Rest) // does the function return object result = typeof result === 'object'? result : obj }Copy the code

Obejct.create

The ** object.create ()** method creates a new Object, using an existing Object to provide the __proto__ of the newly created Object.

Create object is implemented by creating a Fun constructor inside create, assigning the passed object to the prototype of Fun, and then returning an instance of Fun whose __proto__ points to the passed object.

Obejct.prototype.create = function(obj){
  const func = function(){}
  func.prototype = obj
  return new func()
}
Copy the code

Implementation inheritance

Inheritance is an interview necessity. There are many ways to implement inheritance in ES5, both good and bad.

  • Prototype chain inheritance
  • Constructor inheritance (classic)
  • Combination of inheritance
  • Prototype inheritance (similar to prototype chain)
  • Parasitic inheritance (similar to constructors)
  • Parasitic combinatorial inheritance (similar to combinatorial inheritance, optimized to a relatively optimal solution)

Multiple ways in which JavaScript extends inheritance and the pros and cons (take a look at 🔪)

Watch it over and over again, and try to do it a few times. The previous thinking in the bind method is explained here by inheritance.

Function Parent (name){this.name = name} parent.prototype.getName = function (){return this.name} function Child (name,age){ Parent.call(this,name) this.age = age } const Func = function(){} Func.prototype = Child. The Parent. The prototype prototype = new Func () the Child. The prototype. The constructor = Child Child. / / if the prototype = new Parent () Prototype = parent. prototype = parent. prototype; // ES6 class Parent{constructor(name){this.name = name getName(){console.log(this.name)}} class Child extends Parent{ constructor(name,age){ super(name) this.age = age } }Copy the code

Publish and subscribe model

Publish-subscribe patterns are also common, from our JS event listening to the two-way data binding of vUE. Are inseparable from this design pattern.

To address functional coupling between the subject object and the observer. For example, in a JS event, the callback function we define for an operation is a subscription event. After the click operation, the subscriber is notified to execute the corresponding function.

Class EventEmitter{constructor(){this.events = {}} on(name,fn){ This.events [name].push(fn)}else{this.events[name] = [fn]}} off(name,fn){// Unsubscription if(this.events[name]){ this.events[name] = this.events[name].filter((item)=>fn! ==item) }else{ new Error('') } } emit(name,... If (this.events[name]){this.events[name]. ForEach ((item)=>{item.call(this,... Rest)})}} once(name,fn){// Register a one-time subscription const onceF = (... rest)=>{ fn.call(this,... rest) this.off(name,onceF) } this.on(name,onceF) } }Copy the code

LazyMan task queue

** This question is also found in other articles, moved to share. ** examines how to design code in a specific scenario.

Knowledge: Observer mode, simple event queue mechanism.

Topic:

// Implement a LazyMan, which can be called as follows: LazyMan("Hank") output: // Hi! This is Hank! LazyMan (" Hank "). Sleep (10) eat (" dinner ") Hi! This is Hank! // Wait 10 seconds.. // Wake up after 10 // Eat supper ~ LazyMan("Hank").eat(" supper").eat("supper").eat("supper") Eat supper~ // Eat supper~ LazyMan("Hank").sleepFirst(5).eat("supper") output // Wait 5 seconds // Wake up after 5 // Hi This is Hank! // Eat supper // etc.Copy the code
  • Start by defining a Lazy class that accepts a name argument and can perform a series of actions
  • We can subscribe to events that need to be executed from this class through the observer pattern. This class needs to be aggregated.
  • We have sleepFirst, so we need to use setTimeout for macro tasks in event queues.

👌, the requirements are clear, the code is as follows, while looking at the analysis

Class Lazy {constructor(name) {this.name = name // taskList this.tasklist = [] // push the output name into the taskList This.tasklist.push (this.sayname) this.do()} do() {// Each task is executed by setTimeout, SetTimeout (() => {let fn = this.taskList.shift() fn && fn.call(this)}, 0) } sayName () { console.log(this) console.log(`Hi! This is ${this.name}! Push (() => {setTimeout(() => {console.log(' Wake up after ') ${time}`) this.do() }, Time)}) return this} sleepFirst (time) {this.taskList.unshift(() => {setTimeout() => { console.log(`Wake up after ${time}`) this.do() }, time) }) return this } eat (thing) { this.taskList.push(() => { console.log(`Eat ${thing}`) this.do() }) return this } }  function lazyMan (name) { return new Lazy(name) }Copy the code

The function is currified

Function corrification is the technique of converting a function that takes multiple arguments into a function that takes a single argument (the first argument of the original function) and returns a new function that takes the remaining arguments as a result. It is a use of higher-order functions. For example, the summation function add(1,2,3), which currie transforms into Add (1), (2), (3)

JavaScript thematic function Curryification details JS function Curryification

The main purpose, as I roughly understand it on my side, is to be able to take duplicate arguments, process complex logic in advance, and simplify code. (The bind function we wrote at 👆 also uses this logic to accept multiple arguments or accept this first and then pass the argument)

Function curry (fn,... Arg) {const len = fn. Length / / fn number parameter arg = arg | | [] return function (... Rest) {const _arg = [...arg,...rest] if (_arg. Length < len) {return curry.call(this, fn,... _arg) } else { return fn.call(this, ... _arg) } } }Copy the code

compose

Compose performs a series of tasks (functions). The loader execution stream for WebPack uses Compose to process the file code layer by layer. And nodeJS ‘KOA2 framework, the well-known middleware principle that Compose plays a major role in.

Check out this article for an overview of the Koa2 middleware implementation, which provides a good example of compose’s application.

Next, we write 👇 through a simple case

const tasks = [step1, step2, step3, step4]
Copy the code

Each step is a compose and is executed step by step to the end

Compose is an important tool function in functional programming, and the implementation of compose here has three points

  • The first function is multivariate (takes multiple arguments), and the following functions are unitary (takes one argument)
  • Executed from right to left
  • All functions are executed synchronously (more on asynchrony later)

As an example, let’s say we have the following functions

const init = (... args) => args.reduce((total, val) => total + val, 0) const step2 = (val) => val + 2 const step3 = (val) => val + 3 const step4 = (val) => val + 4Copy the code

These functions form a task queue

const steps = [step4, step3, step2, init]
Copy the code

Compose the queue using compose and execute

let composeFunc = compose(... steps) console.log(composeFunc(1, 2, 3))Copy the code

Synchronous:

function compose (... Steps) {steps = [...steps] const init = steps.pop() return function (... Reverse ().reduce((total, step) => step(total), init(... args)) } }Copy the code

Asynchronous:

function compose (... steps) { const init = steps.pop() return function myf (... Args) {return steps.reverse().reduce((res, step) => {// We use promise to wrap each step and make it chain to execute return res.then((... result) => Promise.resolve(step(... result))) }, Promise.resolve(init(... Args))))}} // We will return a promise object, which we will output in the then method let composeFunc = compose(... steps) composeFunc(1, 2, 3).then((val) => { console.log(val) })Copy the code

JSONP

Jsonp is also a constant presence in the front end. It is mainly to solve the problem of obtaining data across domains.

The principle is to achieve cross-domain implementation by dynamically inserting script tags, because script scripts are not restricted by the same origin policy. It consists of two parts: a callback function and data.

We will write a piece of code with the following logic:

  • What our JSONP needs to return is a Promise object, which is more like a normal Ajax request.
  • Process the request address and parameters, concatenate them to the same string, and then define a method as a callback to concatenate the method name to form the request address.
  • Create a script tag with the SRC attribute as the address for the requested data.
  • When the script tag is loaded, the js content returned contains an execution function that executes the name of the callback method we passed, and the parameters are the data we need.
  • It is ultimately executed in a callbackpromise.resolve, and then destroy the callback function we defined.

Go directly to 👇

function jsonp (url, data) { if (! url || ! Data) {return promise.reject (' parameter missing ')} // Process request address and parameter const query = dataToUrl(data) const url = '${url}? ${query}` return new Promise((resolve, Reject) => {const scriptE = document.createElement('script') // const cbFn = 'jsonp${new Date()}'  scriptE.src = `${url}callback=${cbFn}` const head = document.getElementsByTagName('head')[0] head.appendChild(scriptE) Window [cbFn] = function (res) {res? resolve(res) : reject('') head.removeChild(scriptE) window[cbFn] = null } }) } function dataToUrl (data) { let result = '' for (let key  in data) { if (data.hasOwnProperty(key)) { result += `${key}=${data[key]}&` } } return result }Copy the code

Promise article

Promise is also a point I like to examine during the interview process, and I wrote a more detailed handwritten process in another article.

A list of promise points will be summarized and the principles will be analyzed.

👇 👇

Easy to understand the Promise principle and realize it (1)

Easy to understand the Promise principle and realize it (2)

At the end

These are some of the questions I’ve encountered in interviews, or discussed frequently in other articles, and I’ve briefly summarized them here. I also want to consolidate my own memories and share them so that people can understand more about these issues. Progress together, I hope our efforts are fruitful.

If there are more interesting handwritten code problems, I will add. Other students can put forward supplementary ideas, also hope to harvest some opinions, there is a place to discuss together.