简体   繁体   中英

How do I allow objects to inherit members from their class?

This question is about Javascript, however the first programming language I learned was Java so I relate to that the most easily.

In Java, objects have access to static members, as demonstrated in the article Understanding Class Members by Oracle:

public class Bicycle {
  // INSTANCE fields
  private int cadence;
  private int gear;
  private int speed;

  // CONSTRUCTOR
  constructor Bicycle(c,g,s) { /*...*/ }

  //...

  // STATIC fields
  private static int numberOfBicycles = 0;
}

You can also refer to static fields with an object reference like myBike.numberOfBicycles

In addition to objects having access to static fields, subclasses do as well, as mentioned in Inheritance .

public class MountainBike extends Bicycle {
  public int seatHeight;
  constructor MountainBike(c,g,s,h) { /*...*/ }
  private static int mountainBikePrice = 324;
}

MountainBike now has 4 instance fields: cadence , gear , speed , and seatHeight ; and 2 static fields: numberOfBicycles , and mountainBikePrice .

I would like to emulate this behavior in my Javascript program, however, since JS is prototype-oriented and not object-oriented, objects and subclasses cannot have access to their "class" members.

// constructor
function Bicycle(c,g,s) {
  this.cadence = c
  this.gear = g
  this.speed = s
}
// instance methods
Bicycle.prototype.go = function () { /*...*/ }
Bicycle.prototype.stop = function () { /*...*/ }
// STATIC field
Bicycle.numberOfBicycles = 0 // <-- I want THIS field inherited

When I create a new Bicycle object via var myBike = new Bicycle() , I have access to the instance methods myBike.go() , etc., however I cannot get the static field via myBike.numberOfBicycles as I could in Java. The only way to get this is by calling Bicycle.numberOfBicycles .

Additionally, when I create a new subclass, it won't have access to its parent's fields.

// subclass constructor
function MountainBike(c,g,s,h) {
  Bicycle.call(this,c,g,s)
  this.seatHeight = h
}
// vvvvvvvv extension mechanism... ignore this vvvvvvvvvv
MountainBike.prototype = Object.create(Bicycle.prototype)
MountainBike.prototype.constructor = MountainBike
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// STATIC field
MountainBike.mountainBikePrice = 324

If I call MountainBike.numberOfBicycles I get undefined (which would not happen in Java—it would return the parent's value).

Other than assigning Bicycle.prototype.numberOfBicycles = 0 , there must be an easier way for objects to inherit properties of their class, and subclasses to inherit properties of their parent.

  1. Is there a way to access the object's class, such as myBike.class ? If so, then I could call myBike.class.numberOfBicycles .
  2. Is there a way to access a subclass's parent class, something like MountainBike.super ?

Actually, the property you are looking for is constructor (as in myBike.constructor.numberOfBicycles works as intended).

The constructor of an object is (kind of) like the class of an instance in Java. It's probably best to take this with a grain of salt because the concepts are not exactly interchangeable.

Edit:

So I see you are basically trying to create some full implementation of "classes" in JavaScript using prototype and constructor hacks (which is fine really). Backbone has an interesting approach to this in their extend function. Relevant source is the following (see bottom of question for license of this code):

var extend = function(protoProps, staticProps) {
    var parent = this;
    var child;


    // Inherits constructor if not specified in child props.
    if (protoProps && _.has(protoProps, 'constructor')) {
        child = protoProps.constructor;
    } else {
        child = function(){ return parent.apply(this, arguments); };
    }

    // copy all properties from parent and from static properties
    // into child
    _.extend(child, parent, staticProps);

    // figure out child prototype (kind of like you had)
    child.prototype = Object.create(parent.prototype);
    _.extendOwn(child, protoProps);
    child.prototype.constructor = child;

    // Save super for later access (answers question 2)
    child.__super__ = parent.prototype;

    return child;
  };

Annotations are mine. I edited the code to expose some similarities with what the OP had posted (ie used Object.create instead of _.create ). The way to use it is to set in the parent Parent.extend = extend; and then create the child class with Child = Parent.extend({...new properties and methods...},...) .

I decided to go ahead and post the entire function because it might contain solutions to problems you haven't asked about yet (such as inheritance of constructors). Specifically for question 2 , the Backbone devs made a decision to explicitly save the parent class in the child with __super__ , so you could do that as well in your inheritance code (after you call the Object.create ).

It also might just make more sense to use ES2015 Classes and inheritance with extends (here is some good docs on it ). The support for it is not great, so you would need to use a preprocessor such as Babel . Here is an example of these classes:

class Parent {...}

class Child extends Parent {...}

License of Backbone code fragment posted:

(c) 2010-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors. Backbone may be freely distributed under the MIT license.
For all details and documentation:
http://backbonejs.org

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM