在原型和事件中进行事件绑定是否明智?为什么这种情况会丢失?

时间:2015-02-07 19:27:31

标签: javascript javascript-events prototype

为网页创建模块化JavaScript代码时,我经常需要将事件附加到例如纽扣。

采用以下示例代码,通常位于AMD模块中:

define(function(require) {

    var MyObject = function(ele) {
        this.ele = ele;
    }

    MyObject.prototype = {
        myfunct: function() {
            console.log('myfunct called');
        }
    }

    return MyObject;

});

稍后在页面上我会这样做:

$(document).ready(function() {
    var button = $('#button'),
        myobj = new MyObject(button);

    button.click(function() {
        myobj.myfunct();
    });
});

这很有效,但我认为似乎有点不洁净。

例如,我需要在全局命名空间中创建至少一个变量来将函数绑定到按钮。此外,当页面上有许多JavaScript驱动的交互时,代码变得混乱 - 这是我最初想通过使用模块化JavaScript解决的问题。

这就是我的想法是为了原型中的事件绑定:

var MyObject = function(ele) {
    self = this;
    this.element = ele; 

    this.init();
}

MyObject.prototype = {
    init: function() {        
        $(this.element).click(function() {
           self.myfunct();
        });
    },
    myfunct: function() {
        console.log('myfunct called');
    }
}

这样,模块外部的代码看起来像这样:

$(document).ready(function() {
    var button = $('#button'),
        myobj = new MyObject(button);

});

将事件绑定移动到原型中是否明智?如果是这样,我可以采用init()的方式吗?

此外,我注意到当页面上有两个按钮时,某些上下文丢失 - self始终引用MyObj的最后一个实例。

为什么会发生这种情况 - 我想使用self = this;我可以阻止上下文?

Fiddle

3 个答案:

答案 0 :(得分:1)

让我们从第二个问题开始。代码的问题在于您将self声明为全局变量,因为您忘记/省略了var关键字。因此,当您创建两个或更多实例时,最后一个实例会覆盖前一个实例,并且所有点击事件中的self都会指向最后一个实例。

检查固定代码。请注意,您必须将var self = this移动到init方法,因为现在它是本地变量:

var MyObject = function(ele) {
    this.element = ele; 
    this.init();
}

MyObject.prototype = {
    init: function() {        
        var self = this;
        $(this.element).click(function() {
           self.myfunct();
        });
    },
    myfunct: function() {
        console.log('myfunct called');
    }
}

至于第一个问题,它的设计是否合适,并没有任何问题。 init方法中的绑定事件确实更清晰。

答案 1 :(得分:1)

按照您的方式进行事件绑定没有任何问题。你失去范围的原因是因为你做了

self = this;

在构造函数中创建self作为全局变量。因此,每次调用构造函数时,它都会将self设置为该实例。

要修复它,请在init函数中将self设置为局部变量:

MyObject.prototype = {
  init: function() {
    var self = this;  // <-- this is the fix      
    $(this.element).click(function() {
       self.myfunct();
    });
} 

答案 2 :(得分:1)

好的,首先self发生了什么。

这一行:

self = this;

您正在创建一个名为self的全局变量,每次调用构造函数时都会被覆盖。如果您使用严格模式,可以很容易地检测到这一点。此外,如果您正确使用了局部变量,那么您的原型将不知道self是什么,因此您尝试在原型中使用self会被破坏。

我认为你的两种方法都存在问题:

  • 第一种方法需要在MyObject类型的 之外进行过多的手动工作。
  • 第二种方法(如果它正常工作)将事件附加到按钮作为调用构造函数的副作用。这对使用您的API的人来说很困惑,因为人们希望构造函数创建一个对象,而不是修改其他现有对象。

作为补救措施,我建议采用以下方法:

var MyObject = function(ele) {
    this.element = ele; 
}

MyObject.prototype = {
    attachEvents: function() {        
        var self = this;
        $(this.element).click(function() {
           self.myfunct();
        });
    },
    myfunct: function() {
        console.log('myfunct called');
    }
};

$(document).ready(function() {
    var button = $('#button'),
        myobj = new MyObject(button);

    myobj.attachEvents();
});

这需要在实例化MyObject的人的一个额外步骤,但它清楚地传达了将事件附加到myobj的封装元素的意图。它也不需要使用MyObject的人对你的第一种方法进行错综复杂的操作。