preface

Now the front end of the bar is getting higher and higher, not just write pages so simple. Modularity, automation, cross-end development, etc. are increasingly required, but these need to be built on our solid foundation. No matter how the framework and model change, the basic principles of the firm to quickly adapt to the changes in the market. Here are some common source implementations:

  • The call to realize
  • The bind to realize
  • The new implementation
  • Instanceof implementation
  • Object. The create implementation
  • Deep copy implementation
  • Publish subscribe model

call

Call is used to change the reference of the function this and to execute the function

In general, the function’s “this” refers to the person who called it. Using this feature, you can change the point of this function by calling it as a property of an object. This is called implicit binding. The apply implementation does the same, simply changing the input parameter form.

let obj = {
  name: 'JoJo'
}
function foo(){
  console.log(this.name)
}
obj.fn = foo
obj.fn() // log: JoJo
Copy the code

implementation

Function.prototype.mycall = function () {
  if(typeof this! = ='function') {throw 'caller must be a function'
  }
  let othis = arguments[0] | |window
  othis._fn = this
  let arg = [...arguments].slice(1)
  letres = othis._fn(... arg)Reflect.deleteProperty(othis, '_fn') // Delete the _fn attribute
  return res
}
Copy the code

use

let obj = {
  name: 'JoJo'
}
function foo(){
  console.log(this.name)
}
foo.mycall(obj) // JoJo
Copy the code

bind

Bind changes the reference to this and returns a function

Note:

  • As a constructor call, this points to
  • Maintaining the prototype chain
Function.prototype.mybind = function (oThis) {
  if(typeof this! ='function') {throw 'caller must be a function'
  }
  let fThis = this
  / / Array. Prototype. Slice. Call the class Array into an Array
  let arg = Array.prototype.slice.call(arguments.1)
  let NOP = function(){}
  let fBound = function(){
    let arg_ = Array.prototype.slice.call(arguments)
    // New binding is higher than explicit binding
    // When called as a constructor, the pointer is left unchanged
    // Use instanceof to determine if it is a constructor call
    return fThis.apply(this instanceof fBound ? this : oThis, arg.concat(arg_))
  }
  // Maintain the prototype
  if(this.prototype){
    NOP.prototype = this.prototype
    fBound.prototype = new NOP()
  }
  return fBound
}
Copy the code

use

let obj = {
  msg: 'JoJo'
}
function foo(msg){
  console.log(msg + ' ' + this.msg)
}
let f = foo.mybind(obj)
f('hello') // hello JoJo
Copy the code

new

New uses the constructor to create an instance object, adding the this property and method to the instance object

2. The process of new:

  1. Create a new object
  2. The new object __proto__ refers to the constructor prototype
  3. New object adds property method (this points to)
  4. Returns the new object to which this refers
function new_(){
  let fn = Array.prototype.shift.call(arguments)
  if(typeoffn ! ='function') {throw fn + ' is not a constructor'
  }
  let obj = {}
  obj.__proto__ = fn.prototype
  let res = fn.apply(obj, arguments)
  return typeof res === 'object' ? res : obj
}
Copy the code

In ES5, we can simplify this process by using object.create:

function new_(){
  let fn = Array.prototype.shift.call(arguments)
  if(typeoffn ! ='function') {throw fn + ' is not a constructor'
  }
  let obj = Object.create(fn.prototype)
  let res = fn.apply(obj, arguments)
  return typeof res === 'object' ? res : obj
}
Copy the code

instanceof

Instanceof determines whether the prototype on the left exists in the prototype chain on the right.

Implementation idea: look up the prototype layer by layer, if the final prototype is null, it does not exist in the prototype chain, otherwise exist.

function instanceof_(left, right){
  left = left.__proto__
  while(left ! == right.prototype){ left = left.__proto__// Look for the prototype and while it again
    if(left === null) {return false}}return true
}
Copy the code

Object.create

Object.create creates a new Object, using an existing Object to provide the __proto__ of the newly created Object. The second optional argument is a property describing the Object

function objectCreate_(proto, propertiesObject = {}){
  if(typeofproto ! = ='object' && typeofproto ! = ='function'&& proto ! = =null) {throw('Object prototype may only be an Object or null:'+proto)
  }
  let res = {}
  res.__proto__ = proto
  Object.defineProperties(res, propertiesObject)
  return res
}
Copy the code

Deep copy

A deep copy creates an identical copy of an object with different reference addresses. A deep copy is a good choice when you want to use an object but don’t want to modify the original object. Here we implement a base version that only makes deep copies of objects and arrays.

Implementation idea: traversal object, reference type use recursion to continue to copy, basic type direct assignment

function deepClone(origin) {
  let toStr = Object.prototype.toString
  letisInvalid = toStr.call(origin) ! = ='[object Object]'&& toStr.call(origin) ! = ='[object Array]'
  if (isInvalid) {
    return origin
  }
  let target = toStr.call(origin) === '[object Object]' ? {} : []
  for (const key in origin) {
    if (origin.hasOwnProperty(key)) {
      const item = origin[key];
      if (typeof item === 'object'&& item ! = =null) {
        target[key] = deepClone(item)
      } else {
        target[key] = item
      }
    }
  }
  return target
}
Copy the code

Publish subscribe model

The publis-subscribe pattern enables complete decoupling between modules in real development, where modules only need to be concerned with registering and triggering events.

Publish and subscribe to implement EventBus:

class EventBus{
  constructor(){
    this.task = {}
  }

  on(name, cb){
    if(!this.task[name]){
      this.task[name] = []
    }
    typeof cb === 'function' && this.task[name].push(cb)
  }

  emit(name, ... arg){
    let taskQueue = this.task[name]
    if(taskQueue && taskQueue.length > 0){
      taskQueue.forEach(cb= >{ cb(... arg) }) } }off(name, cb){
    let taskQueue = this.task[name]
    if(taskQueue && taskQueue.length > 0) {if(typeof cb === 'function') {letindex = taskQueue.indexOf(cb) index ! = -1 && taskQueue.splice(index, 1)}if(typeof cb === 'undefined') {this.task[name].length = 0}}}once(name, cb){
    let callback = (. arg) = > {
      this.off(name, callback) cb(... arg) }typeof cb === 'function' && this.on(name, callback)
  }
}
Copy the code

use

let bus = new EventBus()
bus.on('add'.function(a,b){
  console.log(a+b)
})
bus.emit('add'.10.20) / / 30
Copy the code