• Write a new function by hand
  • Write the bind/call/apply functions by hand
  • Handwritten anti – shake and throttling functions
  • Handwriting function coriolization function
  • Write a Promise by hand
  • Write a deep copy function
  • Write a publish subscribe model

Implement the new procedure:

Key points:

  1. The first argument to the function is the constructor
  2. The instance’s __proto__ points to the constructor’s prototype property
  3. The rest of the function arguments are mounted to an instance object
  4. When the constructor has a return value, it returns that return value
const createObj = function () {
  let obj = {}
  let Constructor = [].shift.call(arguments) / / 1
  obj.__proto__ = Constructor.prototype / / 2
  let ret = Constructor.apply(obj, arguments) / / 3
  return typeof ret === 'object' ? ret: obj / / 4
}

/ / use
const Fun = function (name) {
  this.name = name
}
Fun.prototype.getName = function() {
  alert(this.name)
}
let fun = createObj(Fun, 'gim')
fun.getName() // gim
Copy the code

Note that the class of ES6 must be called with new, otherwise an error will be reported as follows:

class Fun {
  constructor(name) {
    this.name = name
  }
  getName() {
    alert(this.name)
  }
}
let fun = createObj(Fun, 'gim')
fun.getName() // Uncaught TypeError: Class constructor Fun cannot be invoked without 'new'
Copy the code

Write call, apply, and bind functions by hand

Thing in common:

  1. The first argument is the this to bind
  2. This inside the function is actually the function to bind to (because all three are dot calls)

bind

The simple version is implemented here (the new call results in different results), but the complex version is recommended

  1. When bind is executed, it returns a copy of the original function
  2. The context passed to the FN binding inside the return function
Function.prototype.myBind = function(context, ... args) {
  if (typeof this! = ='function') throw 'caller must be a function'
  const fn = this
  return function() {
    returnfn.call(context, ... args, ... arguments) } }Copy the code

The call and apply functions are implemented with point calls. Use the first parameter to make a relay, after the call to delete.

call

Function.prototype.myCall = function(context = windows, ... args) {
  context._fn = this
  constresult = context._fn(... args)delete context._fn
  return result
}
Copy the code

apply

Function.prototype.myApply = function(context = windows, args) {
  context._fn = this
  const result = context._fn(args)
  delete context._fn
  return result
}
Copy the code

Throttling and anti-shaking

When I first came into contact with these two concepts, I couldn’t tell them apart.

Some browser events, such as: resize, Scroll, KeyDown, KeyUp, keyPress, Mousemove, etc. These events are triggered so frequently that the callbacks bound to them will be called over and over again. It will burden the browser and make the user experience very bad.

Throttling and anti-shaking are mainly made use of closures.

The throttle

Throttling function to fire the function every n milliseconds.

/ / throttling
function throttle (f, wait = 200) {
  let last = 0
  return function (. args) { // All the following internal anonymous functions refer to this anonymous function
    let now = Date.now()
    if (now - last > wait) {
      last = now
      f.apply(this, args) It is useful to note that this of f is bound to this of the internal anonymous function}}}/ / not throttling
input.onkeyup = funciton () {
  $.ajax(url, this.value)
}
/ / throttling
input.onkeyup = throttle(function () { // throttle() returns the internal anonymous function, so input is bound to this of the internal anonymous function
  $.ajax(url, this.value) // Notice that this is applied to this (input) on the inner anonymous function when executed
})
Copy the code

Image stabilization

The stabilization function causes the function to fire only once in n milliseconds.

/ / image stabilization
function debounce (f, wait = 200) {
  let timer = 0
  return function (. args) {
    clearTimeout(timer)
    timer = setTimeout(() = > {
      f.apply(this, args)
    }, wait)
  }
}
/ / not stabilization
input.onkeyup = funciton () {
  $.ajax(url, this.value)
}
/ / image stabilization
input.onkeyup = debounce(function () { // debounce() returns an internal anonymous function, so input is bound to this of the internal anonymous function
  $.ajax(url, this.value) // Notice that this is applied to this (input) on the inner anonymous function when executed
})
Copy the code

Currization function

Currization can use functions and different parameters to form more specialized functions.

Currization is the use of closure technology to cache functions and parameters again and again, until the parameters are enough to execute the function.

function curry(fn, ... rest) {
  const length = fn.length
  return function() {
    const args = [...rest, ...arguments]
    if (args.length < length) {
      return curry.call(this, fn, ... args) }else {
      return fn.apply(this, args)
    }
  }
}
function add(m, n) {
  return m + n
}
const add5 = curry(add, 5)
Copy the code

Promise

Key points:

  1. Three state changes:pending fulfilled rejected
  2. resolve() reject()Function implementation
  3. The key pointthenImplementation of chain calls
class MyPromise {
  constructor(fn) {
    this.status = 'pending'
    this.value = null
    this.resolve = this._resolve.bind(this)
    this.reject = this._reject.bind(this)
    this.resolvedFns = []
    this.rejectedFns = []
    try {
      fn(this.resolve, this.reject)
    } catch (e) {
      this.catch(e)
    }
  }
  _resolve(res) {
    setTimeout(() = > {
      this.status = 'fulfilled'
      this.value = res
      this.resolvedFns.forEach(fn= > {
        fn(res)
      })
    })
  }
  _reject(res) {
    setTimeout(() = > {
      this.status = 'rejected'
      this.value = res
      this.rejectedFns.forEach(fn= > {
        fn(res)
      })
    })
  }
  then(resolvedFn, rejecetedFn) {
    return new MyPromise(function(resolve, reject) {
      this.resolveFns.push(function(value) {
        try {
          const res = resolvedFn(value)
          if (res instanceof MyPromise) {
            res.then(resolve, reject)
          } else {
            resolve(res)
          }
        } catch (err) {
          reject(err)
        }
      })
      this.rejectedFns.push(function(value){
        try {
          const res = rejectedFn(value)
          if (res instanceof MyPromise) {
            res.then(resolve, reject)
          } else {
            reject(res)
          }
        } catch (err) {
          reject(err)
        }
      })
    })
  }
  catch(rejectedFn) {
    return this.then(null, rejectedFn)
  }
}
Copy the code

This. resolvedFns and this.rejectedFns store the processing logic for the arguments of the then function, which will be executed when the Promise operation has a result.

The then function returns a Promise to implement the chain call.

In fact, I mainly rely on rote memorization during the interview, because ONCE I was asked to write 5 realizations for 20 minutes (including promise), who gave you time to think…

Deep copy

The beggar’s version of

function deepCopy(obj) {
  // Check if it is a simple data type,
  if (typeof obj == "object") {
    // Complex data types
    var result = obj.constructor == Array ? [] : {};
    for (let i in obj) {
      result[i] = typeof obj[i] == "object"? deepCopy(obj[i]) : obj[i]; }}else {
    // Assign to simple data types directly ==
    var result = obj;
  }
  return result;
}
Copy the code

Observer mode and publish/subscribe mode

Observer mode Observer Observer and Subject are relatively clear, while publication and subscription mode are handled by a scheduling center, and the boundaries between publisher and subscriber are blurred.

The observer pattern is coupled in that the body stores the instance of the observer, and the notify method calls the update method of the observer over and over. The publish-subscribe model is completely decoupled because the logical processing functions are stored directly in the dispatch center.

Important points: all three events need to be added, removed, and updated.

Observer model

class Subject {
  constructor() {
    this.observers = []
  }
  add(observer) {
    this.observers.push(observer)
    this.observers = [...new Set(this.observers)]
  }
  notify(. args) {
    this.observers.forEach(observer= >observer.update(... args)) }remove(observer) {
    let observers = this.observers
    for (let i = 0, len = observers.length; i < len; i++) {
      if (observers[i] === observer) observers.splice(i, 1)}}}class Observer {
  update(. args) {
    console.log(... args) } }let observer_1 = new Observer() // Create observer 1
let observer_2 = new Observer()
let sub = new Subject() // Create the principal
sub.add(observer_1) // Add observer 1
sub.add(observer_2)
sub.notify('I changed ! ')
Copy the code

Publish and subscribe model

The private # Handlers attribute of class, which is still in the proposal stage, is used here, but is supported by mainstream browsers.

class Event {
  // Define an event container to hold an array of events (since subscribers can be multiple)
  #handlers = {}

  // Add event method. Parameters include event name and event method
  addEventListener(type, handler) {
    Handlers do not have a type event container, or create a new array container
    if(! (typein this.#handlers)) {
      this.#handlers[type] = []
    }
    // Store the event
    this.#handlers[type].push(handler)
  }

  // Trigger the event with two parameters (event name, parameters)
  dispatchEvent(type, ... params) {
    // An error is thrown if the event is not registered
    if(! (typein this.#handlers)) {
      return new Error('Not registered for this event')}// Facilitate triggering
    this.#handlers[type].forEach(handler= >{ handler(... params) }) }// Event removal parameters (event name, deleted event, delete subscription and publication of event if there is no second parameter)
  removeEventListener(type, handler) {
    // Invalid events were thrown
    if(! (typein this.#handlers)) {
      return new Error('Invalid event')}if(! handler) {// Remove the event directly
      delete this.#handlers[type]
    } else {
      const idx = this.#handlers[type].findIndex(ele= > ele === handler)
      // Throw an exception event
      if (idx === -1) {
        return new Error('No such binding event')}// Remove the event
      this.#handlers[type].splice(idx, 1)
      if (this.#handlers[type].length === 0) {
        delete this.#handlers[type]
      }
    }
  }
}
Copy the code

recommended

Recently, I was asked to write a handwritten Promise in the interview. I had planned to write a good article about handwriting functions, but I found a good article in the process of writing. After reading it, I felt like I had nothing left to write.

portal