Chrome 59-Update: Wie ich in der folgenden Antwort vorausgesagt habe, ist die Bindung mit dem neuen optimierenden Compiler nicht mehr langsamer. Hier ist der Code mit Details: https://codereview.chromium.org/2916063002/
Meistens spielt es keine Rolle.
Wenn Sie keine Anwendung erstellen, in .bind
der der Engpass liegt, würde ich mich nicht darum kümmern. Die Lesbarkeit ist in den meisten Fällen viel wichtiger als die reine Leistung. Ich denke, dass die Verwendung von Native .bind
normalerweise besser lesbaren und wartbaren Code bietet - was ein großes Plus ist.
Aber ja, wenn es darauf ankommt - .bind
ist langsamer
Ja, .bind
ist erheblich langsamer als ein Abschluss - zumindest in Chrome, zumindest in der aktuellen Implementierung v8
. Ich persönlich musste Node.JS manchmal wegen Leistungsproblemen einschalten (im Allgemeinen sind Schließungen in leistungsintensiven Situationen etwas langsam).
Warum? Weil der .bind
Algorithmus viel komplizierter ist als das Umschließen einer Funktion mit einer anderen Funktion und das Verwenden von .call
oder .apply
. (Unterhaltsame Tatsache, es gibt auch eine Funktion zurück, bei der toString auf [native Funktion] gesetzt ist).
Es gibt zwei Möglichkeiten, dies unter dem Gesichtspunkt der Spezifikation und unter dem Gesichtspunkt der Implementierung zu betrachten. Beobachten wir beide.
- Sei Target dieser Wert.
- Wenn IsCallable (Target) false ist, lösen Sie eine TypeError-Ausnahme aus.
- Sei A eine neue (möglicherweise leere) interne Liste aller Argumentwerte, die nach thisArg (arg1, arg2 usw.) in der angegebenen Reihenfolge angegeben werden.
...
(21. Rufen Sie die interne Methode [[DefineOwnProperty]] von F mit den Argumenten "Argumente", PropertyDescriptor {[[Get]]: Thrower, [[Set]]: Thrower, [[Enumerable]]: false, [[Configurable] auf. ]: false} und false.
(22. Rückgabe F.
Scheint ziemlich kompliziert, viel mehr als nur ein Wrap.
Lassen Sie uns FunctionBind
den Quellcode der Version 8 (Chrome JavaScript Engine) einchecken:
function FunctionBind(this_arg) {
if (!IS_SPEC_FUNCTION(this)) {
throw new $TypeError('Bind must be called on a function');
}
var boundFunction = function () {
"use strict";
if (%_IsConstructCall()) {
return %NewObjectFromBound(boundFunction);
}
var bindings = %BoundFunctionGetBindings(boundFunction);
var argc = %_ArgumentsLength();
if (argc == 0) {
return %Apply(bindings[0], bindings[1], bindings, 2, bindings.length - 2);
}
if (bindings.length === 2) {
return %Apply(bindings[0], bindings[1], arguments, 0, argc);
}
var bound_argc = bindings.length - 2;
var argv = new InternalArray(bound_argc + argc);
for (var i = 0; i < bound_argc; i++) {
argv[i] = bindings[i + 2];
}
for (var j = 0; j < argc; j++) {
argv[i++] = %_Arguments(j);
}
return %Apply(bindings[0], bindings[1], argv, 0, bound_argc + argc);
};
%FunctionRemovePrototype(boundFunction);
var new_length = 0;
if (%_ClassOf(this) == "Function") {
var old_length = this.length;
if ((typeof old_length === "number") &&
((old_length >>> 0) === old_length)) {
var argc = %_ArgumentsLength();
if (argc > 0) argc--;
new_length = old_length - argc;
if (new_length < 0) new_length = 0;
}
}
var result = %FunctionBindArguments(boundFunction, this,
this_arg, new_length);
return result;
Wir können hier in der Implementierung eine Reihe teurer Dinge sehen. Nämlich %_IsConstructCall()
. Dies ist natürlich erforderlich, um die Spezifikation einzuhalten - aber es macht es in vielen Fällen auch langsamer als eine einfache Umhüllung.
In einem anderen Hinweis unterscheidet sich der Aufruf .bind
ebenfalls geringfügig. Die Spezifikationshinweise "Mit Function.prototype.bind erstellte Funktionsobjekte haben keine Prototyp-Eigenschaft oder die internen [[Code]], [[FormalParameters]] und [[Scope]] Eigenschaften"