1

I'm trying to create a class, and pass it to another class, and i'm running into issues with the prototype. I know I can use bind to get around this, but I can't figure out a way to have the prototype method bound to it's constructor on instantiation. This leaves me with something like this:

foo = new obj(); // has foo.method that depends on "this" being bound to obj
// pass foo.method to bar, with it's context bound to foo
bar = new obj2(foo.method.bind(foo)); //  obj2 uses foo.method as a "callback" internally. ugly. T_T

Here's a contrived example:

/**
* Base horn class. To be shared by cars, clowns, braggads, etc.
*/
var Horn = (function(){
 var Horn = function (noise){
    this.noise = noise;
  };
  Horn.prototype.sound = function(){
    return "*blowing horn* " + this.noise;
  };

  return Horn; // is there a way to bind here?
})();

/**
* Base car class. Needs a horn.
*/
var Car = (function(){
  var Car = function (model, horn) {
    this.model = model;
    this.horn = horn;
  };
  Car.prototype.drive = function(){
    return "i'm driving in my " + this.model + " " + this.horn();
  };
  return Car;
})();

/*
* Visualize output
*/
var term = document.getElementById('term');
term.say = function(message){
  this.innerHTML += message + "\n";
};

// create a horn for cars. 
var carHorn = new Horn('beep beep');
term.say(carHorn.sound()); // *blowing horn* beep beep


// pass the horn to a new Acura
var acura = new Car("acura", carHorn.sound);
term.say(acura.drive()); // i'm driving in my acura *blowing horn* undefined

// Pass the horn to a prius, but bind the horn first
var prius = new Car("prius", carHorn.sound.bind(carHorn)); // whooo bind.
term.say(prius.drive()); //i'm driving in my prius *blowing horn* beep beep

JS Bin

I've done a lot of reading on SO (this post was quite helpful), but I can't seem to find an elegant way to do this.

Also, if i'm going about this in a totally backwards way, let me know.

Community
  • 1
  • 1
Nick Tomlin
  • 28,402
  • 11
  • 61
  • 90
  • 2
    Why don't you pass a `Horn`, not a `Horn` method to the `Car` constructor? Then, you'd use `this.horn.sound()` instead of `this.horn()` – Ian Jun 20 '13 at 22:06
  • *"but I can't figure out a way to have the prototype method bound to it's constructor on instantiation"* Inside the constructor, you can iterate over all prototype methods and assign them bound to the same property name of the instance, i.e. `this[method] = this[method].bind(this)`. Of course this creates a new copy of each method for each instance which kind of defeats the purpose of using `prototype` in the first place. I wouldn't do it. – Felix Kling Jun 20 '13 at 22:08
  • @ian Ideally ``car.horn`` could be any function (even an anonymous one), so ``Car`` can just call it without knowing the function name. – Nick Tomlin Jun 20 '13 at 22:08
  • @NickTomlin I know it's not my place to suggest stuff like this, but if you're using "classes", why not use them everywhere? Sure, it could be any function to call, but you actually have a `Horn` defined, why not use it? In real life, and in your program, I would think there's a "relationship" between cars and horns – Ian Jun 20 '13 at 22:09
  • in Car.prototype.drive instead of calling function from instance of Car, call it while creating new Car("acura", carHorn.sound()); – Givi Jun 20 '13 at 22:12

2 Answers2

1

You can bind a method within a constructor:

var Horn = function (noise){
    this.noise = noise;
    this.sound = this.sound.bind( this );
};

The RHS will read it from the prototype and the LHS will write it directly on the object and it will shadow the one on the prototype when you reference it. You can still reference the unbound version with hornInstance.constructor.prototype.sound or Horn.prototype.sound.

This is usually done when you have no choice though, I.E. when passing a method as an event listener somewhere. In this case you could easily just pass the horn object around.

Esailija
  • 138,174
  • 23
  • 272
  • 326
  • Let's say we were almost synchronous! :) But does your answer assume you still have a prototype method? I don't see why it's needed anymore. – bfavaretto Jun 20 '13 at 22:25
  • @bfavaretto Well it's technically not needed. But some people and I prefer the reduced indentation level and keeping constructors simple. – Esailija Jun 20 '13 at 22:27
  • I can't argue with that, your version does look a lot cleaner. – bfavaretto Jun 20 '13 at 22:30
0

I'd usually pass the whole object, or the function output, as suggested in the comments to the question. However, what you're asking is possible. You just can't have the functions in the prototype, you need a separate (bound) function per instance:

var Horn = (function(){
 var Horn = function (noise){
     this.noise = noise;
     this.sound = function(){
        return "*blowing horn* " + this.noise;
     }.bind(this); // bind here
  };

  return Horn;
})();

http://jsfiddle.net/5xcHG/

bfavaretto
  • 71,580
  • 16
  • 111
  • 150