Search code examples
javascriptprototypeprivate

private prototype from within a class


Say I define a class Template() within which I need to extend String with some prototype to add functionalities to it:

function Template() {
    String.prototype.replaceAll = function(find, replace) {
        return this.replace(new RegExp(find.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"), 'g'), replace);
    }

    this.test = function (){
        return "hello".replaceAll("h", "y");
    }
}

Now if I declare a new Template(), the String.prototype is public:

var myTemplate = new Template();
console.log(myTemplate.test()); // outputs "yello", this is desired
console.log("hello".replaceAll("h", "y")); // outputs "yello", this should not work

Whereas what I would want is for String to go unchanged outside of the Template() class while still being extended from within.

How do you declare a private prototype from within a class in javascript?


Solution

  • In general, you do the opposite; you create a new function, whose prototype is the "class" you want to extend, then you add the new methods to that function;

    function MyString() {
    
    }
    
    MyString.prototype = new String();
    MyString.prototype.replaceAll = function (find, replace) {
        return this.replace(new RegExp(find.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"), 'g'), replace);
    };
    

    ... you can create MyString in whatever scope, so that it is as private or public as you want. Then, inside your scope you create instances of MyString rather than String. This gives you the extra ("private") prototypes you want.

    However, string is a primitive, and primitives don't work like normal objects; specifically, they are autoboxed/ upcast to objects when you call methods on them. This will mean the above is not usable in your situation; as "abc".replaceAll() will always autobox to a String, not MyString; and you can't tell JavaScript to do otherwise.

    Instead, what you should be doing (since you shouldn't modify what you don't own anyway), is creating a helper function which you pass the string into to do your dirty work;

    function replaceAll(str, find, replace) {
        return str.replace(new RegExp(find.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"), 'g'), replace);
    }