Переменная объекта javascript становится неопределенной внутри анонимной функции
Поэтому я не могу понять, почему переменная this.tasks становится неопределенной внутри прослушивателя добавления событий, который есть у меня внутри моего целевого объекта. У меня есть ощущение, что это может быть связано с асинхронным программированием (что я до сих пор не до конца понимаю). Извините, я немного новичок в JS, но если бы вы, ребята, могли бы объяснить мне, что я делаю неправильно, и что может быть лучшим решением, которое было бы здорово! Благодарю.
function Goal(name) {
this.gDiv = document.createElement('div');
this.name = name || "goal";
this.tasks = document.createElement('ul');
//Sets the styling and content and adds it to the parent element
this.initialize = function() {
this.gDiv.className = "default";
this.gDiv.setAttribute("id", this.name);
this.gDiv.innerHTML = this.name;
elem.appendChild(this.gDiv);
this.gDiv.parentNode.insertBefore(this.tasks, this.gDiv.nextSibling);
this.tasks.style.display = "none";
};
//Creates a list underneath the a dive associated with the Goal object
this.addTask = function(task) {
var newLi = document.createElement('li');
newLi.innerHTML = task;
this.tasks.appendChild(newLi);
};
this.gDiv.addEventListener('click', function(){
alert(this.tasks);
});
}
Спасибо вам, ребята! Вы все ответили на мой вопрос! Я почесал голову на это некоторое время. Слава всем вам!
4 ответа
Область действия изменяется, когда вы вводите анонимное закрытие, и изменяется "это". Вы можете взломать его, сделав
var self = this;
И затем использовать self вместо этого (например):
function Goal(name) {
var self = this;
/* ... */
this.gDiv.addEventListener('click', function(){
alert(self.tasks);
});
Если вы используете jQuery, вы можете сделать что-то более приятное:
this.gDiv.addEventListener('click', $.proxy(function() {
alert(this.tasks);
}, this));
В любом случае работает просто отлично.
РЕДАКТИРОВАТЬ: Спасибо за предложение по редактированию.
Вот сравнение некоторых методов (включая вашу проблему), чтобы дать вам дегустатор, и попытаться объяснить немного.
// This is the problem that you have,
// where `this` inside the anonymous function
// is a different scope to it's parent
function Test1(something) {
// `this` here refers to Test1's scope
this.something = something;
setTimeout(function() {
// `this` here refers to the anonymous function's scope
// `this.something` is `undefined` here
console.log(this.something);
}, 1000);
};
new Test1('Hello');
// This solution captures the parent `this` as `test2This`,
// which can then be used inside the anonymous function
function Test2(something) {
var test2This = this;
this.something = something;
setTimeout(function() {
console.log(test2This.something);
}, 1000);
}
new Test2('World');
// This solution captures `this` as `test3This` in an `IIFE closure`
// which can then be used in the anonymous function
// but is not available outside of the `IIFE closure` scope
function Test3(something) {
this.something = something;
(function(test3This) {
setTimeout(function() {
console.log(test3This.something);
}, 1000);
}(this));
}
new Test3('Goodbye');
// This method requires that you load an external library: jQuery
// and then use it's `$.proxy` method to achieve the basics of
// Test3 but instead of being referred to as `test3This` the
// outer scope `this` becomes the inner scope `this`
// Ahh, that's much clearer?
function Test4(something) {
this.something = something;
setTimeout($.proxy(function() {
console.log(this.something);
}, this), 1000);
}
new Test4('Mum');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
// This is approximately what jQuery's `$.proxy` does
// but without having to load the whole library
function Test5(something) {
this.something = something;
setTimeout((function(func, context) {
return function() {
func.call(context);
};
}(function() {
console.log(this.something);
}, this)), 1000);
}
new Test5('Dad');
// Lets create the proxy method as a reuseable
function proxy(func, context) {
var args = Array.prototype.slice.call(arguments, 2);
return function() {
return func.apply(
context,
args.concat(Array.prototype.slice.call(arguments))
);
};
}
// and now using it
function Test6(something) {
this.something = something;
setTimeout(proxy(function() {
console.log(this.something);
}, this), 1000);
}
new Test6('Me want cookies');
Тогда у нас есть функция # связать
function Test7(something) {
this.something = something;
setTimeout(function() {
// `this` was bound to the parent's `this` using bind
console.log(this.something);
}.bind(this), 1000);
};
new Test7('Num num');
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.9/es5-shim.min.js"></script>
И совсем недавно ES2015 Arrow функционирует
function Test8(something) {
this.something = something;
setTimeout(() => console.log(this.something), 1000);
};
new Test8('Whoop');
В ES6 были представлены функции стрелок, которые не связывают их с этим.
Поэтому создание анонимной функции с использованием синтаксиса стрелок, вероятно, является самым простым способом преодоления этой проблемы в настоящее время. В настоящее время он поддерживается всеми основными браузерами, кроме IE.
Ключевое слово this меняет свое значение для обработчика событий для конструктора
пожалуйста, обратитесь к MDN
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this