Comments 42
Не понимаю какой смысл писать
var amazing = bind(returnFoo, context);
вместо
var amazing = returnFoo.bind(context);
в чем смысл и почему этому надо радоваться, как ребенок?
и не он один.
Как это рабоает — понятно. А вот что это дает — вообще осознаваемо.
Просто неочевидный пример. Вот более частый кейс:
var _toString = Function.prototype.call.bind(Object.prototype.toString);

_toString({});     // "[object Object]"
_toString(window); // "[object Window]"
_toString([]);     // "[object Array]"

Т.е. простой вызов функции _toString() используется вместо:
Object.prototype.toString.call( [] )

и
var _toString = Object.prototype.toString
_toString.call( [] )

что весьма удобнее
Действительно удобно, хотя обычно к результату _toString(...) еще .slice(8, -1) применяют. Поэтому проще написать такую функцию:
var getTypeString = function(obj) {
  return Object.prototype.toString.call(obj).slice(8, -1);
};

и дальше использовать её.
UFO landed and left these words here
И всё равно в какой-то момент я потерял контекст происходящего.
Не думаю, что стоит использовать такие трюки в обычном коде.
Внутри библиотеки — возможно, но в основном коде я бы не стал.
Буду опять перечитывать до полного понимания.
Именно поэтому я написал в начале поста дисклеймер.
Речь не о том, чтобы использовать такие приемы в продакшне. Для меня это была неплохая пища для размышлений, еще один повод подробнее изучить парадигму функционального программирования. Такие вещи неплохо контрастируют с бытовым повседневным программированием. Именно поэтому я решил перевести оригинальный пост и опубликовать его тут.
<режим зануды>bind не меняет состояние объекта, он его возвращает</режим зануды>
Этот bind давно и повсюду есть и используется:
jQuery: api.jquery.com/jQuery.proxy/
dojo: dojotoolkit.org/reference-guide/1.7/dojo/hitch.html

Эта штука чаще всего нужна когда ты хочешь подвесить функцию, юзающую контекст из this (метод класса) на событие, чтобы при обработке события этот же контекст остался. Задача похожа на то, что часто еще решают с помощью var that = this.

Это уже давно и повсеместно в продакшне.
es5-shim полностью реализует Function.prototype.bind и ряд других методов, в том числе фиксит страшный баг в IE8 с Array.prototype.splice
А в чем принципиальная разница их какой именно библиотеки использовать этот bind?
В том, что при использовании полифилов, библиотечная функция будет использоваться только в случае отсутствия такой нативной функции. Т.е. в вашем случае, библиотечная реализация bind (которая, к стати в es5-shim сделана лучше, чем в jQuery) будет использоваться только в IE8, а в остальных браузерах будет использоваться нативная функция.
Возможно я буду резковат, но «метод» — обычная перестановка. Практической пользы я не могу придумать, а вот трудности уже перед глазами.

ИМХО, это не изящный сниппет, а головная боль на стадии поддержки.
Если вы хотите писать в стиле фп, то вышеупомянутый код вполне себе претендует на таки снятие лишней головной боли
В Lua круче сделано, там 2 формата вызова методов
obj:func(param1, param3)
тогда в this попадет obj
а можно
obj.func(obj2, param1, param3)
тогда в this попадет obj2
Вообще ок сделано. Никогда не понимал зачем этот this, который по факту — просто неявный параметр функции, придают сакральный смысл.
Завсегда юзал для себя свой бинд вот такого вида. Работает ну точно так же как результат в статье, за исключение того что заведомо писался так, чтобы не изменять прототип Function (ну и ещё доп аргументы замыкает). Согласен с первым комментарием короче я.

function bind (context, method) {
	var prefix = Array.prototype.slice.call(arguments,2);
	return function(){
		return method.apply(context, prefix.concat(Array.prototype.slice.call(arguments)));
	}
}
Не точно выразился. Бинд в статье — часть прототипа, и был ей не всегда.
Код выше писал сам, но код простой очень, тут сложно никого не повторить.
Радует что вы не добавляли это в прототип функции как шим. Я бы все-таки называл функцию по другому, ведь так или иначе программист севший работать с вашим кодом ожидает что bind ведет себя по спецификации, так что я бы не спешил повсеместно это употреблять.
Ремарочка: неужели вы, садясь разбирать код другого программиста, действительно ждете что его функции будут работать по спецификации? Нет, ну правда?
Да, я именно этого и жду, ибо если я буду лазить в каждый подобный косяк смотря что там и как делают, я буду половину своего времени тратить на «чисто посмотреть а что же делает функция аналогичная спецификации». А это простите никому не интересно. И если функция работает как-то по особому я жду отражения этого в документации.
И по моему мои слова не есть открытие для нормальных проектов где работает не 1 человек и переодически вливаются новые
Ну вам повезло работать исключительно в нормальных проектах, иначе вы бы прочувствовали всю ошибочность и времезатратность подхода, который предписывает по умолчанию верить в ответвтенность предыдущего разработчика.
когда программист будет тратить 80% на понимание и исправление предыдущего кода при внесении любых правок у любого проекта встанет вопрос «сделать нормально». Всегда есть дока в общем то, если её нет то проект или не большой и хрень
Т.е. вы не используете встроенный в движок браузера и весьма оптимизированный Function.prototype.bind только из-за того, что когда-то не во всех браузерах был этот метод?
Ну во первых я, как и все, подвержен инерционности мышления, а во вторых вот в соседней статье удивлялись зачем экономить 100кб подключаемого кода библиотек, каналы же сейчас широкие, ну а я вот не парюсь по поводу скорости работы не самой часто-используемой функции.
На мой взгляд, перед шагом

// Немного надоедает использовать .call каждый раз. Может воспользоваться bind?
// Точно! Давайте привяжем функцию call к контексту slice.
slice = Function.prototype.call.bind(Array.prototype.slice);

неплохо бы добавить ещё один — эквивалентный переход от
// Но мы можем использовать apply и call, они позволяют нам передавать нужный контекст
slice.call([1,2,3], 0, 1); // => [1]

к
Function.prototype.call(slice, [1,2,3], 0, 1); // => [1]

Без него непонятно, что происходит при вызове bind().
Спасибо тебе, добрый человек. Без bind никак не мог решить задачу с addEventListener (и remove в перспективе). А с ним смог так:
var foo = {
    prop: 0,
    bar: function(){
        console.log(++this.prop);
    },
    init: function(){
        this.bar = this.bar.bind(this);
        document.addEventListener("click", this.bar);
    }
};
foo.init();
foo.bar();
Может быть вызов обработчика в init лучше оформить в виде классического callback-а, поскольку перезапись this.bar в данном случае смысла не имеет:

    init: function(){
        var callback = this.bar.bind(this);
        document.addEventListener("click", callback);
Любопытно, кстати, что следующий, казалось бы, эквивалентный вариант привязки контекста уже не работает, так как вызов bar с bind срабатывает уже внутри addEventListener в глобальном контексте:
    bar: (function(){
        console.log(++this.prop);
    }).bind(this),
    init: function(){
        document.addEventListener("click", this.bar);
    }
до меня долго доходило, почему мы в call биндим функцию, но потом понял, что для Array.prototype.slice это и есть функция к которой будет применяться call… по сути мы жестко привязываем call на эту функцию и возвращаем значение в переменную. получается для выражения: slice = Function.prototype.call.bind(Array.prototype.slice); на пальцах можно объяснить так: в переменную slice верни функцию call которая будет вызываться в контексте Array.prototype.slice (так как мы ее забиндили для функции call и теперь внутри функции call — this это Array.prototype.slice). Надеюсь может кому-то поможет в осознании того, что написал автор, хотя прочитав сейчас то, что написал засомневался =)
Only those users with full accounts are able to leave comments. Log in, please.