В Javascript, почему оператор "this" несовместим?

В 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');
            }
        }

Ответ 1

В JavaScript "this" всегда ссылается на объект, вызывающий выполняемую функцию. Поэтому, если функция используется в качестве обработчика событий, "this" будет ссылаться на node, который запустил событие. Но если у вас есть объект и вызывается функция на нем, например:

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');
        }
    }

Ответ 2

Быстрый совет по передовым практикам, прежде чем я буду болтать о магии этой переменной. Если вы хотите ООП в Javascript, который близко отражает более традиционные/классические схемы наследования, выберите структуру, узнайте ее причуды и не пытайтесь стать умнее. Если вы хотите стать умным, изучите javascript как функциональный язык и избегайте думать о вещах, подобных классам.

Что вызывает одну из самых важных вещей, чтобы иметь в виду Javascript и повторять для себя, когда это не имеет смысла. Javascript не имеет классов. Если что-то похоже на класс, это умный трюк. Javascript имеет объекты (не нужны высмеивающие кавычки) и функции. (что не на 100% точнее, функции - это просто объекты, но иногда это может быть полезно рассматривать их как отдельные вещи).

Эта переменная привязана к функциям. Всякий раз, когда вы вызываете функцию, ей присваивается определенное значение, в зависимости от того, как вы вызываете функцию. Это часто называют шаблоном вызова.

Существует четыре способа вызова функций в javascript. Вы можете вызвать функцию как метод, как функцию, как конструктор и применить.

В качестве метода

Метод - это функция, привязанная к объекту

var foo = {};
foo.someMethod = function(){
    alert(this);
}

При вызове в качестве метода это будет связано с объектом, частью которого является функция /method. В этом примере это будет связано с foo.

Как функция

Если у вас есть автономная функция, эта переменная будет привязана к "глобальному" объекту, почти всегда к объекту окна в контексте браузера.

 var foo = function(){
    alert(this);
 }
 foo();

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

Многие люди сталкиваются с проблемой, выполняя что-то вроде, um, this

var foo = {};
foo.someMethod = function (){
    var that=this;
    function bar(){
        alert(that);
    }
}

Вы определяете переменную, которая указывает на это. Закрытие (тема все, что у него есть) поддерживает это, поэтому, если вы вызываете бар в качестве обратного вызова, он все еще имеет ссылку.

Как конструктор

Вы также можете вызвать функцию как конструктор. Основываясь на соглашении об именах, которое вы используете (TestObject), это также может быть тем, что вы делаете, и это то, что отключает вас.

Вы вызываете функцию как конструктор с ключевым словом new.

function Foo(){
    this.confusing = 'hell yeah';
}
var myObject = new Foo();

При вызове в качестве конструктора будет создан новый объект, и это будет связано с этим объектом. Опять же, если у вас есть внутренние функции и они используются как обратные вызовы, вы будете вызывать их как функции, и это будет связано с глобальным объектом. Используйте этот трюк/шаблон var that = this;.

Некоторые люди считают, что ключевое слово constructor/new было брошено в Java/традиционные программисты OOP, чтобы создать нечто похожее на классы.

С помощью метода Apply.

Наконец, каждая функция имеет метод (да, функции - объекты в Javascript) с именем apply. Применить позволяет определить, что это будет за значение, а также позволяет передавать массив аргументов. Здесь бесполезный пример.

function foo(a,b){
    alert(a);
    alert(b);
    alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);

Ответ 3

this соответствует контексту для вызова функции. Для функций, которые не называются частью объекта (оператор .), это глобальный контекст (window на веб-страницах). Для функций, называемых объектными методами (через оператор.), Это объект.

Но вы можете сделать все, что захотите. Все функции имеют методы .call() и .apply(), которые могут использоваться для вызова их с помощью настраиваемого контекста. Поэтому, если я создал объект Чили так:

var Chile = { name: 'booga', stuff: function() { console.log(this.name); } };

... и вызовите файл Chile.stuff(), это даст очевидный результат:

booga

Но если я хочу, я могу взять и действительно вкрутить его:

Chile.stuff.apply({ name: 'supercalifragilistic' });

Это действительно полезно...

Ответ 4

Если вы используете фреймворк javascript, может быть удобный метод для этого. Например, в Prototype вы можете вызвать метод и объединить его в конкретный объект "this":

var myObject = new TestObject();
myObject.firstMethod.bind(myObject);

Примечание. Функция bind() возвращает функцию, поэтому вы также можете использовать ее для предварительного просмотра обратных вызовов внутри вашего класса:

callBack.bind(this);

http://www.prototypejs.org/api/function/bind

Ответ 5

Я считаю, что это может быть связано с тем, как в Javascript работает идея [закрытия] (http://en.wikipedia.org/wiki/Closure_(computer_science).

Я просто собираюсь с закрытием себя. Прочитайте связанную статью в википедии.

Здесь другая статья с дополнительной информацией.

Кто-нибудь может подтвердить это?

Ответ 6

Как только методы обратного вызова вызывают из другого контекста, я обычно использую что-то, что я вызываю контекст обратного вызова:

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;
}
}

Ответ 7

Вы также можете использовать Function.Apply(thisArg, argsArray)... Где thisArg определяет значение этого внутри вашей функции... Второй параметр - это необязательный массив аргументов, который вы также можете передать своей функции.

Если вы не планируете использовать второй аргумент, не передавайте ему ничего. Internet Explorer будет бросать вам TypeError, если вы передадите null (или что-то, что не является массивом) в function.apply() второй аргумент...

Код примера, который вы ему дали, будет выглядеть примерно так:

YAHOO.util.Connect.asyncRequest(method, uri, callBack.Apply(this));

Ответ 8

Если вы используете Prototype, вы можете использовать bind() и bindAsEventListener(), чтобы обойти эту проблему.