-->

inheritance using underscore

2020-08-01 05:55发布

问题:

I've been given a class -

Zoo.Controller = (function() {

  function Controller() {}

  Controller.prototype.params = {};

  Controller.prototype.set_params = function(params) {
    this.params = params;
    return this;
  };

  return Controller;

})();

and I want to inherit from that class using _.extend

Zoo.Controllers.WhaleController = _.extend({

  new: function () {
    // do something
  }

}, Zoo.Controller);

When I try to instantiate that class like so...

this.whale_controller = new Zoo.Controllers.WhaleController();

I get -

Uncaught TypeError: object is not a function

Is it possible to do what I'm trying? I've read multiple articles on inheritance in JS, but had assumed that the Underscore library had it solved for me.

回答1:

As Bergi pointed out; it isn't hard to inherit in JavaScript. You should know what a constructor function does and what prototype is used for. This answer may help with that, I tried to demonstrate prototype through simple and hopefully easy to understand examples. You can copy and paste the code in your browsers JS commandline (in the console) and change it to see if you understand how prototype behaves in JavaScript.

To inherit from ZooController you can:

Zoo.Controllers.WhaleController = function(args){
  Zoo.Controller.apply(this,arguments);//re use Zoo.Controller constructor
                                  //and initialize instance variables
  //instance specific members of Whale using an args object
  this.weitht=args.weight||4;
  this.wu=args.weightUnit||wu.metricTon;
  //Zoo.Controller.call(this,arg1,arg2); can be used too but I usually use
  // an args object so every function can pick out and mutate whatever they want
  // for example: var w = new WhaleController({weight:3,weightUnit:wu.metricTon});
  // now it looks more like pythons optional arguments: fn(spacing=15, object=o)
};
//set Zoo.controller.prototype to a shallow copy of WhaleController.prototype
//may have to polyfill the Object.create method if you want to support older browsers
Zoo.Controllers.WhaleController.prototype=Object.create(Zoo.Controller.prototype);
//repair constructor
Zoo.Controllers.WhaleController.prototype.constructor=Zoo.Controllers.WhaleController;
//extend Zoo.controller.prototype.set_params
Zoo.Controllers.WhaleController.prototype.set_params=function(){
  //re use parent set_params
  Zoo.Controller.prototype.set_params.apply(this,arguments);
  //and do something extra
  console.log("extra in set_params from WhaleController");
};
//WhaleController own function
Zoo.Controllers.WhaleController.prototype.whaleSpecific=function(){
  //funciton specific to WhaleController
};

Polyfill for Object.create here.



回答2:

I was wondering this myself, and this is what I came up with.

Define the parent

var parentObj = function(parentMemo) {
   console.log(parentMemo);
};

parentObj.prototype = {
    parentCall : function() {
        console.log('parentCall');
    }
}

Define the child

var childObj = function(childMemo, parentMemo) {
   parentObj.call(this, parentMemo);

   console.log(childMemo);
};

_.extend(childObj.prototype, parentObj.prototype, {
    childCall : function() {
       console.log('childCall');
    }
});

Construct a new a child to see the results

var newChild = new childObj("Constructing Child", "Constructing Parent");
newChild.childCall();
newChild.parentCall();

Console Results:

Constructing Child
Constructing Parent
childCall
parentCall 


回答3:

I've read multiple articles on inheritance in JS, but had assumed that the Underscore library had it solved for me.

No, Underscore does have no helper functions for prototypical inheritance. Read the docs on what extend does:

_.extend(destination, *sources): Copy all of the properties in the source objects over to the destination object, and return the destination object. It's in-order, so the last source will override properties of the same name in previous arguments.

Most interestingly, it does not return a function, but its first argument (which is a plain object).

So get back to the articles you've read, and choose a framework that does actually have an inherit function or implement the inheritance yourself - it's not hard.



回答4:

John Resig has a good blog post on implementing Javascript inheritance that may be useful to you as it contains a solution for prototype inheritance, whereas Underscore extend is designed to extend simple Javascript objects.



回答5:

As others have explained, underscore's extend method creates (very) shallow copies of object instances and doesn't preserve prototype chains. However I recently came up against a small library — Compose.js — whose wider remit is to provide a more flexible API for JS's OO properties.

The second example in the readme seems to deal with your use case almost exactly — I believe in your situation it would be invoked as follows:

Zoo.Controllers.WhaleController = Compose( Zoo.Controller, { new: function () {
  // do something
} );


回答6:

You could reuse the extend() function used by backboneJs. Its a quite simple function that uses _.extend() too

http://backbonejs.org/docs/backbone.html#section-208

Then attach it to your Controller prototype so you could do something like:

var MyController = Controller.extend({ /* your attributes, methods */  });

hope this helps