这是在JavaScript中定义继承链的“标准”方法吗?

时间:2015-01-08 05:47:25

标签: javascript inheritance prototype

我试图了解Javascript中的继承。

我知道每个对象都有一个原型,它是一个继承属性的对象。我知道.prototype属性仅存在于函数上,并且当它被用作构造函数时,它将被设置为从该函数创建的对象的原型。

虽然有些浏览器支持__proto__属性,但我知道对象的原型是不可访问的。 (但由于它不是该语言的经典部分,我想了解如何在没有它的情况下使用该语言。)

所以如果所有这些都是正确的(?),我想要揭示定义继承链的标准方法。

我能想到的唯一方法是:

我希望它们从另一个对象继承的所有对象,必须通过构造函数创建。他们的基础对象'将被设置为其构造函数的.prototype

当我希望他们中的一个成为一个基础对象'其他对象,我将其设置为另一个构造函数的.prototype。等等。

这看起来很奇怪。有没有办法(正常' JS)设置一个对象的基础'直?或者我是否必须以上述方式使用构造函数,以便创建继承链?

什么是'标准'创建继承的方法?我描述的方法是标准方法吗?

3 个答案:

答案 0 :(得分:1)

JavaScript支持继承的主要方式是通过原型继承。具体而言,只要在初始对象上找不到属性查找,JavaScript 委托中的对象就会转移到其他对象。此委托继续,直到JavaScript引擎到达Object.prototype,其中找到该属性或抛出错误。

创建以特定对象作为原型的对象的当前最佳做法是使用Object.create - 您可以查看更多信息here

以下是一个例子:

var methods = {
  method1: function () { console.log( 'something' ); },
  method2: function () { return 'cool'; }
};

/*
 * Now firstObj will delegate to methods whenever a property lookup can't
 * be found on firstObj itself
*/
var firstObj = Object.create( methods ); 

// You can add custom properties on firstObj
firstObj.someOtherProperty = 'custom property'; 

/*
 * You can create a chain of delegations! Property lookup first happens on secondObj.
 * If its not found there, it looks up the property in firstObj. If its not found there,
 * then it looks up the property in methods. Finally, if not found, it tries
 * Object.prototype
*/
var secondObj = Object.create ( firstObj );

答案 1 :(得分:1)

首先,JavaScript中的继承有点难以理解,因为:

  1. JavaScript是一种原型面向对象的编程语言(即对象直接从其他对象继承)。这意味着类和对象之间没有区别。用作类的对象称为原型。
  2. 不幸的是,创建原型实例的传统方法是使用new(这使得人们认为实例继承自构造函数,而不是原型)。这被称为constructor pattern,这是JavaScript混淆的主要原因。
  3. 出于这个原因Object.create被引入。它允许对象直接从其他对象继承。但是,与使用Object.create相比,new速度较慢。我遇到了同样的问题,我正在寻找替代方案;我确实想出了一个。

    JavaScript中传统的OOP方式

    请考虑以下代码:

    
    
    function Person(firstname, lastname, gender) {
        this.firstname = firstname;
        this.lastname  = lastname;
        this.gender    = gender;
    }
    
    Person.prototype.getFullname = function () {
        return this.firstname + " " + this.lastname;
    };
    
    Man.prototype             = new Person;
    Man.prototype.constructor = Man;
    
    function Man(firstname, lastname) {
        Person.call(this, firstname, lastname, "M");
    }
    
    var bobMarley = new Man("Bob", "Marley");
    
    alert(bobMarley.getFullname());
    
    
    

    这种编写代码的方式有几个问题:

    1. 没有封装。构造函数和原型方法在整个地方定义。它看起来不连贯。喜欢shaghetti。它看起来不像是一个逻辑单元。
    2. 我们通过将Man.prototype设置为Person.prototypenew Person继承firstname。但是,在执行此操作时,我们会在lastname上初始化genderMan.prototypeObject.create属性,这是错误的。
    3. JavaScript中的OOP新方法

      随着function Person(firstname, lastname, gender) { this.firstname = firstname; this.lastname = lastname; this.gender = gender; } Person.prototype.getFullname = function () { return this.firstname + " " + this.lastname; }; Man.prototype = Object.create(Person.prototype); Man.prototype.constructor = Man; function Man(firstname, lastname) { Person.call(this, firstname, lastname, "M"); } var bobMarley = new Man("Bob", "Marley"); alert(bobMarley.getFullname());的引入,我们现在可以编写如下代码:

      
      
      Man.prototype = new Person
      
      
      

      唯一的变化是我们写Man.prototype = Object.create(Person.prototype)而不是Object.create。这解决了传统方法的第二个问题。但是,代码仍然看起来像意大利面条。

      然而,var person = { init: function (firstname, lastname, gender) { this.firstname = firstname; this.lastname = lastname; this.gender = gender; }, getFullname: function () { return this.firstname + " " + this.lastname; } }; var man = Object.create(person, { init: { value: function (firstname, lastname) { person.init.call(this, firstname, lastname, "M"); } } }); var bobMarley = Object.create(man); bobMarley.init("Bob", "Marley"); alert(bobMarley.getFullname());非常强大。您也可以使用它来编写面向对象的代码,而根本不创建构造函数。有些人称之为initializer pattern

      
      
      Object.create
      
      
      

      这解决了传统方法的所有问题。但是,它也引入了一些新问题:

      1. 创建原型实例的方式与创建对象文字的方式不一致。
      2. 您必须使用init创建实例,然后使用new初始化新对象。这比仅使用var Person = defclass({ constructor: function (firstname, lastname, gender) { this.firstname = firstname; this.lastname = lastname; this.gender = gender; }, getFullname: function () { return this.firstname + " " + this.lastname; } }); var Man = extend(Person, { constructor: function (firstname, lastname) { Person.call(this, firstname, lastname, "M"); } }); var bobMarley = new Man("Bob", "Marley"); alert(bobMarley.getFullname()); function defclass(prototype) { var constructor = prototype.constructor; constructor.prototype = prototype; return constructor; } function extend(constructor, properties) { var prototype = Object.create(constructor.prototype); var keys = Object.keys(properties); var length = keys.length; var index = 0; while (index < length) { var key = keys[index++]; prototype[key] = properties[key]; } return defclass(prototype); }慢得多。
      3. 我的OOP方式是JavaScript

        为了解决这个问题,我在JavaScript中为OOP编写了自己的函数:

        &#13;
        &#13;
        defclass
        &#13;
        &#13;
        &#13;

        我在JavaScript中为OOP定义了两个函数extenddefclassprototype函数从原型创建“类”。这是可能的,因为prototypes and classes are isomorphic

        extend函数用于继承。它创建constructor extend的实例,并在返回新原型的“类”之前将一些属性复制到其上。

        这是我目前在JavaScript中创建原型链的方式。与其他方法相比,它具有以下优点:

        1. 每个“类”都被封装。整个地方都没有悬挂的原型方法。它看起来不像意大利面条。
        2. Object.create函数使用constructor进行继承。因此,新原型不会添加额外的属性。它是一个空白的原型。
        3. 您不必担心重置prototype上的defclass属性。它会自动为您完成。
        4. 与初始化模式中的对象文字和extend函数不同,Object.createnew函数是一致的。
        5. 我们使用Object.create代替init和{{1}}创建实例。因此,生成的代码要快得多。
        6. 我现在可能错了,但我不这么认为。希望有所帮助。

答案 2 :(得分:-1)

你可以通过javascript中的两种方式继承 - 经典和原型

古典

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

原型

function inherit (o) { 
    function F() {}
    F.prototype = o;
    return new F(); 
}