18

你能解释一下在构造函数中设置方法和通过原型对象设置方法的区别吗?以下代码显示了这两种设置方法的方式 -say_hello并且say_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();
4

3 回答 3

31

foxxtrot 和 anakata 都是正确的,但我会投入 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 开发),否则我们不会讨论这个问题。

大多数 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'
于 2009-01-07T22:55:50.367 回答
6

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

如果您在“类”函数中进行绑定,则 JS 必须为每个实例完成创建和分配的工作。

于 2009-01-07T22:41:27.493 回答
5

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

于 2009-01-07T22:36:57.517 回答