JavaScript bind.apply , wie geht das?

Hm, ich habe es so verstanden, dass nun bind durch apply aufgerufen wird und zwar mit handler als Parameter für this und dann als 2. Parameter das Array [myObject, 'these', 'are',...]

Ich verstehe nicht warum man hier bind so aufruft -> bind(handler, [myObject, 'these'....])

Bind weiss doch welche Funktion es binden soll, es holt ja bind auf dem handler durch handler.bind ?

edit:
Ah ok musste grad feststellen, dass es egal ist ob man handler.bind schreibt oder Function.prototype.bind

Diese 'umgekehrte' Logik begreif ich nicht. bind muss doch mit einem thisArg aufgerufen werden, gefolgt von n Argumenten. Wobei diese n Argumente ja nicht typisiert sind und man da irgendwas mitgeben kann?
Hier wird dann aber bind mit einer Funktion als erstes Argument aufgerufen? :freak:

Oder auch dieses Beispiel auf der MDN bind Seite:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Javascript:
function list() {
  return Array.prototype.slice.call(arguments);
}

const list1 = list(1, 2, 3);

Array.prototype.slice wird hier aufgerufen mit arguments was ja ein Object ist. Aber slice erwartet doch ein optionales begin und end?
 
Zuletzt bearbeitet:
Nein, der daraus folgende bind Aufruf ist bind(handler, myObject, 'these', ...). apply macht aus dem Array einzelne Parameter.

Wenn du handler.bind macht, ist der Aufruf von bind an handler gebunden (also bind hat intern handler als this, da du es direkt aufrufst). Wenn du handler.bind.apply Aufrufst, weiß bind nichts mehr von handler (bind wird ja nicht mal von dir aufgerufen). Stattdessen rufst du apply von bind auf, somit ist this innerhalb von apply bind. Und das this von dem durch apply aufgerufenem bind dürfte bind (also sich selbst) als this haben, da das das this innerhalb des aufrufendem apply ist. Also musst du apply sagen, dass es ein anderes this für bind setzen soll.

Das Beispiel mit Array.prototype.slice.call(arguments) ergibt bei [1, 2, 3] als arguments keinen Sinn. slice erfordert ein Array für this. Korrekt wäre hier list([1, 2, 3], 2, 3.
 
Ich verstehe nicht, warum man überhaupt so viel Zeit mit derart veralteter Syntax verschwendet. Wir sind mittlerweile im Jahre 2021 und ES12 ist gerade verabschiedet worden. Warum dann noch mit Syntax aus der Prä-ES6-Ära herumhantieren?
 
@SheepShaver
Das ist ein in den meisten Szenarien berechtigter Einwand. Es gibt jedoch Fälle, in denen bind/apply noch Sinn ergeben. Und zum lernen wie Javascript funktioniert ist es auch nicht verkehrt, das zu wissen.
 
Ich bezog mich vornehmlich auf den Rest der Code-Schnipsel. Bind ist auch ziemlich teuer, vor allem im Vergleich zu Arrow-Functions.
 
Kann man das irgendwo nachlesen, dass apply einzelne Parameter macht aus dem Array? Schliesslich wird ja bind direkt von apply mit dem Array aufgerufen, da ist doch nichts mehr dazwischen?

Und auch so verstehe ich es nicht, wenn handler als this fungiert, wie kann dann this beim eigentlich Funkionsaufruf dann myObject sein? myObject ist laut deiner Logik ja der 2. Parameter und nicht der erste.
Aber anscheinend wird der erste Parameter handler als die Funktion die es zu binden gilt genommen. Und der zweite Parameter dann als this? Und das verwirrt mich so sehr. Die Methodensignatur von bind sieht das this Argument als erstes und dann folgenden n values.

Das mit slice ist ein Beispiel von MDN und funktionert auch so. Ich verstehe aber nicht wieso. Warum kann man slice ein Array übergeben bzw. das arguments-object. Mir ist klar, dass man übergeben kann was man will und alles in arguments gesammelt wird. Aber wie kann slice dann damit umgehen? Ist slice so implementiert, dass es einfach noch schaut was alles in den arguments drin ist? Und dann findet ok, ich geb das einfach mal als Array zurück? Der Zweck von slice ist ja eigentlich das man nur einen gewissen Teil von einem Array zurückbekommt und nicht, dass man die Werte die man slice übergibt als Array zurückbekommt.

Mir will einfach diese Logik nicht in den Kopf, dass man die Methoden irgendwie 'umgekehrt' aufruft. Also anstatt myArray.slice(begin, end) dann Array.prototype.slice(myArray)

Ich bin irgendwie immer verwirrter :heul:
 
arguments ist zwar kein Array, aber array-like, hat also ein length Attribut und die einzelnen Elemente können über Index abgerufen werden. Es hat aber beispielsweise keine eigene slice Methode wie echte Arrays, daher der Umweg über die Function im Prototypen. Slice ohne begin und end macht eine shallow Copy.
Das Resultat ist dann übrigens ein echtes Array.
 
Ja, dessen bin ich mir bewusst. Ich verstehe aber nicht wie man slice.call nur mit arguments aufrufen kann. arguments würde als this fungieren. Und dann? Was macht slice intern mit diesem this? Das ist für mich undurchsichtig.
 
Ich versteh nicht, was du meinst. Slice macht eine flache Kopie. Entweder du rufst das über die slice Methode einer Array-Instanz auf oder du rufst die Funktion im Array Prototypen auf und übergibst das Array als Argument. Wobei slice eben nicht nur mit Arrays, sondern auch mit Array-likes zurechtkommt.

Array-likes laut API Doku:
array-like objects (objects with a lengthproperty and indexed elements)
 
Ah ok, jetzt hat glaubs Klick gemacht, man setzt mit call(arguments) das array-like als this Variable die dann intern von slice verwendet wird um dann das slicen vorzunehmen. Bei einer method-invocation ist ja this = das object auf dem die Funktion aufgerufen wurde.

Ist einfach so ungewohnt sowas zu sehen (für mich zumindest).
Ergänzung ()

Bagbag schrieb:
Nein, der daraus folgende bind Aufruf ist bind(handler, myObject, 'these', ...). apply macht aus dem Array einzelne Parameter.
Ja eben, dann ist handler = this und der Rest dann die Argumente. Warum soll hier jetzt das zweite Argument als this fungieren?
 
Zuletzt bearbeitet:
Da bin dann wohl auch ich durcheinander gekommen. Ist ja aber auch kein wunder :D

handler wird hierbei nicht als this-Argument (an Stelle 1 der Argumente) übergeben, sondern als this-Bindung genutzt. Das erste Argument, mit dem bind aufgerufen wird, ist myObject.

Also der effektive Aufruf, der aus handler.bind.apply(handler, [myObject].concat(args)) wird ist: handler.bind(myObject, ...args).

Das bei handler.bind.apply(handler, [myObject].concat(args)) handler ganz am Anfang steht ist völlig irrelevant. Da könnte genauso gut Function.prototype.bind.apply(handler, [myObject].concat(args)) stehen.
 
Ja das handler ganz vorne irrelevant ist habe ich ja schon irgendwo in einem früheren Post erkannt.

Und wie erklärst du , dass daraus handler.bind(myObject, ...args) wird?
Wird diese Logik irgendwo an offizieller Stelle wie z.B. MDN beschrieben?
Wo steht beschrieben, dass man wenn man bind direkt via Function.prototype.bind aufruft als erstes Argument die zu bindenden Funktion übergeben muss und das zweite Argument dann als this verwendet wird?

Das versteh ich einfach nicht und konnte mir bislang leider auch niemand erklären (oder ich habs nicht verstanden)
 
Du rufst bind ja nicht direkt auf, sondern apply. Das ist der entscheidende Unterschied. bind bekommt handler nie als Parameter.
 
Ja apply ruft bind auf, das ist mir bewusst. Aber auch wenn nicht direkt, wohin verschwindet 'handler' denn?
 
Vielleicht hilft dir das. Mir jedenfalls nicht, da ich jetzt verwirrt bin und nicht verstehe, warum bei setHtml.bind.apply(setHtml, [button, value]); nichts ausgegeben wird.

Javascript:
function setHtml(value) {
  console.log(this, value);
}
const button = 'the-button';
const value = 'hallo';

console.log('setHtml(value);');
setHtml(value);
console.log();

console.log('setHtml(button);');
setHtml(button);
console.log();

console.log('setHtml.call(button, value);');
setHtml.call(button, value);
console.log();

console.log('setHtml.bind(button)(value);');
setHtml.bind(button)(value);
console.log();

console.log('setHtml.apply(button, [value]);');
setHtml.apply(button, [value]);
console.log();

console.log('setHtml.bind.apply(setHtml, [value]);');
setHtml.bind.apply(setHtml, [value]);
console.log();

console.log('setHtml.bind.apply(setHtml, [button, value]);');
setHtml.bind.apply(setHtml, [button, value]);
console.log();

console.log('setHtml.bind.apply(button, [value]);');
setHtml.bind.apply(button, [value]);
console.log();

Ausgabe:
Code:
[LOG]: "setHtml(value);"
[LOG]: undefined,  "hallo"
[LOG]:
[LOG]: "setHtml(button);"
[LOG]: undefined,  "the-button"
[LOG]:
[LOG]: "setHtml.call(button, value);"
[LOG]: "the-button",  "hallo"
[LOG]:
[LOG]: "setHtml.bind(button)(value);"
[LOG]: "the-button",  "hallo"
[LOG]:
[LOG]: "setHtml.apply(button, [value]);"
[LOG]: "the-button",  "hallo"
[LOG]:
[LOG]: "setHtml.bind.apply(setHtml, [value]);"
[LOG]:
[LOG]: "setHtml.bind.apply(setHtml, [button, value]);"
[LOG]:
[LOG]: "setHtml.bind.apply(button, [value]);"
[ERR]: "Executed JavaScript Failed:"
[ERR]: Function.prototype.bind called on incompatible the-button


Edit: Nein, bin doch nicht verwirrt. Die müssen natürlich noch aufgerufen werden...

Javascript:
setHtml.bind.apply(setHtml, [value])(); // "hallo",  undefined
setHtml.bind.apply(setHtml, [button, value])(); // "the-button",  "hallo"
 
Zuletzt bearbeitet:
Das erklärt es für mich leider nicht, das ist einfach ein anderes Beispiel dafür mit anderem Naming...?
Wo steht geschrieben, dass man 'setHtml', also die Funktion die man binden möchte als erstes übergeben muss? Bei bind, call und apply ist ja jeweils der erste Parameter für das this gedacht, aber hier übergibt man eine Funktion und dann folgt das this?
 
Das steht da: bind und apply
Ergänzung ()

Hier noch ein Erklärungsversuch: TypeScript Playground
Oben links auf "Run" drücken.

Wenn dir auch das nicht weiterhilft, dann weiß ich jetzt echt nicht mehr, wie ich es dir noch erklären/zeigen könnte.
 
Zuletzt bearbeitet:
Die MDN Seiten kenne ich natürllich und wenn ich dort eine Erklärung gefunden hätte würde ich nicht hier fragen :D Ich kann dort aber nichts finden, das erklärt warum man bind zuerst die zu bindende Funktion übergeben muss. Für mich steht da immer noch:
bind(thisArg, arg1, ... , argN)
Sprich das erste Argument entspricht dem zu verwendenden this und nicht das zweite.
Ich sehe nirgends eine Erläuterung, dass wenn man Function.prototype.bind() aufruft man zuerst die zu bindenden Funktion mitgeben muss. Klar, es macht Sinn da JS sonst gar nicht weiss welche Funktion es zu binden gilt. Aber mir fehlt die Erläuterung dazu ala:
Function.prototype.bind(functionToBind, thisArg, arg1, arg2, argN)

Bin ich blind? Oder verstehe ich ein grundlegendes Pattern nicht?
 
Du verstehst offenbar nicht woher das implizite this kommt oder verwechselst this mit thisArg.

Bei myFn.bind() hat bind implizit myFun als this. Bei myFn.bind.apply() hat apply implizit bind als this und "applied" daher auf bind und nicht auf myFn, denn das ist hier in dem Aufruf quasi wie nicht existent.

Was du letzlich ja aber willst ist myFn.bind(), also dass der bind-Aufruf myFn als this hat. apply ruft bind auf. Und wie sagst du apply, was die "applied"-Function als this haben soll? Über das thisArg. Und das soll für bind myFn sein. Damit hat bind dann myFn als this und folglich bindet das (auf den ersten Parameter, der ja über die apply-Args kommt.

Ich hatte gehofft, dass du mit den Ausgaben in meinem Beispiel im TS Playground siehst, dass this etwas anderes als thisArg ist. Und was (vorallem bind) wann sieht. Und thisArg erst "nachträglich" zum this wird. Da siehst du in den wrappern ja auch, dass ich apply und bind immer auf this ausführe und nicht auf thisArg, wie man evtl. in dem fn.bind.apply() Konstrukt zuerst vermuten könnte.

Stell dir mal noch die Frage, woher bind bei myFn.bind(thisArg) weiß, dass es myFn binden soll - du übergibst es ja nicht als Parameter.
 
Zuletzt bearbeitet:
Ok, ich verstehe, dass man in diesem Fall myFn als this übergeben muss, da es kein Methoden-Aufruf ist bei dem das this = das links vom dot ist.

Aber ich ging davon aus, dass das thisArg das man übergibt intern dann auf this gesetzt wird, also sowas wie this = thisArg . Das thisArg ist doch dazu da um eben den default Wert von this zu überschreiben?
Für mich entspricht also thisArg = das this das in der Methode verwendet werden soll.
Für mich nach wie vor unklar ist, warum dann plötzlich das zweite Argument als this verwendet wird beim eigentlich Aufruf später.

Wenn du in deinem Beispiel die args in den beiden Wrappern logst, dann ist es im ApplyWrapper:
[Test: { "something": "something" }, "Max Mustermann"]
und im BindWrapper dann nur noch:
["Max Mustermann"]
Das verwirrt mich am meisten. Wo ist das Test: { "something": "something" } hin zwischen dem ApplyWrapper und dem BindWrapper? Die args werden doch einfach durchgereicht?

1633606331687.png


Aber ich gebe wohl bald auf, ich verstehe es einfach nicht🤯
 

Ähnliche Themen

Zurück
Oben