Довольно часто во многих статьях я вижу, как люди захватывают контекст this для использования в анонимной функции и удивляюсь — то, что уже стало стандартом — просто ужасная практика, которая противоречит всем канонам программирования. Вам знакома такая запись?
Может вам тоже стоит переосмыслить этот аспект?
Итак, пример:
Другой пример (jQuery):
Что вы про них скажете?
Я считаю, что в названиях переменных self и $this (а также that, _this, t) кроется зло. Зло кроется по двум причинам.
Первая — названия совершенно не несут смысловой нагрузки. Таким же чудом мы могли бы использовать, скажем
Вторая — иногда контексты могут множиться и пересекаться и тогда настаёт путаница. Например, когда функции у нас вложены одна в другую:
Всегда надо давать вменяемые имена переменным. Это правило относится не к контексту в JS, а к программированию в целом, но во время захвата контекста все об этом забывают. Например:
Это решит проблему со вложенными функциями.
Но чаще всего такое разнообразие контекстов не требуется. Потому давайте посмотрим на другой способ:
Эту идею я почерпнул из фреймворка MooTools и считаю её замечательной. Немножко расширим прототип Function
Теперь мы можем не терять контекст в течении всей работы. Если нам нужен только внешний контекст — мы прямо это указываем и код становится значительно прозрачнее:
Часто бывает, что для того, чтобы работать с методом объекта приходится городить очень некрасивую конструкцию. Например (пример на MooTools):
мы не можем передать в метод get просто ссылку на метод parse:
потому что тогда потеряется контекст метода. метод bind поможет нам в этом и мы видим, насколько яснее стал этот код:
Небольшой кусок кода из карточной игры Bridge на LibCanvas, демонстрирующий использование bind.
Суть ассинхронности в том, что ИИ не должен совершать никаких действий, пока летит карта.
Например, он берет карту из колоды, но может её туда положить только после того, как карта долетит, иначе будет неприятный для игрока эффект.
vl.vg/28.01.2010/tooltip-jquery
blog.kron0s.com/javascript-programming-patterns_2
habrahabr.ru/blogs/jquery/52185
var self = this;
Может вам тоже стоит переосмыслить этот аспект?
Итак, пример:
var self = this;
asyncFunc(function () {
self.callMethod();
});
Другой пример (jQuery):
$('input').bind('keydown', function () {
var $this = $(this);
$this.css({
background : $this.val()
});
});
Что вы про них скажете?
Ненависть
Я считаю, что в названиях переменных self и $this (а также that, _this, t) кроется зло. Зло кроется по двум причинам.
Первая — названия совершенно не несут смысловой нагрузки. Таким же чудом мы могли бы использовать, скажем
var killmeplz = this;
Вторая — иногда контексты могут множиться и пересекаться и тогда настаёт путаница. Например, когда функции у нас вложены одна в другую:
var self = this;
asyncFunc(function () {
var self2 = this; // wtf ?!!
setTimeout(function () {
self.callMethod(self2);
}, 200);
});
Решение первое — правильно именуем переменные
Всегда надо давать вменяемые имена переменным. Это правило относится не к контексту в JS, а к программированию в целом, но во время захвата контекста все об этом забывают. Например:
$('input').bind('keydown', function () {
var $colorInput = $(this);
$colorInput.css({
background : $colorInput.val()
});
});
Это решит проблему со вложенными функциями.
$block = $(this);
$('button').each(function () {
var $button = $(this);
$.each(users, function () {
var user = this;
$block.append(user.init($button));
});
});
Но чаще всего такое разнообразие контекстов не требуется. Потому давайте посмотрим на другой способ:
Принудительное задание контекста функции
Эту идею я почерпнул из фреймворка MooTools и считаю её замечательной. Немножко расширим прототип Function
Function.prototype.bind = function (scope) {
var fn = this;
return function () {
return fn.apply(scope, arguments);
};
};
Теперь мы можем не терять контекст в течении всей работы. Если нам нужен только внешний контекст — мы прямо это указываем и код становится значительно прозрачнее:
asyncFunc(function () {
this.callMethod();
}.bind(this));
Другие возможности работы с .bind
Часто бывает, что для того, чтобы работать с методом объекта приходится городить очень некрасивую конструкцию. Например (пример на MooTools):
var Analizer = new Class({
initialize : function (name) {
this.dataRouter = new DataRouter[name]();
},
start : function () {
var analizer = this;
this.dataRouter.get(function (data) {
analizer.parse(data);
});
},
parse : function (data) {
// parsing data, using this.privateMethods
}
});
мы не можем передать в метод get просто ссылку на метод parse:
dataGetter.get(analizer.parse);
потому что тогда потеряется контекст метода. метод bind поможет нам в этом и мы видим, насколько яснее стал этот код:
var Analizer = new Class({
initialize : function (name) {
this.dataRouter = new DataRouter[name]();
},
start : function () {
this.dataRouter.get(
this.parse.bind(this)
);
},
parse : function (data) {
// parsing data, using this.privateMethods
}
});
Небольшой кусок кода из карточной игры Bridge на LibCanvas, демонстрирующий использование bind.
Суть ассинхронности в том, что ИИ не должен совершать никаких действий, пока летит карта.
Например, он берет карту из колоды, но может её туда положить только после того, как карта долетит, иначе будет неприятный для игрока эффект.
Bridge.AI = new Class({
// ..
putCardSmart : function (card) {
this.putCard( card,
// Этот метод вызовется только когда карта долетит, но он сохранит контекст.
this.finishSmart.bind(this)
);
},
getCardSmart : function () {
this.getCard(function (card) {
this.canPutCard(card) ?
this.putCardSmart(card) :
this.finishSmart();
}.bind(this)); // Мы захватываем контекст.
},
finishSmart : function () {
this.canFinishMove() ?
this.finishMove() :
this.movement();
}
// ..
});
Примеры вредных советов
vl.vg/28.01.2010/tooltip-jquery
blog.kron0s.com/javascript-programming-patterns_2
habrahabr.ru/blogs/jquery/52185