Maison > interface Web > js tutoriel > le corps du texte

Explication détaillée du processus depuis l'apprentissage de la liaison jusqu'à la mise en œuvre de la liaison en Javascript

小云云
Libérer: 2018-01-08 09:21:45
original
2082 Les gens l'ont consulté

Cet article présente principalement le processus depuis l'apprentissage de bind jusqu'à l'implémentation de bind en Javascript. Les amis intéressés peuvent suivre et apprendre. J'espère que cela pourra aider tout le monde.

Qu'est-ce que bind ?

La méthode bind() crée une nouvelle fonction et, lorsqu'elle est appelée, définit son mot-clé this sur la valeur fournie lors de l'appel de la nouvelle fonction, dans n'importe quel élément fourni précédemment. avec une séquence d’arguments donnée.

var result = fun.bind(thisArg[, arg1[, arg2[, ...]]]) 
result(newArg1, newArg2...)
Copier après la connexion

Si vous ne comprenez pas, ne vous inquiétez pas, continuez à lire.

Que fait exactement bind ?

Trois points ressortent de l'introduction ci-dessus. Tout d'abord, l'appel de la méthode bind renverra une nouvelle fonction (le corps de cette nouvelle fonction doit être le même que fun). En même temps, deux paramètres sont transmis dans bind. Le premier est pointé par ceci, c'est-à-dire que tout ce qui est transmis est égal à ce qu'il est. Comme indiqué dans le code suivant :

this.value = 2
var foo = {
  value: 1
}
var bar = function() {
 console.log(this.value)
}
var result = bar.bind(foo)
bar() // 2
result() // 1,即this === foo
Copier après la connexion

Le deuxième paramètre est une séquence et vous pouvez y transmettre n'importe quel nombre de paramètres. Et il sera préréglé avant les nouveaux paramètres de fonction.

this.value = 2
var foo = {
  value: 1
};
var bar = function(name, age, school) {
 console.log(name) // 'An'
 console.log(age) // 22
 console.log(school) // '家里蹲大学'
}
var result = bar.bind(foo, 'An') //预置了部分参数'An'
result(22, '家里蹲大学') //这个参数会和预置的参数合并到一起放入bar中
Copier après la connexion

Nous pouvons voir que lorsque result(22, 'Study at home in college') est finalement appelé, il contient déjà le 'An' transmis lors de l'appel de bind.

Résumé en une phrase : appeler bind renverra une nouvelle fonction. Ceci dans cette fonction pointe vers le premier paramètre de liaison, et les paramètres suivants seront transmis à cette nouvelle fonction à l'avance. Lorsque la nouvelle fonction est appelée, les paramètres transmis seront placés après les paramètres prédéfinis et transmis ensemble dans la nouvelle fonction.

Implémentez une liaison par vous-même

Pour implémenter une liaison, vous devez implémenter les deux fonctions suivantes

Renvoyer une fonction, la lier et passer les paramètres prédéfinis

La fonction renvoyée par bind peut être utilisée comme constructeur. Par conséquent, lorsqu'il est utilisé comme constructeur, cela devrait être invalide, mais les paramètres transmis sont toujours valides

1 Renvoyez une fonction, liez-la et transmettez les paramètres prédéfinis

this.value = 2
var foo = {
  value: 1
};
var bar = function(name, age, school) {
  console.log(name) // 'An'
  console.log(age) // 22
  console.log(school) // '家里蹲大学'
  console.log(this.value) // 1
}
Function.prototype.bind = function(newThis) {
  var aArgs  = Array.prototype.slice.call(arguments, 1) //拿到除了newThis之外的预置参数序列
  var that = this
  return function() {
    return that.apply(newThis, aArgs.concat(Array.prototype.slice.call(arguments)))
    //绑定this同时将调用时传递的序列和预置序列进行合并
  }
}
var result = bar.bind(foo, 'An')
result(22, '家里蹲大学')
Copier après la connexion
.

Ce détail est la phrase Array.prototype.slice.call(arguments, 1). Nous savons que la variable arguments peut obtenir les paramètres transmis lorsque la fonction est appelée, mais ce n'est pas un tableau, mais c'est le cas. a un attribut de longueur. Pourquoi peut-il être transformé en un tableau pur en l'appelant ainsi ? Ensuite, nous devons revenir au code source de la V8 pour analyse. #Le code source de cette version est une première version avec relativement moins de contenu.

function ArraySlice(start, end) {
 var len = ToUint32(this.length); 
 //需要传递this指向对象,那么call(arguments),
 //便可将this绑定到arguments,拿到其length属性。
 var start_i = TO_INTEGER(start);
 var end_i = len;
 if (end !== void 0) end_i = TO_INTEGER(end);
 if (start_i < 0) {
  start_i += len;
  if (start_i < 0) start_i = 0;
 } else {
  if (start_i > len) start_i = len;
 }
 if (end_i < 0) {
  end_i += len;
  if (end_i < 0) end_i = 0;
 } else {
  if (end_i > len) end_i = len;
 }
 var result = [];
 if (end_i < start_i)
  return result;
 if (IS_ARRAY(this))
  SmartSlice(this, start_i, end_i - start_i, len, result);
 else 
  SimpleSlice(this, start_i, end_i - start_i, len, result);
 result.length = end_i - start_i;
 return result;
};
Copier après la connexion

Vous pouvez voir dans le code source qu'après avoir attribué l'attribut length sous les arguments pour découper l'appel, vous pouvez obtenir le tableau final via start_i et end_i, c'est donc un tableau pur sans le passer dans slice Le tableau peut également obtenir une variable de tableau à la fin.

2. La fonction renvoyée par bind peut être utilisée comme constructeur

Lorsqu'elle est utilisée comme constructeur, elle doit pointer vers l'instance de new, et il doit également y avoir un. attribut prototype, qui pointe vers le prototype de l'instance.

this.value = 2
var foo = {
 value: 1
};
var bar = function(name, age, school) {
 ...
 console.log('this.value', this.value)
}
Function.prototype.bind = function(newThis) {
 var aArgs  = Array.prototype.slice.call(arguments, 1)
 var that = this //that始终指向bar
 var NoFunc = function() {}
 var resultFunc = function() {
  return that.apply(this instanceof that ? this : newThis, aArgs.concat(Array.prototype.slice.call(arguments)))
 } 
 NoFunc.prototype = that.prototype //that指向bar
 resultFunc.prototype = new NoFunc()
 return resultFunc
}
var result = bar.bind(foo, 'An')
result.prototype.name = 'Lsc' // 有prototype属性
var person = new result(22, '家里蹲大学')
console.log('person', person.name) //'Lsc'
Copier après la connexion

Le code de simulation ci-dessus fait deux choses importantes.

1. Simulez un attribut prototype pour la fonction renvoyée. , car les propriétés et méthodes définies sur le prototype peuvent être interrogées via l'instance du constructeur new

var NoFunc = function() {}
...
NoFunc.prototype = that.prototype //that指向bar
resultFunc.prototype = new NoFunc()
return resultFunc
Copier après la connexion

Comme le montre le code ci-dessus, qui pointe toujours vers bar. Dans le même temps, la fonction renvoyée a hérité de ce.prototype, qui est bar.prototype. Pourquoi ne pas simplement rendre l'attribut prototype resultFunc.prototype de la fonction renvoyée égal à bar(that).prototype ? En effet, toute nouvelle instance peut accéder à la chaîne de prototypes. S'il est affecté directement, le nouvel objet peut modifier directement la chaîne prototype de la fonction barre, qui est une pollution de la chaîne prototype. Nous utilisons donc l'héritage (attribuons la chaîne de prototypes du constructeur à l'instance du constructeur parent) pour désengager la chaîne de prototypes du nouvel objet de bar.

2. Déterminez si this est utilisé pour une liaison ordinaire ou pour le constructeur lorsqu'il est actuellement appelé pour en changer le point.

Comment déterminer où cela pointe actuellement ? Dès le premier point, nous savons déjà que la nouvelle fonction renvoyée par la méthode bind a déjà une chaîne prototype. Il ne nous reste plus qu'à changer la chaîne. pointant ceci pour le simuler. Comment juger la posture actuelle d'être appelé. La réponse est instanceof. L'opérateur

instanceof est utilisé pour tester si un objet possède la propriété prototype d'un constructeur dans sa chaîne de prototypes.

// 定义构造函数
function C(){} 
function D(){} 
var o = new C();
// true,因为 Object.getPrototypeOf(o) === C.prototype
o instanceof C; 
// false,因为 D.prototype不在o的原型链上
o instanceof D;
Copier après la connexion

Comme le montre ce qui précède, instanceof peut déterminer si un objet est nouveau grâce à cette fonction. S'il est nouveau, alors la chaîne de prototypes de cet objet doit être le prototype de la fonction.

Jetons donc un coup d'œil à la structure de la fonction renvoyée par la clé :

var resultFunc = function() {
  return that.apply(this instanceof that ? 
    this : 
    newThis, 
    aArgs.concat(Array.prototype.slice.call(arguments)))
 }
Copier après la connexion

En cela, nous devons d'abord reconnaître que this dans cette instance de that est après l'appel de la fonction de liaison, ceci dans la nouvelle fonction renvoyée. Cela peut donc être exécuté dans un environnement de portée normale, et il peut également être nouveau de changer de direction. En y repensant, cela pointe toujours vers bar, et sa chaîne de prototypes that.prototype existe toujours. Donc, si cette nouvelle fonction effectue maintenant une nouvelle opération, alors ceci pointe vers la nouvelle fonction, alors cette instance de cela === true, alors transmettez-la dans apply comme pointeur, qui pointe vers la nouvelle fonction. S'il s'agit d'un appel normal, alors celui-ci n'est pas créé par new, c'est-à-dire que la nouvelle fonction n'est pas utilisée comme constructeur, et cette instance de === false est évidente. Cette fois, c'est un appel de liaison normal. Utilisez simplement le premier paramètre de l’appel comme pointeur vers ceci.

Code complet (implémentation sous MDN)

if (!Function.prototype.bind) {
 Function.prototype.bind = function(oThis) {
  if (typeof this !== 'function') {
   // closest thing possible to the ECMAScript 5
   // internal IsCallable function
   throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
  }

  var aArgs  = Array.prototype.slice.call(arguments, 1),
    fToBind = this,
    fNOP  = function() {},
    fBound = function() {
     return fToBind.apply(this instanceof fNOP
         ? this
         : oThis,
         aArgs.concat(Array.prototype.slice.call(arguments)));
    };

  if (this.prototype) {
   // Function.prototype doesn't have a prototype property
   fNOP.prototype = this.prototype; 
  }
  fBound.prototype = new fNOP();
  return fBound;
 };
}
Copier après la connexion

可以看到,其首先做了当前是否支持bind的判定,不支持再实行兼容。同时判断调用这个方法的对象是否是个函数,如果不是则报错。

同时这个模拟的方法也有一些缺陷,可关注MDN上的Polyfill部分

小结

模拟bind实现最大的一个缺陷是,模拟出来的函数中会一直存在prototype属性,但是原生的bind作为构造函数是没有prototype的,这点打印一下即可知。不过这样子new出来的实例没有原型链,那么它的意义是什么呢。

相关推荐:

Jquery中.bind()、.live()、.delegate()和.on()之间的区别实例分享

jQuery中关于bind()函数详解

Js的this指向 apply().call(),bind()的问题

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!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!