Adding methods to Object / Number / String prototype

Disclaimer

  • This thread is supposed to serve as a help for other people encountering similar problems as well as checking whether there are better solutions. I will attach my own solution, but ideas and improvements (besides making it more generic) are welcome.
  • I know that generally, extending the built-in objects is a bad idea. So an assumption for this thread is that there is a good reason and no way around it.

Scenario

As a developer, I want to add a method someMethod to all Javascript objects, wherein the implementation is different for Object, Number and String.

I want the solution to meet the following acceptance criteria:

  • A) The solution works in a browser
    • A1) The solution works in strict mode in case the script is used within a strict context
    • A2) The solution works in non-strict mode because 'use strict'; will be removed during compression in, e.g., the YUI Compressor[1]
  • B) The solution works in node.js
    • B1) The solution works in strict mode (reason see A1)
    • B2) The solution works in non-strict mode for the same reason as in B2, plus strict mode in node.js cannot be activated on function level[2]
  • C) I want other objects to be allowed to override this method
  • D) If possible, I want to have control over whether or not the method shows up in a for .. in loop to avoid conflicts with other libraries
  • E) The solution shall actually modify the prototypes.

[1] Minfication removes strict directives [2] Any way to force strict mode in node?

Answers


Creating a wrapper object (note this is just an example, it is not very robust):

var $ = (function(){
  function $(obj){
    if(!(this instanceof $))
        return new $(obj);

    this.method = function(method){
        var objtype = typeof obj;
        var methodName = method + objtype[0].toUpperCase() + objtype.substr(1);
        typeof _$[methodName] == 'function' && _$[methodName].call(obj);
    }
  }

  var _$ = {};

  _$.formatNumber = function(){
    console.log('Formatting number: ' + this);
  }

  _$.formatString = function(){
    console.log('Formatting str: "' + this + '"');
  }

  _$.formatObject = function(){
    console.log('Formatting object: ');
    console.log(JSON.stringify(this));
  }

  return $;
})();

Usage:

var num = 5;
var str = 'test';
var obj = {num: num, str: str};

var $num = $(num);
$num.method('format');

$(str).method('format');
$(obj).method('format');

Demo


My own solution

While trying to figure this out, I have encountered a few problems causing one or another acceptance criterium to break (e.g. a problem described in [1]). After some time I came up with the following solution which seems to work for me. This can be written in a more generic way, of course.

(function () {
    'use strict';

    var methodName = 'someMethod',
        /** Sample method implementations */
        __someMethod = {
            'object': function () {
                var _this = this.valueOf();

                return ['Object'].concat( Array.prototype.slice.call( arguments ) );
            },

            'number': function () {
                var _this = this.valueOf();

                return ['Number'].concat( Array.prototype.slice.call( arguments ) );
            },

            'string': function () {
                var _this = this.valueOf();

                return ['String'].concat( Array.prototype.slice.call( arguments ) );
            },

            'boolean': function () {
                var _this = this.valueOf();

                return ['Boolean', _this];
            }
        };

    if( Object.defineProperty ) {
        Object.defineProperty( Number.prototype, methodName, {
            value: __someMethod['number'],
            writable: true
        } );

        Object.defineProperty( String.prototype, methodName, {
            value: __someMethod['string'],
            writable: true
        } );

        Object.defineProperty( Boolean.prototype, methodName, {
            value: __someMethod['boolean'],
            writable: true
        } );

        Object.defineProperty( Object.prototype, methodName, {
            value: __someMethod['object'],
            writable: true
        } );
    } else {
        Number.prototype[methodName] = __someMethod['number'];
        String.prototype[methodName] = __someMethod['string'];
        Boolean.prototype[methodName] = __someMethod['boolean'];
        Object.prototype[methodName] = __someMethod['object'];
    }
})(); 

Edit: I updated the solution to add the solution for the problem mentioned in [1]. Namely it's the line (e.g.) var _this = this.valueOf();. The reason for this becomes clear if using

'number': function (other) {
    return this === other;
}

In this case, you will get

var someNumber = 42;
console.log( someNumber.someMethod( 42 ) ); // false

This, of course, isn't what we'd want (again, the reason is stated in [1]). So you should use _this instead of this:

'number': function (other) {
    var _this = this.valueOf();
    return _this === other;
}

// ...

var someNumber = 42;
console.log( someNumber.someMethod( 42 ) ); // true

[1] Why does `typeof this` return "object"?


Need Your Help

Verifying license information without a boolean check?

c# asp.net-mvc licensing

I'm currently using a simple XML file that contains license information along with the data's signature and public key. So far, this method is working great. However, I'm seeing one rather large se...

how to use $.storage for multiple objects

javascript jquery local-storage

I'm building a canvas where I will have multiple objects and images being moved around and I want to save them in local storage to be retrieved on a later viewing.

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.