Home  >  Article  >  Web Front-end  >  Manually implement call, bind, instanceof in js

Manually implement call, bind, instanceof in js

angryTom
angryTomforward
2020-02-15 17:55:282621browse

In js, call can change the pointer of this, bind can change the pointer of this, and return a function. How is this implemented? This article will take you step by step to implement these functions, and I hope it will be helpful to friends who are learning JavaScript.

Manually implement call, bind, instanceof in js

Preface

Now the front-end threshold is getting higher and higher, and it is no longer just writing The page is so simple. Modularization, automation, cross-end development, etc. are gradually becoming requirements, but these need to be built on our solid foundation. No matter how the framework and model change, only by laying down the basic principles can we quickly adapt to market changes. Here are some commonly used source code implementations:

call implementation

bind implementation

new implementation

instanceof implementation

Object .create implementation

Deep copy implementation

Publish and subscribe mode

call

call is used to change the function this pointer , and execute the function

(recommended js tutorial, welcome to learn!)

Generally, whoever calls the function, the this of the function points to whom. Taking advantage of this feature, by using the function as an attribute of the object and calling it from the object, you can change the this point of the function. This is called implicit binding. Apply implements the same method, just change the input parameter form.

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

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)
  let res = othis._fn(...arg)
  Reflect.deleteProperty(othis, '_fn') //删除_fn属性
  return res
}

Use

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

bind

bind is used to change the function this pointer and return a function

Note:

This points to the constructor call

Maintain 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 将类数组转为数组
  let arg = Array.prototype.slice.call(arguments,1)
  let NOP = function(){}
  let fBound = function(){
    let arg_ = Array.prototype.slice.call(arguments)
    // new 绑定等级高于显式绑定
    // 作为构造函数调用时,保留指向不做修改
    // 使用 instanceof 判断是否为构造函数调用
    return fThis.apply(this instanceof fBound ? this : oThis, arg.concat(arg_))
  }
  // 维护原型
  if(this.prototype){
    NOP.prototype = this.prototype
  }
  fBound.prototype = new NOP()
  return fBound
}

Use

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

new

new uses the constructor to create an instance object, and adds this attribute and method to the instance object.

The process of new:

Create new Object

New object __proto__ points to the constructor prototype

New object adds attribute method (this points to)

Returns the new object pointed to by this

function new_(){
  let fn = Array.prototype.shift.call(arguments)
  if(typeof fn != '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
}

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 proves that it does not exist in the prototype chain, otherwise it does.

function instanceof_(left, right){
  left = left.__proto__
  while(left !== right.prototype){
    left = left.__proto__ // 查找原型,再次while判断
    if(left === null){
      return false
    }
  }
  return true
}

Object.create

Object.create creates a new object, using an existing object to provide the __proto__ of the newly created object, the second optional The parameter is the attribute description object

function objectCreate_(proto, propertiesObject = {}){
  if(typeof proto !== 'object' || typeof proto !== '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
}

Deep copy

Deep copy creates an identical copy of the object, but the reference addresses of the two are different. Deep copying is a good choice when you want to use an object but don't want to modify the original object. A basic version is implemented here, only making deep copies of objects and arrays.

Implementation idea: traverse the object, use recursion to continue copying the reference type, and directly assign the basic type

function deepClone(origin) {
  let toStr = Object.prototype.toString
  let isInvalid = 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
}

Publish and subscribe mode

Publish and subscribe mode in practice Complete decoupling between modules can be achieved during development, and the modules only need to focus on the registration and triggering of events.

Publish and subscribe mode 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 taskQueen = this.task[name]
    if(taskQueen && taskQueen.length > 0){
      taskQueen.forEach(cb=>{
        cb(...arg)
      })
    }
  }

  off(name, cb){
    let taskQueen = this.task[name]
    if(taskQueen && taskQueen.length > 0){
      let index = taskQueen.indexOf(cb)
      index != -1 && taskQueen.splice(index, 1)
    }
  }

  once(name, cb){
    function callback(...arg){
      this.off(name, cb)
      cb(...arg)
    }
    typeof cb === 'function' && this.on(name, callback)
  }
}

Use

let bus = new EventBus()
bus.on('add', function(a,b){
  console.log(a+b)
})
bus.emit('add', 10, 20) //30

The above is the detailed content of Manually implement call, bind, instanceof in js. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:cnblogs.com. If there is any infringement, please contact admin@php.cn delete