В JavaScript оператор this может относиться к разным вещам в разных сценариях.
Обычно в методе внутри «объекта» JavaScript он ссылается на текущий объект.
Но при использовании в качестве обратного вызова он становится ссылкой на вызывающий объект.
Я обнаружил, что это вызывает проблемы в коде, потому что, если вы используете метод в «объекте» JavaScript в качестве функции обратного вызова, вы не можете определить, относится ли «this» к текущему «объекту» или «this» относится к вызывающий объект.
Может ли кто-нибудь прояснить использование и лучшие практики относительно того, как обойти эту проблему?
function TestObject() {
TestObject.prototype.firstMethod = function(){
this.callback();
YAHOO.util.Connect.asyncRequest(method, uri, callBack);
}
TestObject.prototype.callBack = function(o){
// do something with "this"
//when method is called directly, "this" resolves to the current object
//when invoked by the asyncRequest callback, "this" is not the current object
//what design patterns can make this consistent?
this.secondMethod();
}
TestObject.prototype.secondMethod = function() {
alert('test');
}
}



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я считаю, что это может быть связано с тем, как идея [закрытия] (http://en.wikipedia.org/wiki/Closure_(computer_science) работает в Javascript.
Я только начинаю разбираться с закрытием. Прочтите связанную статью в Википедии.
Вот другая статья с дополнительной информацией.
Кто-нибудь может это подтвердить?
Как только методы обратного вызова вызываются из другого контекста, я обычно использую то, что я вызываю контекстом обратного вызова:
var ctx = function CallbackContext()
{
_callbackSender
...
}
function DoCallback(_sender, delegate, callbackFunc)
{
ctx = _callbackSender = _sender;
delegate();
}
function TestObject()
{
test = function()
{
DoCallback(otherFunc, callbackHandler);
}
callbackHandler = function()
{
ctx._callbackSender;
//or this = ctx._callbacjHandler;
}
}
В JavaScript this всегда относится к объекту, вызывающему выполняемую функцию. Таким образом, если функция используется в качестве обработчика событий, this будет ссылаться на узел, который инициировал событие. Но если у вас есть объект и вы вызываете на нем функцию, например:
myObject.myFunction();
Тогда this внутри myFunction будет ссылаться на myObject. Имеет ли это смысл?
Чтобы обойти это, вам нужно использовать закрытие. Вы можете изменить свой код следующим образом:
function TestObject() {
TestObject.prototype.firstMethod = function(){
this.callback();
YAHOO.util.Connect.asyncRequest(method, uri, callBack);
}
var that = this;
TestObject.prototype.callBack = function(o){
that.secondMethod();
}
TestObject.prototype.secondMethod = function() {
alert('test');
}
}
ссылка на действительно хорошую презентацию закрытия в javascript: tinyurl.com/5mrpjv
Имейте в виду, что утверждение «В JavaScript« this »всегда относится к объекту, вызывающему выполняемую функцию» технически неверно. Это может быть четыре разных контекста, в зависимости от того, как вызывается функция.
Разве прототипы функций не должны быть вынесены за пределы конструктора? Или замените TestObject.prototype на that.
this соответствует контексту для вызова функции. Для функций, не вызываемых как часть объекта (без оператора .), это глобальный контекст (window на веб-страницах). Для функций, вызываемых как методы объекта (с помощью оператора.), Это объект.
Но вы можете сделать все, что захотите. Все функции имеют методы .call () и .apply (), которые можно использовать для их вызова с настраиваемым контекстом. Итак, если я настроил объект Чили так:
var Chile = { name: 'booga', stuff: function() { console.info(this.name); } };
... и вызовите Chile.stuff (), это даст очевидный результат:
booga
Но если захочу, могу взять и действительно облажался с этим:
Chile.stuff.apply({ name: 'supercalifragilistic' });
На самом деле это очень полезно ...
Вы также можете использовать Function.Apply (thisArg, argsArray) ... Где thisArg определяет значение это внутри вашей функции ... второй параметр - это необязательный массив аргументов, который вы также можете передать своей функции.
Если вы не планируете использовать второй аргумент, не передавайте ему ничего. Internet Explorer выдаст вам ошибку TypeError, если вы передадите ноль (или что-то еще, кроме массива) во второй аргумент function.apply () ...
В приведенном вами примере кода это будет выглядеть примерно так:
YAHOO.util.Connect.asyncRequest(method, uri, callBack.Apply(this));
Быстрый совет по передовым методам, прежде чем я начну болтать о волшебной переменной это. Если вам нужно объектно-ориентированное программирование (ООП) в Javascript, которое точно отражает более традиционные / классические шаблоны наследования, выберите фреймворк, изучите его причуды и не пытайтесь сообразить. Если вы хотите стать умнее, изучите javascript как функциональный язык и не думайте о таких вещах, как классы.
Это поднимает одну из самых важных вещей, о которых нужно помнить о Javascript, и повторять про себя, когда это не имеет смысла. В Javascript нет классов. Если что-то похоже на класс, это хитрый трюк. В Javascript есть объекты (насмешливые кавычки не нужны) и функции. (это не на 100% точно, функции - это просто объекты, но иногда может быть полезно думать о них как о отдельных вещах)
Переменная это прикреплена к функциям. Каждый раз, когда вы вызываете функцию, это присваивается определенное значение, в зависимости от того, как вы вызываете функцию. Это часто называют шаблоном вызова.
Есть четыре способа вызывать функции в javascript. Вы можете вызвать функцию как метод, как функция, как конструктор и с подать заявление.
Метод - это функция, прикрепленная к объекту.
var foo = {};
foo.someMethod = function(){
alert(this);
}
При вызове в качестве метода это будет привязан к объекту, частью которого является функция / метод. В этом примере это будет связано с foo.
Если у вас есть автономная функция, переменная это будет привязана к «глобальному» объекту, почти всегда к объекту окно в контексте браузера.
var foo = function(){
alert(this);
}
foo();
Это может быть то, что вас сбивает с толку, но не расстраивайтесь. Многие считают это плохим дизайнерским решением. Поскольку обратный вызов вызывается как функция, а не как метод, именно поэтому вы видите то, что кажется непоследовательным поведением.
Многие люди решают проблему, делая что-то вроде этого
var foo = {};
foo.someMethod = function (){
var that=this;
function bar(){
alert(that);
}
}
Вы определяете переменную который, которая указывает на это. Закрытие (это отдельная тема) поддерживает that, поэтому, если вы вызываете bar в качестве обратного вызова, он все равно будет иметь ссылку.
Вы также можете вызвать функцию как конструктор. В соответствии с используемым вами соглашением об именах (TestObject) это также может быть то, что вы делаете, и это то, что вас сбивает.
Вы вызываете функцию как конструктор с ключевым словом new.
function Foo(){
this.confusing = 'hell yeah';
}
var myObject = new Foo();
При вызове в качестве конструктора будет создан новый объект, и это будет привязан к этому объекту. Опять же, если у вас есть внутренние функции и они используются как обратные вызовы, вы будете вызывать их как функции, а это будет привязан к глобальному объекту. Используйте этот трюк / шаблон var that = this;.
Некоторые люди думают, что ключевое слово constructor / new было костью, брошенной Java / традиционным программистам ООП как способ создания чего-то похожего на классы.
Наконец, у каждой функции есть метод (да, в Javascript функции являются объектами) с именем apply. Apply позволяет определить, каким будет значение это, а также позволяет передавать массив аргументов. Вот бесполезный пример.
function foo(a,b){
alert(a);
alert(b);
alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);
Если вы используете Prototype, вы можете использовать связывать() и bindAsEventListener (), чтобы обойти эту проблему.
Если вы используете фреймворк javascript, может быть удобный способ справиться с этим. В Prototype, например, вы можете вызвать метод и привязать его к определенному объекту «this»:
var myObject = new TestObject();
myObject.firstMethod.bind(myObject);
Примечание: bind () возвращает функцию, поэтому вы также можете использовать ее для обратных вызовов до определения области видимости внутри вашего класса:
callBack.bind(this);
Отличное объяснение загадочного этого поведения на основе контекста здесь