Ожидание правильного вызывающего контекста (этого) в объекте JavaScript - PullRequest
4 голосов
/ 13 декабря 2010

Учтите это:

window.onload = function () {
    myObj.init();
};

var myObj = {
    init: function () {
        console.log("init: Let's call the callMe method...");

        //callMe is not defined...
        callMe();

        //Works fine!
        this.callMe();
    },

    callMe: function () {
        console.log('callMe');
    }
};

Так как функция init вызывается таким образом (myObj.init), я ожидаю, что this будет myObj в функции init. И если это так, то почему функция callMe не работает? Как я должен вызывать функцию callMe, не используя контекст this в теле инициализации? (На самом деле, слишком раздражает вызывать методы объекта, используя this снова и снова через функции. Так какой смысл иметь один объект?)

Я хотел бы знать, как я могу это исправить, чтобы метод callMe вызывался с использованием первого вызова в приведенном выше коде?

Ответы [ 2 ]

8 голосов
/ 13 декабря 2010

this никогда не подразумевается в JavaScript, как в некоторых других языках. Хотя есть способы сделать это, например, используя оператор with:

init: function () {
    console.log("init: Let's call the callMe method...");

    // Make `this` implicit (SEE BELOW, not recommended)
    with (this) {
        // Works
        callMe();
    }
},

... это вообще плохая идея . Дуглас Крокфорд, вероятно, написал одно из лучших описаний , почему это плохая идея, которую вы можете найти здесь . По сути, использование with делает почти невозможным сказать, что будет делать код (и замедляет код, если вы сделаете что-нибудь еще в этом with утверждении, которое не приходит из this объект).

Это не единственный способ, которым JavaScript this отличается от некоторых других языков. В JavaScript this полностью определяется как как функция называется , а не там, где функция определена . Когда вы делаете this.callMe() (или эквивалент this["callMe"](), или, конечно, foo.callMe() и т. Д.), Происходит две вещи: ссылка на функцию извлекается из свойства, и функция вызывается в специальный способ установить this в качестве объекта, из которого получено свойство. Если вы не вызываете функцию через свойство таким способом, то при вызове не устанавливается никакого конкретного значения this, и вы получаете значение по умолчанию (которое является глобальным объектом; window в браузерах). Это акт совершения вызова, который устанавливает, что this. Я подробно изучил это в нескольких статьях в моем блоге: здесь и здесь .

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

callMe.call({});

... вызовет функцию callMe с пустым объектом ({}) как this.

Так что, в основном, привыкайте к набору this. :-) Все еще полезно иметь свойства и методы, связанные с объектом, даже без синтаксического удобства (и путаницы!) Неявного this.

2 голосов
/ 13 декабря 2010

Вы также можете использовать шаблон модуля, который захватывает все закрытые переменные внутри замыкания, поэтому вы можете использовать их без this, , так как они находятся в той же области действия .Затем вы выбираете, какие методы / переменные вы хотите сделать общедоступными:

var myObj = (function () {
   var init = function () {
      callMe(); // This now works
   };

   var callMe = function () {
      ...
   };

   // Now choose your public methods (they can even be renamed):
   return {
      init: init, // Same name
      callMyName: callMe // Different name
   };
}) ();

Сейчас:

myObj.init(); // Works
myObj.callMyName(); // Works
myObj.callMe(); // Error
...