Maison  >  Article  >  interface Web  >  Implémenter manuellement call, bind, instanceof dans js

Implémenter manuellement call, bind, instanceof dans js

angryTom
angryTomavant
2020-02-15 17:55:282619parcourir

En js, call peut changer le pointeur de this, bind peut changer le pointeur de this et renvoyer une fonction. Comment est-ce implémenté ? Cet article vous guidera étape par étape dans la mise en œuvre de ces fonctions, et j'espère qu'il sera utile aux amis qui apprennent JavaScript.

Implémenter manuellement call, bind, instanceof dans js

Avant-propos

Aujourd'hui, le seuil front-end est de plus en plus élevé, et il est il ne suffit plus d'écrire. La page est si simple. La modularisation, l'automatisation, le développement cross-end, etc. deviennent progressivement des exigences, mais celles-ci doivent être construites sur des bases solides. Quelle que soit l'évolution du cadre et du modèle, ce n'est qu'en établissant les principes de base que nous pourrons nous adapter rapidement aux évolutions du marché. Voici quelques implémentations de code source couramment utilisées :

implémentation d'appel

implémentation de liaison

nouvelle implémentation

instance d'implémentation

Implémentation de l'objet .create

implémentation de la copie approfondie

mode de publication et d'abonnement

appel

l'appel est utilisé pour changer la fonction vers laquelle cela pointe et exécutez la fonction

(recommander le tutoriel js, bienvenue pour apprendre !)

Généralement, celui qui appelle la fonction, le this de la fonction montre qui. En profitant de cette fonctionnalité, en utilisant la fonction comme attribut de l'objet et en l'appelant depuis l'objet, vous pouvez modifier le point présent de la fonction. C'est ce qu'on appelle la liaison implicite. Apply implémente la même méthode, changez simplement le formulaire des paramètres d’entrée.

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

implémente

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
}

en utilisant

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

bind

bind est utilisé pour modifier ce point de la fonction et renvoyer un function

Remarque :

Ceci, en tant qu'appel du constructeur, pointe vers

Maintenir la chaîne de prototypes

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
}

Utiliser

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

new

new utilise le constructeur pour créer un objet instance et ajoute cet attribut et cette méthode à l'objet instance. Le processus de

new :

.

Créer un nouvel objet

Nouvel objet __proto__ pointe vers le prototype du constructeur

Un nouvel objet ajoute une méthode d'attribut (cela pointe vers)

Renvoie un nouvel objet pointé par 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 détermine si le prototype de gauche existe dans la chaîne de prototypes de droite.

Idée d'implémentation : Rechercher le prototype couche par couche. Si le prototype final est nul, cela prouve qu'il n'existe pas dans la chaîne de prototypes, sinon il existe.

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 crée un nouvel objet, en utilisant un objet existant pour fournir le __proto__ de l'objet nouvellement créé, le deuxième facultatif Le paramètre est l'objet de description d'attribut

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
}

Copie profonde

Copie profonde crée une copie identique de l'objet, mais les adresses de référence des deux sont différentes. La copie approfondie est un bon choix lorsque vous souhaitez utiliser un objet mais ne souhaitez pas modifier l'objet d'origine. Une version de base est implémentée ici, réalisant uniquement des copies complètes des objets et des tableaux.

Idée d'implémentation : parcourir les objets, utiliser la récursion pour continuer à copier les types de référence et attribuer directement les types de base

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
}

Mode publication et abonnement

Publier et le mode d'abonnement en pratique Un découplage complet entre les modules peut être réalisé lors du développement, et les modules doivent uniquement se concentrer sur l'enregistrement et le déclenchement des événements.

Le modèle de publication et d'abonnement implémente 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)
  }
}

Utiliser

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer