James Ko
James Ko

Reputation: 34519

What's the equivalent of type-specific extension methods in JavaScript?

Say you have a class Person. Its definition is the following:

function Person(name, age) {
    this.name = name;
    this.age = age;
}

Now say you have an array of Persons (or people) and you want to find the oldest one. In C#, you would do this via extension methods:

Person Oldest(this IEnumerable<Person> people) =>
    people.OrderByDescending(p => p.Age).First();

// Usage:
var elder = people.Oldest();

What's the equivalent in JavaScript? So far, this is all I was able to come up with:

Array.prototype.oldest = function() {
    return this.slice().sort(function(left, right) {
        return left.age - right.age;
    }).pop();
};

This works well and has the same syntax as C#, but has a few drawbacks:

1) It modifies the prototype of a builtin, which could potentially lead to conflicts with other libraries that do so.

2) It is not type-safe: it's easy to accidentally call on an array that doesn't contain people, which could lead to a runtime error.

Alternatively, you could define it like this:

function oldest(people) {
    // ...
}

But then you would have to call it like oldest(people) instead of people.oldest(), which would hamper readability.

Is there any type-safe, syntactically "nice" way to do this in JavaScript that I'm missing?

Upvotes: 0

Views: 387

Answers (3)

Nina Scholz
Nina Scholz

Reputation: 386654

Another approach with a prototype of sorting. Person.prototype.ageCompare checks if the parameter is instance of Person and throws an error if not.

function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.ageCompare = function (b) {
    if (!(b instanceof Person)) {
        throw 'b is no instance of Person!';
    }
    return this.age - b.age;
};


var peoples = [new Person('Tim', 31), new Person('Jane', 32), new Person('Frank', 28), new Person('Sue', 31), new Person('Baby', 2)];
peoples.sort(function (a, b) { return a.ageCompare(b); });
document.write('<pre>' + JSON.stringify(peoples, 0, 4) + '</pre>');

Upvotes: 0

bri
bri

Reputation: 3030

Because js isn't type safe, you're better off implementing that method as oldest(people) ...

Just think of it as a decorator pattern - a legit approach to projecting functionality into an unknown set of related entities.

Then - you can do type-checking within your function.

You could namespace it to make the syntax more "obvious" if that's your concern I guess:

personList(persons).getOldest()

As one objective-c-ish example.

Then, you can add your type-specific checking to the creator for personList:

function personList(persons) {

     //iterate over persons and make sure 
     //it's valid. return undefined or {} if not. 

     return {
         getOldest: function() {
              // insert clever code here ...
              persons.slice().dice().puree();
          },
          getYoungest: function() {
             //even more clever code here ...
          }
      }

}

With that approach, the code

PersonList(cats).getOldest()

With throw a 'getOldest undefined' error -- which makes it easy to spot the problem when you have 11,000,000 lines of js code to sort through.

It has the added advantage of making your c# inner-child feel more at home (with a js file called "ArrayExtensions.Persons.js" in your project folder). Feels like "chicken soup for the c# soul," right?

Upvotes: 2

Oriol
Oriol

Reputation: 288250

You can try different approaches:

  • If you only have one array of Persons in your program, you can add the method as an own property of the array.

    var people = [person1, person2];
    people.oldest = function() {
      // Find the oldest person in this array
    };
    // ... later
    people.oldest();
    
  • You can define your method as an static property of Person, and pass the array of Persons as an argument:

    Person.oldest = function(people) {
       // Find the oldest person in an array
    };
    var people = [person1, person2];
    Person.oldest(people);
    
  • Extend Array (requires ES6)

    class People extends Array {
      oldest() {
        // Find the oldest person in a People collection
      }
    }
    var people = new People();
    people.push(person1, person2);
    people.oldest();
    

Upvotes: 1

Related Questions