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

Posted

技术标签:

【中文标题】`this.some_property` 在匿名回调函数中变得未定义【英文标题】:`this.some_property` becomes undefined inside anonymous callback function 【发布时间】:2014-03-13 05:30:54 【问题描述】:

所以我不太明白为什么变量 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);              
        );

    

谢谢你们!你们都回答了我的问题!我一直在为此挠头。向大家致敬!

【问题讨论】:

这能回答你的问题吗? How to access the correct `this` inside a callback? 【参考方案1】:

当您输入匿名闭包并且“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));

任何一种方式都很好。

编辑:在 ES6 中,可以使用箭头函数代替,因为它们不绑定自己的“this”,所以它变得更加简单:

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

【讨论】:

你的回答很好。你改变了它,现在我想知道是什么让你认为使用 jQuery 方法更好? 在大型代码库中,我看到 'var self = this;'惯用语总是很笨拙,尤其是当您进一步嵌套闭包时。这可确保您的“this”始终如您所愿。 或者可以使用调用方法 感谢米莎的帮助!【参考方案2】:

这里是一些方法的比较(包括你的问题),给你一个品尝者,并试着解释一下。

// 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#bind

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 functions

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

new Test8('Whoop');

【讨论】:

感谢您抽出宝贵时间整理所有内容!你是冠军! 是的,这是一篇很棒的文章。非常感谢@Xotic750——我会努力像你将来一样彻底地回答问题。我不得不承认我对 javascript 比较陌生,这是我见过的关于 JS 中代理函数的最佳解释之一。【参考方案3】:

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

MDN for reference.

因此,使用箭头语法创建匿名函数可能是当今解决此问题的最简单方法。目前所有主流浏览器都支持,除了IE。

【讨论】:

【参考方案4】:

关键字'this'改变了它对构造函数的事件处理程序的含义

请参考MDN

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#As_a_DOM_event_handler

【讨论】:

感谢蒂姆的参考! 我建议使用文档,而不是在没有深入了解的情况下遵循惯用的快速修复。

以上是关于`this.some_property` 在匿名回调函数中变得未定义的主要内容,如果未能解决你的问题,请参考以下文章

Git的进阶学习

ASP .NET MVC 3 - 将参数从视图传递到控制器

ES6深入浅出-9 Promise 3 视频-1.回调与回调地狱

RPC

Java基础——面向对象

在C#中啥是匿名方法?使用它都有哪些好处?