【问题标题】:Setting methods through prototype object or in constructor, difference? [duplicate]通过原型对象或构造函数设置方法,区别? [复制]
【发布时间】:2010-09-30 03:57:15
【问题描述】:

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

【问题讨论】:

    标签: javascript constructor prototype


    【解决方案1】:

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

    【讨论】:

    • 所以如果我有 'BetterMessageClass.prototype = new MessageClass' 只有 'say_hello' 会被继承?
    • 我可以从子类调用“say_bye”方法(声明为“this”)。这是我的代码: function Employee() { this.say_bye = function () { alert('see ya'); }; } function Manager() { } Manager.prototype = new Employee; Manager.prototype.constructor = 经理; var manager = new Manager(); manager.say_bye();
    • @foxxtrot 那不正确。甚至在构造函数内部 this 上声明的方法也可以在子类中访问。考虑一下 Prestaul 的代码errorA.setPrivate('A'),它正在访问父类MessageClassthis 上声明的方法。
    • 我也错误地认为区别在于继承,但事实并非如此:function parentClass() { this.inheritedFunc = function() {return "Some Value"}; } function childClass() {}; childClass.prototype = new parentClass(); var childObj = new childClass(); console.log(childObj.inheritedFunc()); //successfully logs "Some Value"
    【解决方案2】:

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

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

    【讨论】:

    • 如果我先将方法定义为函数,然后在构造函数中说:'this.say_bye = MessageClass_sayhello;
    • 稍微好一点,但仍然不如原型设计那么高效,您仍然无法在此基础上进行扩展
    • 能否扩展对象的问题很大程度上取决于您使用的是哪个 js lib 以及是否使用了一个。如果在扩展对象时调用构造函数,则方法可用。我相信大多数主要库在扩展时都会调用构造函数。
    • 比如我自己扩展一个对象:Subclass.prototype = new Class();那么在 Class 构造函数中分配的函数在 Subclass 上可用。但是,这些函数在 Subclass 的实例之间共享,这可能导致意外行为。制作原型当然更好。
    【解决方案3】:

    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'
    

    【讨论】:

    • 该死的你的口才:)(虽然我不同意没有明显的区别)
    • 虽然如果你使用 this._private,这个变量是可以从外部访问的。
    • (从技术上讲,您可以在大多数(如果不是全部)JS 环境中使用调试器进行跟踪)
    猜你喜欢
    • 1970-01-01
    • 2013-10-21
    • 2012-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-09
    • 2016-06-26
    • 1970-01-01
    相关资源
    最近更新 更多