Замыкания Javascript и this

У меня проблема с созданным мной объектом, который выглядит примерно так:

var myObject = {

    AddChildRowEvents: function(row, p2) {
        if (document.attachEvent) {
            row.attachEvent('onclick', function(){this.DoSomething();});
        } else {
            row.addEventListener('click', function(){this.DoSomething();}, false);
        }
    },

    DoSomething: function() {
        this.SomethingElse(); //<-- Error here, object 'this' does not support this method.
    }
}

Проблема в том, что когда я нахожусь внутри функции «DoSomething», «this» не относится к «myObject», что я делаю не так?

1) глупый вопрос, а есть ли у вашего объекта метод SomethingElse? 2) попробуйте выполнить "function () {this.DoSomething (this);}" в закрытии, и пусть DoSomething принимает либо 0 аргументов, либо 1 аргумент - посмотрите, что произойдет, и опубликуйте свои результаты.

Claudiu 06.12.2008 10:19

В вашем комментарии говорится, что ошибка связана с вызовом SomethingElse, но разве это не связано с вызовом DoSomething?

user200783 13.11.2018 07:29

Замыкания на самом деле не имеют ничего общего с этим примером, значение, если this не исходит из замыкания здесь.

Andy 13.03.2019 09:57

Отвечает ли это на ваш вопрос? Как получить доступ к правильному `this` внутри обратного вызова?

Inigo 15.06.2020 20:05
Поведение ключевого слова "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) для оценки ваших знаний,...
38
4
27 131
5

Ответы 5

КОГДА функция вызывается, «this» относится к строке. Если вы хотите получить объект, вы можете сделать это примерно так: ]

AddChildRowEvents: function(row, p2) {
    var theObj = this;
    if (document.attachEvent) {
         row.attachEvent('onclick', function(){theObj.DoSomething();});
    } else {
         row.addEventListener('click', function(){theObj.DoSomething();}, false);
    }
},

Когда функция вызывается, она имеет доступ к переменной theOBj, которая была в области видимости, когда функция была определена.

мне потребовалось некоторое время, чтобы получить это - причина в том, что row.attachEvent прикрепляет событие к строке, и когда это событие срабатывает, строка является той, которая вызывает функцию (она, вероятно, делает что-то вроде f.apply (this, args )). я прав?

Claudiu 20.12.2008 11:40

Это обычная проблема с закрытием. Чтобы решить эту проблему, попробуйте что-то вроде этого:

var myObject = {    
    AddChildRowEvents: function(row, p2) { 
        var self = this;

        if (document.attachEvent) {            
             row.attachEvent('onclick', function(){this.DoSomething(self);});        
        } else {            
             row.addEventListener('click', function(){this.DoSomething(self);}, false);        
        }    
    },    

    DoSomething: function(self) {       
        self.SomethingElse(); 
    }
}

К вашему сведению, «self» - очень опасное имя переменной в контексте Javascript. В зависимости от вашей области действия вы можете фактически получить ссылку на объект document.window.

Matthemattics 05.09.2012 04:06

this всегда относится к внутренней функции, если у вас есть вложенные функции, вы должны создать другую переменную и указать ее на this.

var myObject = {
    AddChildRowEvents: function(row, p2) {
        var that = this;
        if (document.attachEvent) {
            row.attachEvent('onclick', function(){that.DoSomething();});
        } else {
            row.addEventListener('click', function(){that.DoSomething();}, false);
        }
    }
}

Вы уверены, что "that = this;" действителен в случае определения его внутри литерала объекта? @svinto

GnrlBzik 04.01.2013 21:27

@GnrlBzik: Хороший улов! Перед ним должен быть var.

svinto 21.05.2013 22:05

чувак, это объект, он ожидает пары ключ: значение. запустите этот код против jshint.com

GnrlBzik 23.05.2013 18:29

Похоже я очень устал что ли, извините за глупость.

svinto 23.05.2013 23:53

бывает :) я делаю это постоянно; )

GnrlBzik 27.07.2013 00:03

Проблема с вашим кодом здесь, в следующей строке кода вы определили анонимную функцию и передали ее в качестве обработчика события onClick в следующих строках:

    row.attachEvent('onclick', function(){this.DoSomething();});

и

    row.addEventListener('click', function(){this.DoSomething();}, false);

когда возникает событие onclick и оно вызывает анонимную функцию, которую вы передали, контекст это ссылается на объект, который вызвал событие, но не на myObject. потому что в этот момент контекст выполнения находится в контексте обработчика событий.

Решение: вы можете проделать трюк, о котором рассказали Майку Кантору, или, используя jQuery, использовать прокси-метод, чтобы определить контекст это в анонимной функции.

Итак, ваш код будет таким:

var myObject = {

    AddChildRowEvents: function(row, p2) {
        if (document.attachEvent) {
            row.attachEvent('onclick', $.proxy(function () {
                this.DoSomething();
            }, this));
        } else {
            row.addEventListener('click', $.proxy(function () {
                this.DoSomething();
            },this), false);
        }
    },

    DoSomething: function() {
        this.SomethingElse(); //<-- Error here, object 'this' does not support this method.
    }
}

вы упомянули, что ошибка находится в this.SomthingElse (), но ваш код не показывает ее. Если вы действительно получаете ошибку в этой строке кода, возможно, вы используете метод DoSomthing где-то еще в качестве обработчика событий.

Проблема заключается в том, как this управляется в JS, что может быть быстро, особенно когда у вас асинхронно вызывается обратный вызов (и когда создается закрытие, привязанное к этому).

Когда вызывается метод AddChildRowEvents, this хорошо ссылается на переменную myObject. Но цель этого метода - поставить обработчик клика. Так что событие будет запущено когда-нибудь в будущем. В то время какой объект действительно вызовет событие? Фактически, это DOM element, на который пользователь будет нажимать. Таким образом, this будет ссылаться на эту DOM element, а не на переменную myObject.

Для более современного решения, чем представленные, можно использовать bind method или arrow function.

1. Решение с функцией стрелки

let handler = {
	addEventHandler: function(row) {
	  console.info("this", this)
  	row.addEventListener("click", () => {
    	console.info("this", this)
    	this.doSomethingElse()
    })
  },
  
  doSomethingElse: function() {
  	console.info("something else")
  }
}
var div = document.querySelector("div")
handler.addEventHandler(div)
<div>one</div>

В arrow function (которые доступны с ES2015) контекст this находится не в контексте выполнения, а в лексическом контексте. Разница между ними в том, что лексический контекст обнаруживается во время сборки, а не во время выполнения, поэтому лексический контекст легче отслеживать. Здесь, внутри arrow function, this ссылается на тот же this внешней функции (то есть addEventHandler), так что ссылка на myObject.

Дополнительные сведения о свойствах функции жирной стрелки по сравнению с обычными функциями: https://dmitripavlutin.com/6-ways-to-declare-javascript-functions/

2. Решение с привязкой

let handler = {
	addEventHandler: function(row) {
	  console.info("this", this)
  	row.addEventListener("click", function() {
    	console.info("this", this)
    	this.doSomethingElse()
    }.bind(this))
  },
  
  doSomethingElse: function() {
  	console.info("something else")
  }
}
var div = document.querySelector("div")
handler.addEventHandler(div)
<div>one</div>

На этот раз, когда функция обратного вызова передается в addEventListener, можно связать контекст this с элементом this во внешней функции addEventHandler.

.bind () работает медленно (ссылка: sitepoint.com/…)

killjoy 30.04.2019 23:55

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