【问题标题】:adding prototype in class definition in javascript在javascript的类定义中添加原型
【发布时间】:2013-07-02 18:18:13
【问题描述】:

在stackoverflow上阅读What techniques can be used to define a class in JavaScript, and what are their trade-offs?我明白我可以通过

方法一:

function Person(name, gender){
   this.name = name;
   this.gender = gender;
}

并在原型中添加函数,以避免每次实例化时重新创建成员函数。喜欢

Person.prototype.speak = function(){
   alert("my name is" + this.name);
}

并通过

创建它的实例
var person = new Person("Bob", "M"); 

我认为没有新的关键字也可以创建相同的对象

方法二:

var Person = function (name, gender) {

    return {name:name, gender:gender}; 

}

person  = Person("Bob", "M");

第二种方法是否与第一种方法完全相同?另外,如果是这样,我将如何在第二种方法中模拟通过原型 (正如我们在方法 1 中看到的那样)添加函数

【问题讨论】:

    标签: javascript class object prototype


    【解决方案1】:

    不,方法 2 != 方法 1。第二种方法是使用指向 Object.prototype 的原型创建一个新的匿名对象,而第一种方法是使用指向 Person.prototype 的原型创建一个新对象。

    在伪代码中:

    // Method #1
    function Person(name, gender) {
        // The magic JS does *for us* (but *only* when invoked with `new`)
        var this = {};
        // __proto__ is the *internal* prototype reference
        // It's not required to be accessible unless you're in an ES6 environment.
        this.__proto__ = Person.prototype;
    
        // Person.prototype is also created by JS for us
        // and provided with a reference (among other things)
        // to this function as prototype.constructor.
    
        // Our stuff
        this.name = name;
        this.gender = gender;
    
        // More JS magic
        return this;
    }
    
    // Method #2
    function Person(name, gender) {
        // Just our stuff - no magic JS goodness here
        return {
            name: name, gender: gender
        };
    }
    

    【讨论】:

    • "+1" 真棒.. 这有帮助.. :)
    • JS 不做var this = {}; this.prototype = Person.prototype; this.constructor = Person;。这是完全错误的。取而代之的是var this = Object.create(Person.prototype);。当你纠正你的错误时,我会删除反对票。第二个对象(字面量对象)的原型也指向Object.prototype,而不是Object
    • @AaditMShah - 两者都很好 :-) 我已经更新了我的答案以纠正错误并明确表示它是 internal 正在设置的原型属性(不一定是名为prototype 的可访问属性)。谢谢!
    • @SeanVieira 没问题。我删除了反对票。顺便说一句,实例上没有设置 constructor 属性。它继承自原型。
    【解决方案2】:

    正如 Sean Vieira 所解释的,第一种方法与第二种方法不同。在第一个方法中,实例继承自Person.prototype。在第二种方法中,实例直接继承自Object.prototype

    方法一:

            null
             ^
             |
             | __proto__
             |
    +------------------+
    | Object.prototype |
    +------------------+
             ^
             |
             | __proto__
             |
    +------------------+
    | Person.prototype |
    +------------------+
             ^
             |
             | __proto__
             |
    +------------------+
    |      person      |
    +------------------+
    

    方法二:

            null
             ^
             |
             | __proto__
             |
    +------------------+
    | Object.prototype |
    +------------------+
             ^
             |
             | __proto__
             |
    +------------------+
    |      person      |
    +------------------+
    

    有趣的是,上面的图表向您展示了 对象从 JavaScript 中的其他对象继承,而不是从构造函数。因此,当您创建 new Person 时,实例继承自 Person.prototype,而不是 Person 本身。

    这些相关信息如何?对于初学者,它表明您不需要创建构造函数来创建对象的实例。相反,您可以直接创建原型对象,如下所示:

    var person = {
        create: function (name, gender) {
            var person = Object.create(this);
            person.gender = gender;
            person.name = name;
            return person;
        },
        speak: function () {
            alert("My name is " + this.name + ".");
        }
    };
    

    在上面的例子中person 等价于Person.prototype。您现在可以创建person 的实例,如下所示:

    var bob = person.create("Bob", "M");
    

    bob 的原型链如下所示:

            null
             ^
             |
             | __proto__
             |
    +------------------+
    | Object.prototype |
    +------------------+
             ^
             |
             | __proto__
             |
    +------------------+
    |      person      |
    +------------------+
             ^
             |
             | __proto__
             |
    +------------------+
    |       bob        |
    +------------------+
    

    那么为什么要创建这样的对象呢?

    1. 看起来更干净。一切都封装在一个对象字面量中。
    2. 对象继承自对象更容易理解。不需要构造函数。
    3. 您不需要使用new 来创建实例。这个solveslotproblems

    有关此模式的更多信息,请阅读我在 "Why Prototypal Inheritance Matters" 上的博文。

    【讨论】:

    • 有趣的博客,我很好奇您能否解释原型模式优于构造函数的 3 个原因: 1:技术上可行,但您不喜欢语法(在 cmets 中清除) . 2.同1,不喜欢语法,容易出错。 3.不喜欢语法,太混乱(同1和2)。我缺少的是具有更好可扩展性或性能更好的技术基础。在某种程度上,我同意构造函数可能容易出错,但即使在使用闭包编译器之前,我也几乎不会滥用它们(忘记新的)
    • @HMR 我已经在以下答案中回答了原型继承相对于经典继承的优势:stackoverflow.com/a/16872315/783743。尽管如此,我必须承认,这两种模式在权力上是等价的,在其中一种模式之间进行选择只是一个偏好问题。但是你应该注意到newObject.create 快得多。因此,如果您想要性能,请坚持使用new。我创建了一个小而快的库,它使使用构造函数和原型变得更加容易(特别是对于那些不理解它的人):github.com/javascript/augment
    • 感谢您的回复,也许这对您来说很有趣。 Michael Bolin 研究过闭包编译器,并在功能模式上为构造函数辩护这是一个旧的,没有提到 Object.create(原型模式?)bolinfest.com/javascript/inheritance.php 在某些时候 JavaScript 将有类(Ecma 6)
    • +1 用于原型,但我不确定它是否与 OP 的方法 2 相同,因为它看起来像功能模式。
    • @HMR No. Crockford 的功能模式肯定和我的prototypal pattern 不一样。不同之处在于函数模式没有delegation 形式的继承。相反,它扩展了具有新属性的单个对象。 Crockford 所做的与此答案的类似:stackoverflow.com/a/17008693/783743 它满足 Liskov 的替换原则。谷歌。
    【解决方案3】:

    您的示例在访问属性方面实现了相同的目标,但它们并不相同,因为第一种方法具有基本的 Object 原型。第二个问题的答案是:

    function Person( name, gender ){
       return {
         name: name,
         gender: gender,
         speak: function(){
            alert("my name is" + this.name);
         }
       };
    }
    

    这里没有原型。该函数被烘焙到对象文字中。使用文字而不是原型的唯一价值是创建一个闭包,这样您就可以拥有私有变量,例如:

    function Person( name, gender ){
       return {
         speak: function(){
            alert("my name is" + name);
         }
       };
    }
    

    【讨论】:

    • 它们不等价 - 在方法 2 中,person instanceof Person === false
    • 我相信你的建议类似于this.speak = function() {..} not in protype..
    • @Alnitak 公平点。我想'等效'!=='做同样的事情',这取决于你需要它做什么。
    【解决方案4】:

    也许这个答案可以解释更多关于构造函数、继承、私有变量和覆盖方法的信息:Prototypical inheritance - writing up

    其他答案已经解决了您的问题;将对象创建为对象文字 {prop:val,method:function(){}} 并使用构造函数创建对象:var MyObject=function(){this.prop=val};MyObject.prototype.method=function(){};

    【讨论】:

      猜你喜欢
      • 2011-06-02
      • 1970-01-01
      • 2018-03-22
      • 1970-01-01
      • 2019-11-18
      • 2010-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多