![](https://habrastorage.org/storage/7dde21f1/b830edb7/17db71e6/27d917ea.jpg)
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