introduce

In the front-end interview, tearing code by hand is obviously inevitable, and accounts for a large proportion of the proportion.

Generally speaking, if you write good code, even if the theoretical knowledge is not clear, you can have a good chance to pass the interview. In fact, a lot of handwriting is often behind the examination of your understanding of the relevant theory.

There are several types of programming problems:

Implementation of a component with some functionality * Other (advanced) : Implementation of the subscription publisher pattern; Object oriented programming, process oriented programming, functional programming to put the elephant in the refrigerator and so onCopy the code

The first two types account for the largest proportion. Get into the habit of brushing leetcode once a day, focusing on data structures (stacks, lists, queues, trees), dynamic programming, DFS, and BFS

This article mainly covers the second type of various emphasis handwriting.

It is recommended to master:

  • Instanceof (Examining the understanding of prototype chains)
  • New (Understanding the process of creating an object instance)
  • Call&apply &bind
  • Handwritten Promises (understanding asynchrony)
  • Handwritten native Ajax (understanding of Ajax principles and HTTP requests, with emphasis on implementation of GET and POST requests)
  • Event Subscription publishing
  • Other: array, string API implementation, relatively low difficulty. As long as you know how to use arrays and strings in general, you can write a general idea on the spot. (PS: I think the array reduce method is difficult, so I can read it separately. Even if you are not asked to implement reduce in the interview, it is also very helpful to use it in other questions.)

No more words, just get started

1. The handwritten instanceof

Instanceof function:

Determines whether an instance is an instance of its parent or ancestor type.

Instanceof iterates through the prototype chain of the left variable until it finds the prototype of the right variable and returns false

 let myInstanceof = (target,origin) = > {
     while(target) {
         if(target.__proto__===origin.prototype) {
            return true
         }
         target = target.__proto__
     }
     return false
 }
 let a = [1.2.3]
 console.log(myInstanceof(a,Array));  // true
 console.log(myInstanceof(a,Object));  // true
Copy the code

2. Implement the map method of array

The map() method of the array returns a new array with each element in the new array corresponding to the value returned by a call to the provided function.

Usage:

const a = [1.2.3.4];
const b = array1.map(x= > x * 2);
console.log(b);   // Array [2, 4, 6, 8]
Copy the code

Before implementing this, let’s look at the parameters of the map method

The map method has two parameters, one is the method fn which operates on the array element, and the other is the method this refers to (optional)

Native implementation:

    / / implementation
     Array.prototype.myMap = function(fn, thisValue) {
            let res = []
            thisValue = thisValue||[]
            let arr = this
            for(let i=0; i<arr.length; i++) {
                res.push(fn.call(thisValue, arr[i],i,arr))   This refers to the current item, the current index, and the current array
            }
            return res
        }
        / / use
        const a = [1.2.3];
        const b = a.myMap((a,index) = > {
                return a+1; })console.log(b)   // Output [2, 3, 4]
Copy the code

3. Reduce implements the map method of arrays

Map method is implemented by using the built-in Reduce method of array, and the principle of Reduce is investigated

Array.prototype.myMap = function(fn,thisValue){
     var res = [];
     thisValue = thisValue||[];
     this.reduce(function(pre,cur,index,arr){
         return res.push(fn.call(thisValue,cur,index,arr));
     },[]);
     return res;
}
​
var arr = [2.3.1.5];
arr.myMap(function(item,index,arr){
 console.log(item,index,arr);
})
Copy the code

4. Reduce method of handwritten array

The reduce() method takes a function as an accumulator, and each value in the array (from left to right) begins to shrink to a single value, another array-by-array method added in ES5

Parameters:

  • Callback (a function called on each item in the array, which takes four functions 🙂

    • PreviousValue (the value returned when the callback function was last called, or the initial value)

    • CurrentValue (array element currently being processed)

    • CurrentIndex (index of the array element currently being processed)

    • Array (array that calls the reduce() method)

  • InitialValue (optional initialValue. The value passed to previousValue as the first call to the callback function)

 function reduce(arr, cb, initialValue){
     var num = initValue == undefined? num = arr[0]: initValue;
     var i = initValue == undefined? 1: 0
     for (i; i< arr.length; i++){
        num = cb(num,arr[i],i)
     }
     return num
 }
 
 function fn(result, currentValue, index){
     return result + currentValue
 }
 
 var arr = [2.3.4.5]
 var b = reduce(arr, fn,10) 
 var c = reduce(arr, fn)
 console.log(b)   / / 24
Copy the code

5. Array flattening

Array flattening converts a multidimensional array into a one-dimensional array

1. Es6 provides a new method flat(depth)

let a = [1[2.3]]; 
a.flat(); / / [1, 2, 3]
a.flat(1); / / [1, 2, 3]
Copy the code

There is an even easier way to make the target array a 1-dimensional array without knowing the dimensions. Depth is set to Infinity.

let a = [1[2.3[4[5]]]]; 
a.flat(Infinity); [1,2,3,4,5] a is a 4-dimensional array
Copy the code

2. Use cancat

function flatten(arr) {
     var res = [];
     for (let i = 0, length = arr.length; i < length; i++) {
     if (Array.isArray(arr[i])) {
     res = res.concat(flatten(arr[i])); //concat does not alter the original array
     //res.push(... flatten(arr[i])); // Or use the extension operator
     } else{ res.push(arr[i]); }}return res;
 }
 let arr1 = [1.2[3.1], [2.3.4[2.3.4]]]
flatten(arr1); //[1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
Copy the code

6. Coriolization of functions

Juejin. Im /post/684490…

The definition of currization is to receive some parameters, return a function to receive the rest of the parameters, after receiving enough parameters, execute the original function.

When the Coriolization function receives enough parameters, it will execute the original function. How do you determine when enough parameters are reached?

There are two ideas:

  1. The length property of the function is used to obtain the number of parameters of the function. The number of parameters is the required number of parameters

  2. Manually specify the number of arguments required when calling the Cremation utility function

Put these two things together and implement a simple Curry function:


/** * currify *@param The antiderivative of f sub n to be currized *@param Len Specifies the number of parameters required. The default is the number of parameters of the original function */
function curry(fn,len = fn.length) {
 return _curry.call(this,fn,len)
}
​
/** ** transfer function *@param The antiderivative of f sub n to be currized *@param Len Number of parameters required *@param Args List of received arguments */
function _curry(fn,len,... args) {
    return function (. params) {
         let _args = [...args,...params];
         if(_args.length >= len){
             return fn.apply(this,_args);
         }else{
          return _curry.call(this,fn,len,... _args) } } }Copy the code

Let’s verify:

let _fn = curry(function(a,b,c,d,e){
 console.log(a,b,c,d,e)
});
​
_fn(1.2.3.4.5);     / / print: 1, 2, 3, 4, 5
_fn(1) (2) (3.4.5);   / / print: 1, 2, 3, 4, 5
_fn(1.2) (3.4) (5);   / / print: 1, 2, 3, 4, 5
_fn(1) (2) (3) (4) (5); / / print: 1, 2, 3, 4, 5
Copy the code

Our popular library, LoDash, also provides the Curry method, and has added a very interesting placeholder feature that changes the order of incoming arguments.

For example, if we pass a placeholder, the argument passed by this call ignores the placeholder, and the position of the placeholder is filled by the argument of the next call, like this:

Take a look at the example from the official website:

Now let’s think about how to implement the function of placeholders.

For the Curry function of Lodash, the Curry function is mounted on the LoDash object, so the LoDash object is used as the default placeholder.

Our self-implemented Curry function is not itself mounted on any objects, so we use it as a default placeholder

The purpose of using placeholders is to change the order in which arguments are passed, so in the Curry implementation, you need to record whether a placeholder is used and the position of the argument represented by the placeholder each time.

Directly on the code:


/ * * *@param  F sub n to be Currified *@param  Length Specifies the number of parameters. The default value is parameter number *@param  Holder placeholder that defaults to the current Currization function *@return {Function}   The Currie function */
function curry(fn,length = fn.length,holder = curry){
 return _curry.call(this,fn,length,holder,[],[])
}
/** ** transfer function *@param The antiderivative of f sub n Coriolization *@param Length Number of arguments required by the original function *@param Holder receives placeholder *@param Args List of received parameters *@param List of placeholder positions received by holders *@return {Function}   The function or final result of continuing the Curryization */
function _curry(fn,length,holder,args,holders){
 return function(. _args){
 // Make a copy of the parameters to avoid arguments confusion caused by multiple operations on the same function
 let params = args.slice();
 // Make a copy of the placeholder position list to add the newly added placeholder
 let _holders = holders.slice();
 // Loop in parameters, append parameters or replace placeholders
 _args.forEach((arg,i) = >{
 // The real parameter is preceded by a placeholder. Replace the placeholder with the real parameter
 if(arg ! == holder && holders.length) {let index = holders.shift();
     _holders.splice(_holders.indexOf(index),1);
     params[index] = arg;
 }
 // There is no placeholder before the real parameter appends the parameter to the parameter list
 else if(arg ! == holder && ! holders.length){ params.push(arg); }// The placeholder is passed in. There was no placeholder to record the position of the placeholder
 else if(arg === holder && ! holders.length){ params.push(arg); _holders.push(params.length -1);
 }
 // The placeholder is passed in, and the previous placeholder is deleted
 else if(arg === holder && holders.length){ holders.shift(); }});// Params does not contain a placeholder in the previous length
 if(params.length >= length && params.slice(0,length).every(i= >i! ==holder)){return fn.apply(this,params);
 }else{
 return _curry.call(this,fn,length,holder,params,_holders)
 }
 }
}
Copy the code

Verify:;

let fn = function(a, b, c, d, e) {
 console.log([a, b, c, d, e]);
}
​
let _ = {}; // Define a placeholder
let _fn = curry(fn,5, _);// Corrify the function, specifying the number of arguments required, and specifying the required placeholders
​
_fn(1.2.3.4.5);                 / / print: 1, 2, 3, 4, 5
_fn(_, 2.3.4.5) (1);              / / print: 1, 2, 3, 4, 5
_fn(1, _, 3.4.5) (2);              / / print: 1, 2, 3, 4, 5
_fn(1, _, 3) (_,4, _) (2) (5);         / / print: 1, 2, 3, 4, 5
_fn(1, _, _, 4) (_,3) (2) (5);        / / print: 1, 2, 3, 4, 5
_fn(_, 2) (_, _,4) (1) (3) (5);        / / print: 1, 2, 3, 4, 5
Copy the code

So far, we have fully implemented a curry function ~~

7. Shallow copy and deep copy implementation

Deep and shallow copies are only for reference data types such as Object and Array.

Shallow copy and deep copy differences:

Shallow copy: Creates a new object with an exact copy of the original object’s property values. If the property is of a primitive type, it copies the value of the primitive type. If the property is of a reference type, it copies the memory address. If one object changes the property of the reference type, it affects the other object.

Deep copy: A complete copy of an object from the heap, creating a new area of the heap for storage. This way, changing copy values does not affect the old objects

Shallow copy implementation:

Method one:

function shallowCopy(target, origin){
    for(let item in origin) target[item] = origin[item];
    return target;
}
Copy the code

Other methods (built-in API) :

  1. Object.assign
var obj={a:1.b: [1.2.3].c:function(){console.log('i am c')}}
var tar={};
Object.assign(tar,obj);
Copy the code

Of course, this method only works with object types; for arrays you can use the slice and concat methods

  1. Array.prototype.slice
var arr=[1.2[3.4]].var newArr=arr.slice(0);
Copy the code
  1. Array.prototype.concat
var arr=[1.2[3.4]].var newArr=arr.concat();
Copy the code

The same tests as above (assign object test, slice concat array test) are better understood with the concept of shallow copy and deep copy

Deep copy implementation:

Method one:

Const a = json.parse (json.stringify (b))

Method 2:

// Implement deep copy recursion
function deepCopy(newObj,oldObj){
     for(var k in oldObj){
         let item=oldObj[k]
         Array, object, simple type?
         if(item instanceof Array){
             newObj[k]=[]
             deepCopy(newObj[k],item)
         }else if(item instanceof Object){
             newObj[k]={}
             deepCopy(newObj[k],item)
         }else{  // Simple data type, direct assignment
             newObj[k]=item
         }
     }
}
Copy the code

8. Hand write call, apply, bind

Write a call

Function.prototype.myCall=function(context=window){  // Function method, so write on the Fuction prototype object
 if(typeof this! = ="function") {// If is not necessary and will automatically throw an error
    throw new Error(It's not a function)}const obj=context||window   // The ES6 method can be used here to add default values for parameters, js strict mode global scope this is undefined
 obj.fn=this      //this is the context of the call,this is the function, and this is the method of obj
 const arg=[...arguments].slice(1)   // The first object is objres=obj.fn(... arg)delete obj.fn   // Not deleting will result in more and more context attributes
 return res
}
Copy the code
F.call (obj,arg1)
function f(a,b){
 console.log(a+b)
 console.log(this.name)
}
let obj={
 name:1
}
f.myCall(obj,1.2) // Otherwise this points to window

obj.greet.call({name: 'Spike'}) // This is Spike

Copy the code

Write apply(arguments[this, [parameter 1, parameter 2…..]])

Function.prototype.myApply=function(context){  // Arrow functions never have an argument object !!!!! I can't write this as an arrow function
 let obj=context||window
 obj.fn=this
 const arg=arguments[1[]] | |// If there are parameters, the result is an array
 letres=obj.fn(... arg)delete obj.fn
 return res
} 
function f(a,b){
 console.log(a,b)
 console.log(this.name)
}
let obj={
 name:'Joe'
}
f.myApply(obj,[1.2])  //arguments[1]
Copy the code

Write a bind

this.value = 2
var foo = {
 value: 1
};
var bar = function(name, age, school){
 console.log(name) // 'An'
 console.log(age) / / 22
 console.log(school) // 'home college'
}
var result = bar.bind(foo, 'An') // select * from 'An';
result(22.'College at home') // This parameter is merged with the preset parameter into the bar
Copy the code

Simple version

Function.prototype.bind = function(context, ... outerArgs) {
 var fn = this;
 return function(. innerArgs) {   // Returns a function... Rest is the argument passed in when the actual call is made
 return fn.apply(context,[...outerArgs, ...innerArgs]);  // Return the function that changed this,
 // Merge parameters}}Copy the code

Reasons for new failure:

Ex. :

// Declare a context
let thovino = {
 name: 'thovino'
}
​
// Declare a constructor
let eat = function (food) {
 this.food = food
 console.log(`The ${this.name} eat The ${this.food}`)
}
eat.prototype.sayFuncName = function () {
 console.log('func name : eat')}/ / the bind
let thovinoEat = eat.bind(thovino)
let instance = new thovinoEat('orange')  Orange is in thovino
console.log('instance:', instance) / / {}

Copy the code

The generated instance is an empty object

When executing the new operator, our thovinoEat function can look like this:

function thovinoEat (. innerArgs) { eat.call(thovino, ... outerArgs, ... innerArgs) }Copy the code

Thovinoeat.call (obj,… Args), where obj is the simple empty object {} created by the new operator itself, but it does not actually replace the context object thovino inside thovinoEat. This is beyond the reach of call, because instead of replacing the this pointer inside the thovinoEat function, you should be replacing the Thovino object.

In other words, we want the new operator to point this in eat to the empty object created by the operator itself. But the third step to the thovino new operator did not succeed!

New inheritable version

Function.prototype.bind = function (context, ... outerArgs) {
 let that = this;
​
function res (. innerArgs) {
     if (this instanceof res) {
         // when the new operator executes
         // This in the third step of the new operator refers to the simple empty object created by the new itself {}
         that.call(this. outerArgs, ... innerArgs) }else {
         / / ordinary bindthat.call(context, ... outerArgs, ... innerArgs) } } res.prototype =this.prototype / /!!!!!!
     return res
}
Copy the code

9. Implement new manually

New process text description:

  1. Create an empty object obj;

  2. Points the implicit proTO of the empty object to the constructor’s prototype.

  3. Use call to redirect this

  4. If no value is returned or a non-object value is returned, obj is returned as the new object; If the return value is a new object, return that object directly.

function Person(name,age){
 this.name=name
 this.age=age
}
Person.prototype.sayHi=function(){
 console.log('Hi! I am '+this.name)
}
let p1=new Person('Joe'.18)
​
//// Manually implement new
function create(){
 let obj={}
 // Get the constructor
 let fn=[].shift.call(arguments)  // Arguments is not an array but an object!! This method removes the first element of the arguments array. Let arg = [].slice.call(arguments,1)
 obj.__proto__=fn.prototype
 let res=fn.apply(obj,arguments)    // Change this to add methods and attributes to the instance
 // Make sure to return an object (in case fn is not a constructor)
 return typeof res==='object'? res:obj }let p2=create(Person,'bill'.19)
p2.sayHi()
Copy the code

Details:

[].shift.call(arguments) Can also be written as:let arg=[...arguments]
 let fn=arg.shift()  // Enables arguments to call array methods with the constructor as the first argument
 obj.__proto__=fn.prototype
 // Change this to add methods and attributes to the instance
 let res=fn.apply(obj,arg)
Copy the code

10. Write promises by hand (often test promises.all, promise.race)

// Promise/A+ Three states specified by the specification
const STATUS = {
 PENDING: 'pending'.FULFILLED: 'fulfilled'.REJECTED: 'rejected'
}
​
class MyPromise {
 // The constructor receives an execution callback
 constructor(executor) {
     this._status = STATUS.PENDING // Promise initial state
     this._value = undefined // then the value of the callback
     this._resolveQueue = [] // The success queue triggered when resolve
     this._rejectQueue = [] // reject indicates the reject queue// Use the arrow function to fix this (resolve is triggered in executor, otherwise this cannot be found)
 const resolve = value= > {
     const run = () = > {
         // Promise/A+ The Promise state specified by the specification can only change from pending to depressing
         if (this._status === STATUS.PENDING) {
             this._status = STATUS.FULFILLED // Change the state
             this._value = value // Store the current value for the then callback// Perform the resolve callback
             while (this._resolveQueue.length) {
                 const callback = this._resolveQueue.shift()
                 callback(value)
             }
         }
     }
     // Encapsulate the resolve callback into a function and place it in setTimeout to implement the promise asynchronous call feature.
     setTimeout(run)
 }
​
 / / to resolve
 const reject = value= > {
     const run = () = > {
         if (this._status === STATUS.PENDING) {
         this._status = STATUS.REJECTED
         this._value = value
        ​
         while (this._rejectQueue.length) {
             const callback = this._rejectQueue.shift()
             callback(value)
         }
     }
 }
     setTimeout(run)
 }

     // Execute executor immediately when new Promise() is passed in resolve and reject
     executor(resolve, reject)
 }
​
 The then method receives a successful callback and a failed callback
 function then(onFulfilled, onRejected) {
  // According to the specification, if the argument to then is not function, it is ignored, and the value is passed down, and the chain call continues
  typeofonFulfilled ! = ='function' ? onFulfilled = value= > value : null
  typeofonRejected ! = ='function' ? onRejected = error= > error : null

  // then returns a new promise
  return new MyPromise((resolve, reject) = > {
    const resolveFn = value= > {
      try {
        const x = onFulfilled(value)
        // The class discusses the return value, if it is a Promise, then wait for the Promise state to change, otherwise resolve
        x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
      } catch (error) {
        reject(error)
      }
    }
  }
}
​
  const rejectFn = error= > {
      try {
        const x = onRejected(error)
        x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
      } catch (error) {
        reject(error)
      }
    }

    switch (this._status) {
      case STATUS.PENDING:
        this._resolveQueue.push(resolveFn)
        this._rejectQueue.push(rejectFn)
        break;
      case STATUS.FULFILLED:
        resolveFn(this._value)
        break;
      case STATUS.REJECTED:
        rejectFn(this._value)
        break; }})}catch (rejectFn) {
  return this.then(undefined, rejectFn)
}
/ / promise. Finally method
finally(callback) {
  return this.then(value= > MyPromise.resolve(callback()).then(() = > value), error= > {
    MyPromise.resolve(callback()).then(() = > error)
  })
}

 // Static resolve method
 static resolve(value) {
      return value instanceof MyPromise ? value : new MyPromise(resolve= > resolve(value))
  }

 // Static reject
 static reject(error) {
      return new MyPromise((resolve, reject) = > reject(error))
    }

 // Static all method
 static all(promiseArr) {
      let count = 0
      let result = []
      return new MyPromise((resolve, reject) = >       {
        if(! promiseArr.length) {return resolve(result)
        }
        promiseArr.forEach((p, i) = > {
          MyPromise.resolve(p).then(value= > {
            count++
            result[i] = value
            if (count === promiseArr.length) {
              resolve(result)
            }
          }, error= > {
            reject(error)
          })
        })
      })
    }

 Static race method
 static race(promiseArr) {
      return new MyPromise((resolve, reject) = > {
        promiseArr.forEach(p= > {
          MyPromise.resolve(p).then(value= > {
            resolve(value)
          }, error= > {
            reject(error)
          })
        })
      })
    }
}
Copy the code

11. Handwritten native AJAX

steps

  1. Create an instance of XMLHttpRequest

  2. Making an HTTP request

  3. The server returns a string in XML format

  4. JS parses the XML and updates local pages

But over the course of history, XML has been phased out in favor of JSON.

Once you know the properties and methods, follow the AJAX steps and write the simplest GET request.

Version 1.0:

myButton.addEventListener('click'.function () {
  ajax()
})

function ajax() {
  let xhr = new XMLHttpRequest() // instantiate to call the method
  xhr.open('get'.'https://www.google.com')  // Parameter 2, url. Parameter 3: Asynchronous
  xhr.onreadystatechange = () = > {  // This function is called whenever the readyState property changes.
    if (xhr.readyState === 4) {  // The current state of the XMLHttpRequest agent.
      if (xhr.status >= 200 && xhr.status < 300) {  //200-300 The request succeeded
        let string = request.responseText
        The // json.parse () method parses JSON strings to construct JavaScript values or objects described by the strings
        let object = JSON.parse(string)
      }
    }
  }
  request.send() // Used to actually make HTTP requests. GET request without parameters
}
Copy the code

Promise to realize

function ajax(url) {
  const p = new Promise((resolve, reject) = > {
    let xhr = new XMLHttpRequest()
    xhr.open('get', url)
    xhr.onreadystatechange = () = > {
      if (xhr.readyState == 4) {
        if (xhr.status >= 200 && xhr.status <= 300) {
          resolve(JSON.parse(xhr.responseText))
        } else {
          reject('Request error')
        }
      }
    }
    xhr.send()  // Send the HPPT request
  })
  return p
}
let url = '/data.json'
ajax(url).then(res= > console.log(res))
  .catch(reason= > console.log(reason))
Copy the code

12. Handwritten throttling anti-shake function

Function throttling and function stabilization are both used to limit the execution frequency of functions, and are a performance optimization scheme, such as those applied towindowObject resize, Scroll events, mousemove events, text input, keyup events.Copy the code

Throttling: Fires events continuously but executes the function only once in n seconds

For example :(continuous motion needs to be called, set the time interval), like dom drag, if you use shake elimination, there will be a feeling of stagnation, because only in the stop of the execution of a time, this time should use throttling, in a certain period of time for many times, it will be much smoother.

Buffering: The function can be executed only once within n seconds after the event is triggered. If the event is triggered again within N seconds, the function execution time is recalculated.

Example :(continuous trigger does not call, triggered after a period of time to call), like imitation baidu search, it should use anti-shake, when I continuously input, will not send a request; I only send a request once if I haven’t entered in a while; If you continue typing less than this time, the time will be recalculated and the request will not be sent.

The realization of anti-shake:

function debounce(fn, delay) {
     if(typeoffn! = ='function') {
        throw new TypeError(F sub n is not a function.)}let timer; // Maintain a timer
     return function () {
         var _this = this; // call debounce to execute scoped this(the object to which the original function is mounted)
         var args = arguments;
         if (timer) {
            clearTimeout(timer);
         }
         timer = setTimeout(function () {
            fn.apply(_this, args); // Apply to the object that called debounce, equivalent to _this.fn(args);
         }, delay);
     };
}

/ / call
input1.addEventListener('keyup', debounce(() = > {
 console.log(input1.value)
}), 600)
Copy the code

Throttling implementation:

function throttle(fn, delay) {
  let timer;
  return function () {
    var _this = this;
    var args = arguments;
    if (timer) {
      return;
    }
    timer = setTimeout(function () {
      fn.apply(_this, args); // Args accepts arguments to the function returned from the outside
      // fn.apply(_this, arguments); Note: Chrome 14 and Internet Explorer 9 still don't accept array-like objects. If an array-like object is passed in, they throw an exception.
      timer = null; // Clear the timer after fn is executed after delay, and throttle triggers to enter the timer
    }, delay)
  }
}

div1.addEventListener('drag', throttle((e) = > {
  console.log(e.offsetX, e.offsetY)
}, 100))

Copy the code

13. Hand write promises to load pictures

function getData(url) {
  return new Promise((resolve, reject) = > {
    $.ajax({
      url,
      success(data) {
        resolve(data)
      },
      error(err) {
        reject(err)
      }
    })
  })
}
const url1 = './data1.json'
const url2 = './data2.json'
const url3 = './data3.json'
getData(url1).then(data1= > {
  console.log(data1)
  return getData(url2)
}).then(data2= > {
  console.log(data2)
  return getData(url3)
}).then(data3= >
  console.log(data3)
).catch(err= >
  console.error(err)
)
Copy the code

14. The function implements one number per second

(!!!!!! This question has been asked in the byte school recruitment interview these days. What is printed by var? Why is it ok to change it to let? Is there another way to do it? In my blog, I wrote the second way to write let, but I forgot it.

ES6: Implemented using the let block-level scope principle

for(let i=0; i<=10; i++){// All prints with var are 11
 setTimeout(() = >{
    console.log(i);
 },1000*i)
}
Copy the code

Written without LET: The principle is to create a block-level scope with a function that executes immediately

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

15. Create 10 tabs and pop up the corresponding serial number when clicked?

var a
for(let i=0; i<10; i++){ a=document.createElement('a')
 a.innerHTML=i+'<br>'
 a.addEventListener('click'.function(e){
     console.log(this)  // This is the current click 
     e.preventDefault()  // If this method is called, the default event behavior will no longer fire.
     For example, after executing this method, if you click on a link (the A tag), the browser will not jump to the new URL. We can use event.isDefaultPrevented() to determine whether this method has been called (on that event object).
     alert(i)
 })
 const d=document.querySelector('div')
 d.appendChild(a)  //append appends an existing element to that element.
}
Copy the code

16. Implement eventBus

Implement EventBus class, with on off once trigger function, corresponding to bind event listener, unbind, execute once to remove event binding, trigger event listener. This topic surface byte and quick hand have asked, recently busy, the answer will be updated in the follow-up

class EventBus {
    on(eventName, listener) {}
    off(eventName, listener) {}
    once(eventName, listener) {}
    trigger(eventName){}}const e = new EventBus();
// fn1 fn2
e.on('e1', fn1)
e.once('e1', fn2)
e.trigger('e1') // fn1() fn2()
e.trigger('e1') // fn1()
e.off('e1', fn1)
e.trigger('e1') // null
Copy the code

Implementation:

      / / class declaration
      class EventBus {
        constructor() {
          this.eventList = {} // Create an object collection event
        }
        // Publish events
        $on(eventName, fn) {
          // Check whether the event name has been published. Add a publication: Create and add a publication
          this.eventList[eventName]
            ? this.eventList[eventName].push(fn)
            : (this.eventList[eventName] = [fn])
        }
        // Subscribe to events
        $emit(eventName) {
          if(! eventName)throw new Error('Please pass in the event name')
          // Get the subscription parameter
          const data = [...arguments].slice(1)
          if (this.eventList[eventName]) {
            this.eventList[eventName].forEach((i) = > {
              try{ i(... data)// Polling events
              } catch (e) {
                console.error(e + 'eventName:' + eventName) // Collect errors during execution}}}})// Execute once
        $once(eventName, fn) {
          const _this = this
          function onceHandle() {
            fn.apply(null.arguments)
            _this.$off(eventName, onceHandle) // Cancel listening after the command is successfully executed
          }
          this.$on(eventName, onceHandle)
        }
        // Unsubscribe
        $off(eventName, fn) {
          // Unsubscribe all subscriptions if no arguments are passed
          if (!arguments.length) {
            return (this.eventList = {})
          }
          // Cancel multiple subscriptions when eventName is passed in an array
          if (Array.isArray(eventName)) {
            return eventName.forEach((event) = > {
              this.$off(event, fn)
            })
          }
          // Cancel all queues under the name of the event if fn is not passed
          if (arguments.length === 1| |! fn) {this.eventList[eventName] = []
          }
          // Cancel the fn in the event name
          this.eventList[eventName] = this.eventList[eventName].filter(
            (f) = >f ! == fn ) } }const event = new EventBus()

      let b = function (v1, v2, v3) {
        console.log('b', v1, v2, v3)
      }
      let a = function () {
        console.log('a')
      }
      event.$once('test', a)
      event.$on('test', b)
      event.$emit('test'.1.2.3.45.123)

      event.$off(['test'], b)

      event.$emit('test'.1.2.3.45.123)

Copy the code

Reference:

Array flattened juejin. Im /post/5c971e…

Im /post/684490…

Throttling stabilization www.jianshu.com/p/c8b86b09d…

Event subscription publishing implementation heznb.com/archives/js…

Deep shallow copy copy segmentfault.com/a/119000001…