Bind, Call и Apply в JavaScript

от автора

От переводчика:
Прошу принять во внимание, что приведенный здесь код, возможно, не является хорошими практиками. Тем не менее разбор сниппета из этого поста может оказаться еще одним поводом окунуться в функциональный JavaScript.

Недавно я увидел изящный JS сниппет в этом твите.

var bind = Function.prototype.call.bind(Function.prototype.bind); // #fp 

Взглянув на него, я смог догадаться, что он делает. Он превращает x.y(z) в y(x, z). Радуясь как ребенок, я показал его своим коллегам. Они спросили меня, что же тут происходит. Я открыл рот, чтобы объяснить и… не смог сказать ни слова. Я развернулся и ушел.

В большинстве случаев, взглянув на хорошо написанный код, можно легко догадаться, что он делает. Имея какой-то опыт в функциональном JavaScript, прочитав «Functional JavaScript» и «JavaScript Allongé» (обе замечательны), у меня не возникло особых трудностей в его прочтении. Но как объяснить этот код кому-то без опыта функционального программирования?

Я решил поэтапно разобраться на простых примерах что же тут происходит. Результат был таков:

// Создадим простой объект, чтобы использовать его в качестве контекста var context = { foo: "bar" };  // Функция, которая возвращает свойство «foo» контекста «this» function returnFoo () {   return this.foo; }  // Свойства не существует в текущей области видимости, поэтому undefined returnFoo(); // => undefined  // Но если мы свяжем эту функцию с контекстом var bound = returnFoo.bind(context);  // Свойство теперь в области видимости bound(); // => "bar"  // // Так работает Function.prototype.bind. // Так как returnFoo — это функция, она наследует прототип Function.prototype //  // Существует несколько способов связывания функции с контекстом // Call и apply позволяют вам вызывать функцию с нужным контекстом returnFoo.call(context); // => bar returnFoo.apply(context); // => bar  // Так же можно вложить функцию в объект context.returnFoo = returnFoo; context.returnFoo(); // => bar  // // Теперь давайте немного усложним //  // В Array.prototype есть замечательный метод slice. // При вызове на массиве он возвращает копию массива // от начального индекса до конечного (исключительно) [1,2,3].slice(0,1); // => [1]  // Мы берем slice и присваиваем его локальной переменной var slice = Array.prototype.slice;  // slice теперь оторван от контекста. Из-за того, что Array.prototype.slice // работает с данным ему контекстом «this», метод больше не работает slice(0, 1); // => TypeError: can't convert undefined to object slice([1,2,3], 0, 1); // => TypeError: ...  // Но мы можем использовать apply и call, они позволяют нам передавать нужный контекст slice.call([1,2,3], 0, 1); // => [1]  // Apply работает как call, но принимает аргументы в виде массива slice.apply([1,2,3], [0,1]); // => [1]  // Немного надоедает использовать .call каждый раз. Может воспользоваться bind? // Точно! Давайте привяжем функцию call к контексту slice.  slice = Function.prototype.call.bind(Array.prototype.slice);  // Теперь slice использует первый аргумент в качестве контекста slice([1,2,3], 0, 1); // => [1]  // // Неплохо, правда? Но у меня осталась еще кое-что. //  // Давайте проделаем с самим bind то же, // что мы делали со slice var bind = Function.prototype.call.bind(Function.prototype.bind);  // Обдумайте то, что мы только что сделали. // Что происходит? Мы оборачиваем call, возвращая функцию, которая принимает функцию и контекст // и возвращает связанную с контекстом функцию.  // Вернемся к нашему первоначальному примеру var context = { foo: "bar" }; function returnFoo () {   return this.foo; }  // И к нашему новому bind var amazing = bind(returnFoo, context); amazing(); // => bar  // Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind 

ссылка на оригинал статьи http://habrahabr.ru/post/199456/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *