3

This is my first stab at OOP, so please bear with me:

(function(){

  var Ship = function(){
    this.passengers = [];
    this.hasAliens = function() {
      return this.passengers.some(function(passenger){
         return passenger.isAlien()
      });
    }        
  }; 

  var Passenger = function(){};
  Passenger.prototype.isAlien = function(){
    return this instanceof Alien;
  };
  Passenger.prototype.board = function(ship) {
    ship.passengers.push(this)
  }

  var Alien = function() { Passenger.call(this); }
  var Human = function() { Passenger.call(this); }
  Alien.prototype = Object.create(Passenger.prototype);
  Human.prototype = Object.create(Passenger.prototype);
  Alien.prototype.constructor = Alien.constructor;   
  Human.prototype.constructor = Human.constructor;  

  var ship = new Ship();   
  var john = new Human();
  var zorg = new Alien();

  //simple testing
  john.board(ship);
  console.log("Ship does not have aliens ", ship.hasAliens()===false);
  zorg.board(ship);
  console.log("Ship has aliens ", ship.hasAliens()===true);

})();

This works fine. However, I'd like to know how to pass the Passenger.isAlien() method to save me that nasty nested anonymous function. I'm trying to do it like this:

  var Ship = function(){
    this.passengers = [];
    this.hasAliens = function(){
      return this.passengers.some(Passenger.isAlien);          
    };
  };

But that gives me "undefined is not a function"

http://jsfiddle.net/WYyxY/

1
  • 2
    There are some flaws in your code. E.g. Alien.prototype = Object.create(Passenger); should be Alien.prototype = Object.create(Passenger.prototype); and instead of assigning the functions inside the Passenger constructor, you should assign them to Passenger.prototype. Regarding your question: You get the error because isAlien is a property of an instance of the constructor function, not the constructor function itself. There is not really a more concise way to do this than using an anonymous function. Commented Dec 4, 2012 at 7:03

2 Answers 2

1

As I said, isAlien is a property of the prototype, i.e. an instance of the constructor function, and not the constructor function itself. Passenger.isAlien is indeed undefined (nowhere in your code is Passenger.isAlien = function....).

There is not really a more concise way to do this. Think about what a callback passed to .some is doing: It has to take an element of the array as argument and then do something with it. In your case you want to execute a method of that element.

One way to call a method and pass the object it should be called on as parameter is to use .call [MDN]. Unfortunately, as with all functions in JavaScript, you cannot just get a reference to Passenger.prototype.isAlien.call, because .call looses its context (it does not know which function it refers to). You'd have to bind it to Passenger.prototype.isAlien first

this.passengers.some(
    Passenger.prototype.isAlien.call.bind(Passenger.prototype.isAlien)    
);

and personally I find that not more readable.

Stick with the anonymous function, your intend is much clearer. Or if you want to, you can let another function create that function:

function callOn(funcName) {
    return function(obj) {
        return obj[funcName]();
    };
}

this.passengers.some(callOn('isAlien'));
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your help, I've refactored the code. What do you think about a top level isAlien = function(passenger){...} and then that would allow return this.passengers.some(isAlien); and Passenger.prototype.isAlien = isAlien;
Passenger.prototype.isAlien = isAlien would not work as you'd expect it to. isAlien in this case accepts an argument, so john.isAlien() would through an error because passenger is not defined in the function. It's basically the same problem, but vice versa. You'd have to call john.isAlien(john) which is not straightforward either.
But if you do something like if(!alien) alien=this then I could provide the context no? jsfiddle.net/WYyxY/2. but I'm still not sure it's more readable.
0

For doing OOP with javascript, I strongly recommend checking out prototypeJS. Your code becomes much more readable, and it also supports inheritance!

Here's a quick look at it

2 Comments

Thanks indieman, I'm aware of some libraries that could help me with this, but I'd like to try it out with straight js.
Inheritance is natively supported by JavaScript. Just sayin' because you make it sound like it does not.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.