【问题标题】:Stoyan Stefanov: JavaScript Patterns - "The Default Pattern"Stoyan Stefanov:JavaScript 模式 - “默认模式”
【发布时间】:2014-02-06 17:26:19
【问题描述】:

在第 6 章(代码重用模式)中有以下示例:

// the parent constructor
function Parent(name) {
    this.name = name || 'Adam';
}

// adding functionality to the prototype
Parent.prototype.say = function () {
    return this.name;
};

// empty child constructor
function Child(name) {}

// inheritance magic happens here
inherit(Child, Parent);

在“经典模式#1 — 默认模式”部分中,inherit() 函数的实现是:

function inherit(C, P) {
    C.prototype = new P();
}

在“使用模式 #1 时的缺点”一节中是以下示例:

var s = new Child('Seth');
s.say(); // "Adam"

我不明白以下作者的解释:

这不是你所期望的。孩子有可能通过 父的构造函数的参数,但你必须这样做 每次你需要一个新的孩子时继承,这是低效的, 因为你最终会一遍又一遍地重新创建父对象。

子级如何将参数传递给父级的构造函数?如果不通过隐藏的原型属性,如何在构造后更改子对象的原型?谁能给我一个例子,作者是什么意思?

【问题讨论】:

    标签: javascript design-patterns inheritance prototype


    【解决方案1】:

    每次需要新子对象时都必须进行继承,这是低效的,因为最终会一遍又一遍地重新创建父对象。

    这不是低效的,如果done properly 则不会创建更多对象。实际上,每次传递参数和调用父构造函数时,您都必须进行显式继承。

    function Child(name) {
        Parent.call(this, name); // apply the parent constructor on new instance
                                 // to set up instance variables like `name`
    }
    
    // inheritance stuff happens here
    Child.prototype = Object.create(Parent.prototype);
    

    ...当您了解原型链的工作原理以及 Object.create 的作用时,这并不神奇。

    【讨论】:

    • 正是我要写的东西:) Mat - 你可以从 Douglas Crockford 那里读到一些东西,javascript.crockford.com/prototypal.html
    • @Windkiller 尽管 Crockford 在“经典继承”中投入了所有的糖,但他仍然做错了:javascript.crockford.com/inheritance.html#sugar 创建 Parent 的实例来设置 Child 的原型部分是错误的,所以 this.prototype = new parent(); 应该是 `this.prototype = Object.create(parent.prototype);' Crockford 有一些不错的模式,但他自己称之为“经典”的模式似乎无法做到。
    • @Windkiller 他还忘记通过在 Child 中调用 Parent.call(this,args); 来重新使用 Parent 构造函数代码,这使得将 Child.prototype 设置为 parent 的实例更加危险(stackoverflow.com/a/21550855/1641941)。有关该代码中 args 的更多信息:stackoverflow.com/a/16063711/1641941 在“传递(构造函数)参数”下
    • @HMR - 除了 Object.create 基于旧的 Crockfords 模式这一事实。所以他当然没有使用它,因为它不存在。此外,他的模式现在被用作不支持 Object.create 的浏览器的 shim。
    • @Windkiller Crockford 有一些好东西和好的演示文稿,但我还没有看到他正确使用“经典继承”的演示文稿或博客文章。对于从 OOP JavaScript 开始并试图理解构造函数和原型的人来说,他没有任何帮助。 polyfil 已经存在多年,也被用于闭包库 goog.inherits bolinfest.com/javascript/inheritance.php (2009) 但 Crockford 在 2011 年仍然误导人们:stackoverflow.com/a/21617146/1641941
    【解决方案2】:

    提议的继承方案是烂的。
    正确的继承方式,即使是“简单的方式”是:

    // the parent constructor
    function Parent(name) {
        this.name = name || 'Adam';
    }
    
    // adding functionality to the prototype
    Parent.prototype.say = function () {
        return this.name;
    };
    
    // Child constructor
    function Child(name) {
          Parent.apply(this, arguments);  // to call with exact same parameters.
         // or  .call(this, /* the parameters you want */ ) ;
    }
    
    Child.prototype = Object.create(Parent.prototype); 
    
    Child.prototype.otherMethod = function() { /*...*/} ;
    

    请注意,调用new P() 而不是Object.create(P.prototype) 是完全愚蠢的,特别是因为构造函数可能不接受任何参数。在这种情况下,当 Object.create 不能失败时,您的应用程序可能会为继承抛出异常......

    忘记其他 cmets... 谈论每个类(而不是每个实例)创建一个对象的低效率是无关紧要的。 (也许忘了这本书?:-))

    【讨论】:

      【解决方案3】:

      正如 Bergi 指出的,您不应该创建 Parent 的实例来设置 Child 的原型部分。

      要处理构造函数参数或将参数传递给正在构建的函数链(您必须维护的代码以及将来需要新功能的应用程序始终在构建中),您可以在传递参数时使用一个对象.

      当您使用调解器/发布订阅对象并且不知道另一个函数完成后将调用什么函数时,这也很有帮助。

      有关示例,请参阅“传递(构造函数)参数”下的 here

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-07
        • 1970-01-01
        • 1970-01-01
        • 2011-05-28
        • 2020-12-21
        • 1970-01-01
        相关资源
        最近更新 更多