通过原型对象或构造函数设置方法,区别?

时间:2009-01-07 22:34:46

标签: javascript constructor prototype

你能解释一下构造函数中的设置方法和原型对象之间的区别吗?以下代码显示了设置方法的这两种方法 - say_hellosay_bye都可以正常工作:

function MessageClass() {
  this.say_bye = function() { alert('see ya'); };
}

MessageClass.prototype.say_hello = function() { alert('hello'); };

x = new MessageClass();
x.say_hello();
x.say_bye();

3 个答案:

答案 0 :(得分:31)

foxxtrot和annakata都是正确的,但我会投入2美分。

如果您使用原型,那么“MessageClass”的每个实例实际上都引用了相同的函数。这些函数只在内存中存在一次,并用于所有实例。如果在构造函数中声明方法(或以其他方式将其添加到特定实例)而不是原型,则为MessageClass的每个实例创建一个新函数。

话虽如此,大多数情况下可能没有明显的性能差异,您也不太可能看到内存使用差异。我会选择原型方法,除非你有其他令人信服的理由。我可能想要在构造函数中声明方法的唯一原因是你需要一个闭包。例如,如果您有事件处理程序,或者您想使用getter / setter模拟私有属性,那么您可能会这样做:

function MessageClass() {
    var self = this;
    this.clickHander = function(e) { self.someoneClickedMe = true; };

    var _private = 0;
    this.getPrivate = function() { return _private; };
    this.setPrivate = function(val) { _private = val; };
}

编辑:因为讨论了这个效果对象如何通过构造函数中指定的函数扩展另一个对象,我正在添加更多细节。我可能会使用术语“类”来简化讨论,但重要的是要注意js不支持类(这并不意味着我们不能做好OO开发)或者我们不会讨论这个问题。 / p>

大多数javascript库调用基类和子类的构造函数。 (例如Prototype.js的Object.extend)这意味着在每个构造函数的构造函数中分配的方法将在结果对象上可用。但是,如果您自己扩展对象,可能会产生意外后果。

如果我上面的MessageClass并扩展它:

function ErrorMessageClass() {}
ErrorMessageClass.prototype = new MessageClass();

errorMsg = new ErrorMessageClass();

然后errorMsg将有一个getPrivate和setPrivate方法,但它们可能不会像您期望的那样运行。因为这些函数在分配时是作用域的(即在“ErrorMessageClass.prototype = new MessageClass()”中,不仅共享了get / setPrivate方法,所以_private变量也会在ErrorMessageClass的所有实例之间共享。这实际上使_private成为ErrorMessageClass的静态属性。例如:

var errorA = new ErrorMessageClass();
var errorB = new ErrorMessageClass();
errorA.setPrivate('A');
console.log(errorA.getPrivate()); // prints 'A'
console.log(errorB.getPrivate()); // prints 'A'
errorB.setPrivate('B');
console.log(errorA.getPrivate()); // prints 'B'

与clickHandler函数和someoneClickedMe属性类似:

errorA.clickHandler();
console.log(errorA.someoneClickedMe); // prints 'true'
console.log(errorB.someoneClickedMe); // prints 'true'

但是,请更改这些函数定义以使用this._private:

this.getPrivate = function() { return this._private; };
this.setPrivate = function(val) { this._private = val; };

并且ErrorMessageClass实例的行为变得更符合您的期望:

errorA.setPrivate('A');
errorB.setPrivate('B');
console.log(errorA.getPrivate()); // prints 'A'
console.log(errorB.getPrivate()); // prints 'B'

答案 1 :(得分:6)

如果你通过原型JS绑定方法只需要做一次并绑定到一个对象类(这使得它可以用于OO JS扩展)。

如果你在“class”函数中进行绑定,JS必须为每个实例创建和分配工作。

答案 2 :(得分:5)

不同之处在于从Message Class派生类。只有在原型上声明的方法才能在Message的子类上使用。