Heim  >  Artikel  >  Web-Frontend  >  Detaillierte Erläuterung des Prozesses vom Erlernen von Bind bis zur Implementierung von Bind in Javascript

Detaillierte Erläuterung des Prozesses vom Erlernen von Bind bis zur Implementierung von Bind in Javascript

小云云
小云云Original
2018-01-08 09:21:452138Durchsuche

Dieser Artikel stellt hauptsächlich den Prozess vom Erlernen von Bind bis zur Implementierung von Bind in Javascript vor. Freunde, die interessiert sind, können mitmachen und lernen.

Was ist bind?

Die bind()-Methode erstellt eine neue Funktion und setzt beim Aufruf der neuen Funktion ihr Schlüsselwort auf den bereitgestellten Wert mit einer gegebenen Folge von Argumenten.

var result = fun.bind(thisArg[, arg1[, arg2[, ...]]]) 
result(newArg1, newArg2...)

Wenn Sie es nicht verstehen, machen Sie sich keine Sorgen, lesen Sie weiter.

Was genau macht bind?

Drei Punkte sind aus der obigen Einleitung ersichtlich. Erstens gibt der Aufruf der Bind-Methode eine neue Funktion zurück (der Funktionskörper dieser neuen Funktion sollte mit fun identisch sein). Gleichzeitig werden in bind zwei Parameter übergeben. Der erste wird durch this angezeigt, das heißt, was auch immer übergeben wird, ist gleich dem, was es ist. Wie im folgenden Code gezeigt:

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

Der zweite Parameter ist eine Sequenz, und Sie können eine beliebige Anzahl von Parametern daran übergeben. Und es werden vor den neuen Funktionsparametern voreingestellt.

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中

Wir können sehen, dass result(22, 'Study at home in College') schließlich aufgerufen wird, es bereits das 'An' enthält, das beim Aufruf von bind übergeben wurde.

Ein Satz Zusammenfassung: Der Aufruf von bind gibt eine neue Funktion zurück. Dies zeigt in dieser Funktion auf den ersten Parameter von bind, und die darauf folgenden Parameter werden im Voraus an diese neue Funktion übergeben. Beim Aufruf der neuen Funktion werden die übergebenen Parameter nach den voreingestellten Parametern platziert und gemeinsam an die neue Funktion übergeben.

Implementieren Sie eine Bindung selbst

Um eine Bindung zu implementieren, müssen Sie die folgenden zwei Funktionen implementieren

Eine Funktion zurückgeben, diese binden und Voreingestellte Parameter übergeben

Die von bind zurückgegebene Funktion kann als Konstruktor verwendet werden. Daher sollte dies bei Verwendung als Konstruktor ungültig sein, aber die übergebenen Parameter sind immer noch gültig

1 Geben Sie eine Funktion zurück, binden Sie diese und übergeben Sie die voreingestellten Parameter

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, '家里蹲大学')

Dieses Eine Detail darin ist der Satz Array.prototype.slice.call(arguments, 1) Wir wissen, dass die Variable arguments die übergebenen Parameter erhalten kann, wenn die Funktion aufgerufen wird, aber es ist kein Array, sondern es hat ein Längenattribut. Warum kann es durch diesen Aufruf in ein reines Array umgewandelt werden? Dann müssen wir zur Analyse zum Quellcode von V8 zurückkehren. #Der Quellcode dieser Version ist eine frühe Version mit relativ wenig Inhalt.

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;
};

Sie können dem Quellcode entnehmen, dass Sie nach der Zuweisung des Längenattributs unter den Argumenten zum Slice-Through-Aufruf das endgültige Array über start_i und end_i erhalten können, sodass es ein reines Array ist, ohne es an zu übergeben Das Array kann am Ende auch eine Array-Variable erhalten.

2. Die von bind zurückgegebene Funktion kann als Konstruktor verwendet werden.

Bei Verwendung als Konstruktor sollte dies auf die Instanz von new verweisen, und es sollte auch eine vorhanden sein Prototyp-Attribut, das auf die Instanz verweist.

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'

Der obige Simulationscode erledigt zwei wichtige Dinge.

1. Simulieren Sie ein Prototypattribut für die zurückgegebene Funktion. , weil die im Prototyp definierten Eigenschaften und Methoden über die Instanz vom Konstruktor new abgefragt werden können

var NoFunc = function() {}
...
NoFunc.prototype = that.prototype //that指向bar
resultFunc.prototype = new NoFunc()
return resultFunc

Wie aus dem obigen Code ersichtlich ist, zeigt dieser immer auf bar. Gleichzeitig hat die zurückgegebene Funktion that.prototype geerbt, also bar.prototype. Warum nicht einfach das Prototypattribut resultFunc.prototype der zurückgegebenen Funktion gleich machen bar(that).prototype Dies liegt daran, dass jede neue Instanz auf die Prototypenkette zugreifen kann. Bei direkter Zuweisung kann das neue Objekt die Prototypenkette der Balkenfunktion direkt ändern, was eine Verschmutzung der Prototypenkette darstellt. Daher verwenden wir Vererbung (weisen Sie die Prototypenkette des Konstruktors der Instanz des übergeordneten Konstruktors zu), um die Prototypenkette des neuen Objekts von bar zu trennen.

2. Bestimmen Sie, ob dies für die normale Bindung oder für den Konstruktor verwendet wird, wenn er aktuell aufgerufen wird, um den Punkt davon zu ändern.

Wie können wir feststellen, wohin dies aktuell zeigt? Vom ersten Punkt an wissen wir bereits, dass die neue Funktion, die von der Bindungsmethode zurückgegeben wird, bereits über eine Prototypenkette verfügt davon, um es zu simulieren. Wie beurteilt man die aktuelle Haltung des Angerufenen? Die Antwort ist Instanz von. Der Operator

instanceof wird verwendet, um zu testen, ob ein Objekt in seiner Prototypenkette über die Prototypeigenschaft eines Konstruktors verfügt.

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

Wie aus dem Obigen ersichtlich ist, kann Instanz von anhand dieser Funktion feststellen, ob ein Objekt neu ist. Wenn es neu ist, sollte die Prototypenkette dieses Objekts der Prototyp der Funktion sein.

Schauen wir uns also die Struktur der zurückgegebenen Schlüsselfunktion an:

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

Dabei müssen wir zunächst erkennen, dass dies in dieser Instanz nach dem Aufruf der Bindungsfunktion erfolgt. dies in der zurückgegebenen neuen Funktion. Dies kann also in einer normalen Scope-Umgebung ausgeführt werden, und es kann auch neu sein, die Richtung zu ändern. Wenn man das noch einmal betrachtet, zeigt das immer auf bar und seine Prototypenkette that.prototype existiert immer. Wenn diese neue Funktion nun eine neue Operation ausführt, dann zeigt dies auf die neue Funktion, dann ist diese Instanz davon === wahr, also übergeben Sie dies in apply als Zeiger, der auf die neue Funktion zeigt. Wenn es sich um einen normalen Aufruf handelt, wird dieser nicht von new erstellt, d. h. die neue Funktion wird nicht als Konstruktor verwendet, und diese Instanz davon === false ist offensichtlich. Diesmal handelt es sich um einen normalen Bind-Aufruf. Verwenden Sie einfach den ersten Parameter des Aufrufs als Zeiger darauf.

Vollständiger Code (Implementierung unter 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;
 };
}

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

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

小结

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

相关推荐:

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

jQuery中关于bind()函数详解

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

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung des Prozesses vom Erlernen von Bind bis zur Implementierung von Bind in Javascript. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn