【问题标题】:How to avoid "this" refering to the DOM element, and refer to the object如何避免“this”引用DOM元素,并引用对象
【发布时间】:2013-11-06 13:44:32
【问题描述】:

我有一个无法解决的问题。

上下文是:我想有一个继承链,属于这个继承的对象的方法必须是事件处理程序,同时能够到达对象的属性。

我正在尝试编写不带“新”字的 JavaScript,而是使用带有一些继承层次结构的 Object.create()。所以首先采用这种方法。

所以我为我的其余对象(myProto)制定了蓝图,然后我使用 Object.create 创建对象(这样就没有闭包,我可以将this 分配给thatself)。现在,当我使用该对象的方法来处理例如 div 上的单击事件时,this 显然将引用 DOM 对象,并且我失去了访问对象属性的可能性。

var myProto = {
    init: function (name, value) {
        this.name = name;
        this.value = value;

        return this;
    },
    someHandler: function (e) {
        // Normally I would use this instead of e.target...

        e.target.innerHTML = this.name + this.value; // This does not refer to the object.
    }
};

var myObject = Object.create(myProto).init('myName', 'myValue');
document.getElementById('myDiv').onclick = myObject.someHandler;

这里是小提琴:http://jsfiddle.net/pgPHM/

现在是“经典”方法:如果我使用新的 Constructor 形式,在闭包中分配 this 然后访问它会很容易,但是 Constructor.prototype 中的函数存在问题

var Constructor = function (name, value) {
    var self = this;

    self.name = name;
    self.value = value;
};

Constructor.prototype.someHandler = function () {/*self does not reach this here*/};

jsfiddle:http://jsfiddle.net/ZcG3J/2/

我真的不明白为什么 JS 对象没有真正的 thisself 或者没有这些棘手的上下文、闭包等来引用自己的东西......

基本上问题是:

如何使用对象的方法作为事件处理程序,并且仍然能够访问该对象?

【问题讨论】:

    标签: javascript oop inheritance this


    【解决方案1】:

    除了使用new时,Javascript中的this是根据函数的调用方式设置的。以正确的方式称呼它,this 将是您想要的。我无法真正说出您要在问题中解决什么问题,但这是确定this 的方式。

    1. 调用obj.method() - this 将设置为objmethod()
    2. 使用function.call()function.apply() 来控制this 将成为您自己。
    3. 根据您是否处于严格模式,进行普通函数调用,例如 func()this 将设置为全局对象或 undefined
    4. 使用.bind()(在现代浏览器中)创建一个函数存根,它将在内部自动使用.apply() 将其值“绑定”到函数的执行。
    5. 当您使用new 调用构造函数时,this 将被设置为构造函数内新创建的对象。
    6. 当您将回调函数作为参数传递给任何函数调用时(例如addEventListener(),调用函数有责任决定它希望将this 指针设置为什么以及不绑定到什么您自己的对象。addEventListener()this 设置为导致事件的 DOM 对象。

    对于您希望成为特定对象上的方法调用的事件处理程序,您必须创建一种将方法调用与对象关联的方法。您可以使用上面指定的.bind(),也可以使用可以在闭包中引用保存的this 值的匿名函数。

    var self = this;
    document.addEventListener('keyup', function(e) {
        self.handleKeys(e);
    })
    

    或者,使用.bind()

    document.addEventListener('keyup', this.handleKeys.bind(this));
    

    仅供参考,这两种方法之间没有任何真正的功能差异,因为.bind() 只是创建了一个闭包并执行第一个示例的操作,但它会为您完成。

    【讨论】:

    • 如何使用对象的方法作为事件处理程序,并且仍然能够访问该对象?
    • @ikaros45 - 请参阅我的答案中的选项 4。
    • @ikaros45 - 添加了几个示例。
    • 谢谢,非常完整的回答。
    【解决方案2】:

    您的 jsfiddle 中的第一个问题是 self 是 Constructor 的局部变量,它在函数之外不可用。您对以下代码的看法:

    var Constructor = function(name, value) {
        var self = this;
        self.name = name;
        self.value = value;
        self.someHandler = function(e) {
            e.target.innerHTML = self.name + self.value; // self undefined    
        }
        return self;
    };
    
    var myObject = Constructor('myName', 'myValue');
    document.getElementById('myDiv').onclick = myObject.someHandler;
    

    JsFiddle -> http://jsfiddle.net/ZcG3J/4/

    它的结构是你想要的吗?

    【讨论】:

    • 这很简单也很有效,但这不是使用构造函数的 .prototype 对象。如果您想多次实例化构造函数,则效率不高。基本上你是从头开始构建一个对象,而不是使用原型继承。
    • 嗯 ...据我所知,如果您使用 new 关键字,您也在创建新对象。在某些情况下,原型继承可能会非常混乱。这就是为什么我更喜欢显示模块模式。
    • 你如何将显示模式与继承结合起来?对我来说似乎很难让一个对象继承自另一个使用该模式创建的对象,或者 Crockford 的“SuperConstructor”,这是一种相同的。
    • 我建议阅读这篇文章krasimirtsonev.com/blog/article/…我解释我的方法。
    • 嗯,JS 甚至没有类......你的继承方法只是在这里和那里覆盖属性。我真的认为必须有一种更微妙和“javascriptian”的继承方式。无论如何,我发现了一些其他有趣的东西。谢谢
    【解决方案3】:

    简单。如果函数作为方法而不是“裸”被调用,this 总是指最后一个点之前的单词。所以而不是:

    document.getElementById('myDiv').onclick = myObject.someHandler;
    
    // You're just passing the function here, not calling it.
    // It will be called by the onclick handler so `this` is changed
    

    这样做:

    document.getElementById('myDiv').onclick = function(){
        myObject.someHandler();
    
        // Here, you're actually calling it. So this is the word
        // before the last dot. Which is myObject
    }
    

    在更现代的 javascript 中,您当然可以使用 bind:

    document.getElementById('myDiv').onclick = myObject.someHandler.bind(myObject);
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-04
    • 2012-12-08
    • 1970-01-01
    • 2020-09-04
    • 2017-07-28
    • 1970-01-01
    • 2014-02-04
    相关资源
    最近更新 更多