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.
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!