`this.some_property`在匿名回调函数中变成未定义的。

问题描述 投票:12回答:4

所以我不太明白,为什么变量的 this.tasks 在我的目标对象里面的add事件监听器里面变成了undefined。我感觉这可能与异步编程有关(我还不完全理解)。对不起,我是个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);              
        });

    }

谢谢你们! 你们都回答了我的问题! 我一直在为这个问题伤脑筋。给你们点赞!

javascript callback closures anonymous-function
4个回答
15
投票

当你进入匿名闭包时,范围就会改变,"this "也会改变。你可以通过以下方法来解决这个问题

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

无论哪种方式都很好用。

EDIT: 在ES6中,箭头函数可以用来代替,因为它们不绑定自己的 "this",所以变得更加简单。

this.gDiv.addEventListener('click', () => {
    alert(this.tasks);
 });

10
投票

这里是一些方法的比较(包括你的问题),给你一个试听的机会,并试图解释一下。

// 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箭头功能

function Test8(something) {
  this.something = something;
  setTimeout(() => console.log(this.something), 1000);
};

new Test8('Whoop');

1
投票

在ES6中,引入了箭头函数,它不绑定自己的这个。

MDN供参考.

所以使用箭头语法创建一个匿名函数可能是目前克服这个问题最简单的方法。目前除了IE之外,所有主流的浏览器都支持这种方法。


0
投票

关键字'this'对于事件处理程序和构造函数的意义发生了变化。

请参考MDN

https:/developer.mozilla.orgen-USdocsWebJavaScriptReferenceOperatorsthis#As_a_DOM_event_handler。

© www.soinside.com 2019 - 2024. All rights reserved.