【问题标题】:Javascript Closures and 'this'Javascript 闭包和“this”
【发布时间】:2010-09-25 15:38:04
【问题描述】:

我创建的对象看起来像这样:

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 args 或 1 arg - 看看会发生什么并发布你的结果。
  • 你的评论说错误是在调用SomethingElse,但实际上不是在调用DoSomething吗?
  • 闭包实际上和这个例子没有任何关系,如果this的值不是来自这里的闭包。

标签: javascript closures


【解决方案1】:

当函数被调用时,“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))。我说的对吗?
【解决方案2】:

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:很好!它前面应该有一个var
  • 老兄,这是一个对象,它需要键:值对。针对jshint.com运行此代码
  • 我好像很累什么的,对不起。
  • 发生了:)我一直这样做; )
【解决方案3】:

问题来自如何在 JS 中管理 this,这可以很快,尤其是当您异步调用回调时(并且当创建闭包时,绑定到 this)。

当调用 AddChildRowEvents 方法时,this 很好地引用了变量 myObject。但是这个方法的目的是为点击放置一个处理程序。因此,该事件将在将来的某个时间触发。到时候,什么对象会真正触发事件呢?事实上,用户点击的就是DOM element。所以this 将引用这个DOM element 而不是myObject 变量。

对于比所提供的更现代的解决方案,可以使用bind methodarrow function

1。带箭头函数的解决方案

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

使用arrow function(自ES2015 起可用),this 上下文不在执行上下文中,而是在词法上下文中。两者的区别在于词法上下文是在构建时而不是在执行时找到的,因此词法上下文更容易跟踪。这里,在arrow function 内部,this 引用了与外部函数(即addEventHandler)相同的this,所以引用myObject

关于胖箭头函数与常规函数的属性的更多解释:https://dmitripavlutin.com/6-ways-to-declare-javascript-functions/

2。绑定解决方案

let handler = {
	addEventHandler: function(row) {
	  console.log("this", this)
  	row.addEventListener("click", function() {
    	console.log("this", this)
    	this.doSomethingElse()
    }.bind(this))
  },
  
  doSomethingElse: function() {
  	console.log("something else")
  }
}
var div = document.querySelector("div")
handler.addEventHandler(div)
&lt;div&gt;one&lt;/div&gt;

这一次,当回调函数被传递给addEventListener时,我们将this上下文绑定到了this外部addEventHandler函数中的元素。

【讨论】:

【解决方案4】:

这是闭包的常见问题。要解决它,请尝试以下操作:

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 对象的引用。
【解决方案5】:

您的代码有问题,在以下代码行中,您定义了一个匿名函数并将其作为 onClick 事件的事件处理程序传递,如下行:

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

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

当 onclick 事件引发并调用您传递的匿名函数时,this 上下文是指已引发事件的对象,而不是 myObject。 因为此时执行上下文是在事件处理程序上下文中。

解决方案:你可以按照 Mike Kantor 所说的技巧,或者使用 jQuery,使用代理方法,在匿名函数中定义 this 上下文。

所以你的代码会是这样的:

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 作为事件处理程序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多