Будет ли функция Function.prototype.bind() всегда медленной?
Я пишу библиотеку с открытым исходным кодом javascript, и я сильно использую метод .bind()
, потому что у меня есть идея, что объектно-ориентированный код выглядит более понятным тогда. (спорный, хотя)
Пример
A1:
var that = this;
setTimeout(function () {
that.method();
}, 0);
против
B1
setTimeout(this.method.bind(this), 0);
Или более практичная часть кода
A2:
remoteDataSource.getData(function (a, b, c, d) {
obj.dataGetter(a, b, c, d);
})
vs B2:
remoteDataSource.getData(obj/* or prototype */.dataGetter.bind(obj));
Я использую non-native bind
для старых браузеров, и все прошло идеально, пока я не открыл тест jsperf для привязки.
Похоже, что код с использованием bind
в 100 раз медленнее. Теперь, прежде чем переписывать всю мою библиотеку, у меня есть вопрос для тех, кто знаком с движками javascript:
Есть ли вероятность того, что, будучи новой функцией, bind
будет оптимизирована
скоро, или нет шансов из-за ограничений архитектуры JavaScript?
Ответы
Ответ 1
Прежде всего, зафиксирован jsperf http://jsperf.com/bind-vs-emulate/13.
= Вы не должны воссоздавать статические функции внутри эталона. Это нереально, потому что в реальном коде статические функции создаются только один раз.
Вы можете видеть, что шаблон var self = this
все еще на 60% быстрее. Но для этого требуется определение функции, где вы можете связываться из любой точки и, следовательно, иметь лучшую ремонтопригодность.
Ну нет, встроенная семантика связывания смехотворно запутана.
Когда я связываюсь, я просто хочу:
function bind(fn, ctx) {
return function bound() {
return fn.apply(ctx, arguments);
};
}
Если бы я хотел предварительно применить аргументы или использовать черную магию глубокого конструктора, мне бы хотелось, чтобы для этого была совершенно другая функция. Я понятия не имею, почему это было включено в bind.
<rant> Btw, та же проблема связана с почти чем-либо, введенным в ES5, наказывая общий случай, заставляя реализации обрабатывать некоторый теоретический краевой случай, который вряд ли имеет отношение к кому-либо на практике. Следующая языковая версия продолжается по тому же пути. </rant>
Эмулированное связывание даже не пытается эмулировать привязку вообще. И даже если вы попытаетесь подражать ему, вы не сможете
сделать это полностью, чтобы просто нечестно.
Таким образом, при всем остальном, равном *, встроенное связывание не может быть быстрее, чем обычное связывание, которое просто связывается.
* В JITs код пользователя не имеет существенного недостатка для встроенного кода. Фактически оба SM и V8 реализуют множество встроенных модулей
в Javascript.
Ответ 2
В настоящее время, в конце 2013 года, наилучшим решением будет реализация рукопашных версий Function.prototype.bind
и либо переопределить собственный "поставщик" (не являющийся по-настоящему родным), либо собственный код javascript, либо используйте свой собственный myBind
.
Function.prototype.apply
выполняется довольно быстро, а нарезка, согласование и сдвиг массива выполняется медленно, поэтому лучше использовать apply
вместо bind
или минимизировать манипуляции с аргументами в функции myBind
. Это оставит вас без возможности предварительной настройки параметров.
Поэтому я не вижу необходимости переписывать все обратно на закрытие (уродливый var that = this;
). Пусть функциональный характер javascript победит.
Более подробные и рабочие примеры кода здесь:
http://jsperf.com/function-bind-performance/4
http://jsperf.com/function-bind-performance/5
http://jsperf.com/bind-vs-emulate/4.. 10.
Подведение итогов: обходные решения не так уж плохи. Используйте их храбро. Если вы используете не полностью признанный bind
, не заходите слишком далеко от оригинальной концепции, так как я не нашел причин, которые он еще не реализовал в C. Это вопрос времени.