Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass an object method to an array method in javascript

Tags:

javascript

oop

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/

like image 252
methodofaction Avatar asked Mar 16 '26 03:03

methodofaction


1 Answers

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'));
like image 77
Felix Kling Avatar answered Mar 19 '26 00:03

Felix Kling