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?
Home >JavaScript String Extension Methods. It happens sometimes when writing code and cope with an object that you need a functionality namely a method which the object doesn't expose.
An extension method must be defined in a top-level static class. An extension method with the same name and signature as an instance method will not be called. Extension methods cannot be used to override existing methods. The concept of extension methods cannot be applied to fields, properties or events.
In C#, the extension method concept allows you to add new methods in the existing class or in the structure without modifying the source code of the original type and you do not require any kind of special permission from the original type and there is no need to re-compile the original type.
JavaScript doesn't have an exact analogue for C#'s extension methods. JavaScript and C# are quite different languages.
The nearest similar thing is to modify the prototype object of all string objects: String.prototype
. In general, best practice is not to modify the prototypes of built-in objects in library code meant to be combined with other code you don't control. (Doing it in an application where you control what other code is included in the application is okay.)
If you do modify the prototype of a built-in, it's best (by far) to make that a non-enumerable property by using Object.defineProperty
(ES5+, so basically any modern JavaScript environment, and not IE8¹ or earlier). To match the enumerability, writability, and configurability of other string methods, it would look like this:
Object.defineProperty(String.prototype, "SayHi", { value: function SayHi() { return "Hi " + this + "!"; }, writable: true, configurable: true });
(The default for enumerable
is false
.)
If you needed to support obsolete environments, then for String.prototype
, specifically, you could probably get away with creating an enumerable property:
// Don't do this if you can use `Object.defineProperty` String.prototype.SayHi = function SayHi() { return "Hi " + this + "!"; };
That's not a good idea, but you might get away with it. Never do that with Array.prototype
or Object.prototype
; creating enumerable properties on those is a Bad Thing™.
Details:
JavaScript is a prototypical language. That means that every object is backed by a prototype object. In JavaScript, that prototype is assigned in one of four ways:
new Foo
creates an object with Foo.prototype
as its prototype)Object.create
function added in ES5 (2009)__proto__
accessor property (ES2015+, only on web browsers, existed in some environments before it was standardized) or Object.setPrototypeOf
(ES2015+)So in your example, since firstName
is a string primitive, it gets promoted to a String
instance whenever you call a method on it, and that String
instance's prototype is String.prototype
. So adding a property to String.prototype
that references your SayHi
function makes that function available on all String
instances (and effectively on string primitives, because they get promoted).
Example:
Object.defineProperty(String.prototype, "SayHi", { value: function SayHi() { return "Hi " + this + "!"; }, writable: true, configurable: true }); console.log("Charlie".SayHi());
There are some key differences between this and C# extension methods:
(As DougR pointed out in a comment) C#'s extension methods can be called on null
references. If you have a string
extension method, this code:
string s = null; s.YourExtensionMethod();
works (unless YourExtensionMethod
throws when it receives null
as the instance parameter). That isn't true with JavaScript; null
is its own type, and any property reference on null
throws an error. (And even if it didn't, there's no prototype to extend for the Null type.)
null
.) That isn't true in JavaScript: If you change the prototype of a built-in, that change is seen by all code in the entire realm you do that in (a realm is the global environment and its associated intrinsic objects, etc.). So if you do this in a web page, all code you load on that page sees the change. If you do this in a Node.js module, all code loaded in the same realm as that module will see the change. In both cases, that's why you don't do this in library code. (Web workers and Node.js worker threads are loaded in their own realm, so they have a different global environment and different intrinsics than the main thread. But that realm is still shared with any modules they load.)¹ IE8 does have Object.defineProperty
, but it only works on DOM objects, not JavaScript objects. String.prototype
is a JavaScript object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With