Heim > Web-Frontend > js-Tutorial > Hauptteil

So implementieren Sie bind in Javascript

亚连
Freigeben: 2018-06-13 15:30:36
Original
1487 Leute haben es durchsucht

In diesem Artikel wird hauptsächlich der Prozess vom Erlernen von Bind bis zur Implementierung von Bind in Javascript vorgestellt. Interessierte Freunde können mitmachen und lernen.

Was ist bind?

Die Methode bind() erstellt eine neue Funktion und setzt beim Aufruf ihr Schlüsselwort this auf den bereitgestellten Wert of stellt beim Aufruf einer neuen Funktion eine bestimmte Folge von Argumenten vor jeder Lieferung bereit.

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

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

Was genau macht bind?

Drei Punkte sind aus der obigen Einführung 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
Nach dem Login kopieren

Der zweite Parameter ist eine Sequenz, in die Sie beliebig viele Parameter übergeben können. Und es wird 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中
Nach dem Login kopieren

Wir können sehen, dass result(22, 'Study at home in College') schließlich aufgerufen wurde, es bereits das 'An' enthielt, 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

Gib a zurück Funktion und binden Sie dies, übergeben Sie die voreingestellten Parameter

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

Es gibt Ein Detail hier: Array.prototype.slice.call(arguments, 1) In diesem Satz wissen wir, dass die Variable arguments die beim Aufruf der Funktion übergebenen Parameter abrufen kann, aber es ist kein Array, sondern 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;
};
Nach dem Login kopieren

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 sich um ein reines Array handelt, wenn dies nicht erforderlich ist an Slice übergeben werden. Sie können auch eine Array-Variable erhalten.

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

Bei Verwendung als Konstruktor sollte diese auf die von new erzeugte Instanz verweisen und es sollte auch ein Prototypattribut vorhanden sein , was auf den Prototyp der Instanz verweist.

this.value = 2
var foo = {
 value: 1
};
var bar = function(name, age, school) {
 ...
 console.log(&#39;this.value&#39;, 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, &#39;An&#39;)
result.prototype.name = &#39;Lsc&#39; // 有prototype属性
var person = new result(22, &#39;家里蹲大学&#39;)
console.log(&#39;person&#39;, person.name) //&#39;Lsc&#39;
Nach dem Login kopieren

Der obige Simulationscode erledigt zwei wichtige Dinge.

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

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

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 bar(that).prototype machen? 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 gerade 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 Darauf zeigen, um es zu simulieren. So ermitteln Sie die aktuell aufgerufene Haltung. 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;
Nach dem Login kopieren

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)))
 }
Nach dem Login kopieren

Dabei müssen wir zunächst erkennen, dass dies in dieser Instanz die neue Funktion ist, die nach dem Aufruf der Bindefunktion zurückgegeben wird. 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ühren muss, 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.

完整代码(MDN下的实现)

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

  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&#39;t have a prototype property
   fNOP.prototype = this.prototype; 
  }
  fBound.prototype = new fNOP();
  return fBound;
 };
}
Nach dem Login kopieren

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

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

小结

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

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

MVVM框架如何解析双向绑定

JS运动特效

JS中链式运动(详细教程)

Das obige ist der detaillierte Inhalt vonSo implementieren Sie bind in Javascript. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
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
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!