Javascript 闭包和“这个”

IT技术 javascript closures
2021-02-09 23:49:56

我创建的一个对象有问题,它看起来像这样:

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”,我做错了什么?

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 ))。我对么?
2021-04-05 23:49:56

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);
        }
    }
}
@GnrlBzik:抓得好!var前面应该有一个
2021-03-22 23:49:56
你确定“那个=这个;” 在对象文字中定义它的情况下有效吗?@svinto
2021-03-23 23:49:56
看来我是很累了还是什么的,为自己的愚蠢感到抱歉。
2021-03-24 23:49:56
老兄这是一个对象,它需要键:值对。针对jshint.com运行此代码
2021-03-26 23:49:56
发生 :) 我一直这样做;)
2021-04-06 23:49:56

问题来自thisJS 中的管理方式,可以很快,尤其是当您异步调用回调时(并且当创建一个闭包时,绑定到此)。

当调用 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)
<div>one</div>

使用arrow function(自 以来可用ES2015),this上下文不是执行上下文,而是词法上下文。两者之间的区别在于词法上下文是在构建时而不是在执行时找到的,因此词法上下文更容易跟踪。这里,内侧的arrow functionthis引用相同的this外部功能(即,addEventHandler),以便参考myObject

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

2.使用bind解决

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)
<div>one</div>

这一次,当回调函数传递给 时addEventListener,已经将this上下文绑定this外部addEventHandler函数中元素

.bind() 很慢(参考:sitepoint.com/...
2021-03-20 23:49:56

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

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 对象的引用。
2021-04-11 23:49:56

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

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

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

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

解决方案:您可以按照 Mike Kantor 所说的技巧,或者使用 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方法作为事件处理程序。