Matthew Patrick Cashatt Matthew Patrick Cashatt - 7 months ago 12
Javascript Question

How do I write an extension method in JavaScript?

I need to write a few extension methods in JS. I know just how to do this in C#. Example:

public static string SayHi(this Object name)
{
return "Hi " + name + "!";
}


and then called by:

string firstName = "Bob";
string hi = firstName.SayHi();


How would I do something like this in JavaScript?

Answer

In that specific case, you'd assign your method to String.prototype, like this:

String.prototype.SayHi = function SayHi() {
    return "Hi " + this + "!";
};

or better as a non-enumerable property using Object.defineProperty (ES5 and higher, so basically, everything but IE8 and earlier):

Object.defineProperty(String.prototype, "SayHi", {
    value: function SayHi() {
        return "Hi " + this + "!";
    }
});

JavaScript is a prototypical language. That means that every object is backed by a prototype object. In JavaScript, that prototype is assigned either by the constructor function for the object, or by the new(ish) ECMAScript5 Object.create function.

In the former case (the constructor function), the prototype assigned to an object is defined by the prototype property of the constructor function. So if you have a constructor function called Foo:

function Foo() {
}

...then the statement

var f = new Foo();

...assigns Foo.prototype to the f instance as its prototype object. Thus:

function Foo(b) {
    this.baz = b;
}
Foo.prototype.bar = function bar() {
    console.log(this.baz);
};

var f = new Foo("Charlie");
f.bar(); // logs "Charlie"

So in your example, since firstName is a String instance (actually a string primitive, but don't worry, it gets automagically promoted to a String instance whenever necessary), its prototype is String.prototype, so adding a property to String.prototype that references your SayHi function makes that function available on all String instances.

As DougR pointed out in a comment, one difference from C# extension methods is that C#'s extension methods can be called on null references (if you have a string extension method, string s = null; s.YourExtensionMethod(); actually works). This isn't true with JavaScript; null is its own type and there's no prototype to extend for it.


A quick note about the function names in those examples, e.g.

Object.defineProperty(String.prototype, "SayHi", {
    value: function SayHi() {
// HERE ------------^
        return "Hi " + this + "!";
    }
});

Foo.prototype.bar = function bar() {
// AND HERE -----------------^
    console.log(this.baz);
};

That form, where we're using a function expression with a name in it (a "named function expression," aka NFE) used to be famous for having issues (on IE8 and earlier; and on really, really old versions of a couple of other browsers). With everything else being dead and IE8 being nearly dead, there's no need to worry about NFEs anymore. (And the above would be okay even in IE8.)

Comments