Chrome 59更新:正如我在下面的答案中預測的那樣,使用新的優化編譯器,bind不再會變慢。以下是具體細節的代碼:https://codereview.chromium.org/2916063002/
大多數情況下並不重要。
除非你正在創建一個應用程序,其中.bind
是瓶頸我不會打擾。在大多數情況下,可讀性比純粹的性能要重要得多。我認爲使用本機.bind
通常會提供更易讀和可維護的代碼 - 這是一個很大的優勢。
但是,是的,當它的事項 - .bind
較慢
是,.bind
相當比一個封閉慢 - 至少在Chrome中,至少它在v8
實施的電流的方式。我個人不得不在Node.JS中多次切換性能問題(更一般的情況是,在性能密集的情況下,閉包速度很慢)。
爲什麼?因爲.bind
算法比使用其他函數包裝函數並使用.call
或.apply
複雜得多。 (有趣的是,它還返回一個函數,toString設置爲[native函數])。
從規範的角度以及從實現的角度來看,有兩種方法來看待這個問題。我們來觀察兩者。
- 我們的目標是這個值。
- 如果IsCallable(Target)爲false,則引發TypeError異常。
- 設A是按順序在thisArg(arg1,arg2等)之後提供的所有參數值的新(可能爲空)內部列表。
...
(21.調用F與論據 「論據」 的[[DefineOwnProperty]]內部方法,PropertyDescriptor的{[[獲取]:運動員,[設置]:運動員,[可枚舉]:假的,[配置]:。假},假
(22返回F.
似乎很複雜,不僅僅是一個包裝多很多
讓我們來看看FunctionBind
在V8(Chrome的JavaScript引擎)的源代碼:
function FunctionBind(this_arg) { // Length is 1.
if (!IS_SPEC_FUNCTION(this)) {
throw new $TypeError('Bind must be called on a function');
}
var boundFunction = function() {
// Poison .arguments and .caller, but is otherwise not detectable.
"use strict";
// This function must not use any object literals (Object, Array, RegExp),
// since the literals-array is being used to store the bound data.
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") {
// Function or FunctionProxy.
var old_length = this.length;
// FunctionProxies might provide a non-UInt32 value. If so, ignore it.
if ((typeof old_length === "number") &&
((old_length >>> 0) === old_length)) {
var argc = %_ArgumentsLength();
if (argc > 0) argc--; // Don't count the thisArg as parameter.
new_length = old_length - argc;
if (new_length < 0) new_length = 0;
}
}
// This runtime function finds any remaining arguments on the stack,
// so we don't pass the arguments object.
var result = %FunctionBindArguments(boundFunction, this,
this_arg, new_length);
// We already have caller and arguments properties on functions,
// which are non-configurable. It therefore makes no sence to
// try to redefine these as defined by the spec. The spec says
// that bind should make these throw a TypeError if get or set
// is called and make them non-enumerable and non-configurable.
// To be consistent with our normal functions we leave this as it is.
// TODO(lrn): Do set these to be thrower.
return result;
這裏,我們可以在執行看到一堆昂貴的東西。即%_IsConstructCall()
。這當然需要遵守規範 - 但它在很多情況下也比簡單的包裝要慢。
在另一方面,呼籲.bind
也略有不同,使用Function.prototype.bind創建的規範說明「函數對象沒有prototype屬性或[編號],[FormalParameters] ,[[Scope]]內部屬性「
」應該避免綁定「---除非您每千頁時間做一頁 - 您不應該關心它。 – zerkms
從小塊中組裝異步複雜任務可能需要與nodejs完全相同的東西,因爲回調需要以某種方式對齊。 – Paul
我想這是因爲瀏覽器沒有盡最大努力來優化它。請參閱Mozilla的代碼(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility)以手動實現它。瀏覽器只是在內部做這些事情,這比快速關閉更有效。 – Dave