Почему в 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');
            }
        }

Отличное объяснение загадочного этого поведения на основе контекста здесь

RBT 23.10.2017 13:59
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
27
1
10 259
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Я считаю, что это может быть связано с тем, как идея [закрытия] (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

Jared 17.09.2008 08:58

Имейте в виду, что утверждение «В JavaScript« this »всегда относится к объекту, вызывающему выполняемую функцию» технически неверно. Это может быть четыре разных контекста, в зависимости от того, как вызывается функция.

Alan Storm 17.09.2008 19:29
stackoverflow.com/questions/80084/…
Alan Storm 18.09.2008 17:21

Разве прототипы функций не должны быть вынесены за пределы конструктора? Или замените TestObject.prototype на that.

Thai 02.01.2011 06:01

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 / традиционным программистам ООП как способ создания чего-то похожего на классы.

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

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

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

Возможно, стоит добавить: есть также метод call, который работает как apply, за исключением того, что аргументы не передаются в виде массива.

eyelidlessness 28.09.2009 12:38

@Alan, отличный ответ, но стоит также упомянуть bind.

João Pimentel Ferreira 15.11.2018 22:37

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

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

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

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

callBack.bind(this);

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

Другие вопросы по теме