tags:

views:

39

answers:

2

Ok - am trying to create a string library that contains a handful of useful things missing from JavaScript. Here is what I have so far:

function $__STRING__$(in_string) { this.s = in_string; }
$__STRING__$.prototype = {
    uppercase:      function(){this.s = this.s.toUpperCase(); return this;},
    lowercase:      function(){this.s = this.s.toLowerCase(); return this;},
    trim:           function(){this.s = this.s.replace(/^\s+|\s+$/g,""); return this;},
    ltrim:          function(){this.s = this.s.replace(/^\s+/,""); return this;},
    rtrim:          function(){this.s = this.s.replace(/\s+$/,""); return this;},
    striptags:      function(){this.s = this.s.replace(/<\/?[^>]+(>|$)/g, ""); return this;},
    escapetags:     function(){this.s = this.s.replace(/</g,"<").replace(/>/g,">"); return this;},
    unescapetags:   function(){this.s = this.s.replace(/</g,"<").replace(/>/g,">"); return this;},
    underscorize:   function(){this.s = this.s.replace(/ /g,"_"); return this;},
    dasherize:      function(){this.s = this.s.replace(/ /g,"-"); return this;},
    spacify:        function(){this.s = this.s.replace(/_/g," "); return this;},
    left:           function(length){this.s = this.s.substring(length,0); return this;},
    right:          function(length){this.s = this.s.substring(this.s.length,this.s.length-length); return this;},
    shorten:        function(length){if(this.s.length<=length){return this.s;}else{this.left(this.s,length)+"..."; return this;}},
    mid:            function(start,length){return this.s.substring(start,(length+start));},
    _down:          function(){return this.s;},
    contains:       function(needle){if(this.s.indexOf(needle)!==-1){return true;}else{return false;}},
    startswith:     function(needle){if(this.left(this.s,needle.length)==needle){return true;}else{return false;}},
    endswith:       function(needle){if(this.right(this.s,needle.length)==needle){return true;}else{return false;};},
    toString:       function(){return this.s;}
}

function $E(in_string){return new $__STRING__$(in_string);}
String.prototype._enhance   = function(){return new $__STRING__$(this);};
String.prototype._up        = function(){return new $__STRING__$(this);};

It works fairly well, and I can chain commands etc.

I set it up so I can cast a string as an enhanced string these 2 ways:

$E('some string');
'some string'._enhance();

However, each time I want to use a built-in string method, I need to convert it back to a string first. So for now, I put in _down() and _up() methods like so:

alert( $E("hello man").uppercase()._down().replace("N", "Y")._up().dasherize() ); 
alert( "hello man"._enhance().uppercase()._down().replace("N", "Y")._up().dasherize() );

It works fine, but what I really want to do it be able to use all of the built-in functions a string can use. I realize I can just replicate each function inside my object, but I was hoping there was a simpler way.

So question is, is there an easy way to do that?

Thanks -

+3  A: 

You can loop through the methods in String.prototype:

for(var name in String.prototype) {
    if (typeof String.prototype[name] !== "function") continue;
    if (name in $__STRING__$.prototype) continue;

    addToProto(name);
}

function addToProto(name) {
    $__STRING__$.prototype[name] = function() {
        this.s = String.prototype[name].apply(this.s, arguments);
        return this;
    };
}

Note that this will not handle methods like indexOf correctly; you could modify it like this:

$__STRING__$.prototype[name] = function() {
    var retVal = String.prototype[name].apply(this.s, arguments);
    if (typeof retVal === "string") {
        this.s = retVal;
        return this;
    } else 
        return retVal;
};

Also, you should move your explicit methods to the prototype, like this:

 $__STRING__$.prototype.uppercase = function(){this.s = this.s.toUpperCase(); return this;};

EDIT: The for .. in loop will not work correctly (the methods in String.prototype are not enumerable).

Instead, you need to call addToProto manually for each method name, like this:

addToProto('replace');
SLaks
very interesting. trying to wrap my head around what you have written here, but will give it a try. Thanks (will 'accept' if it works as needed of course).
OneNerd
adjusted code to use prototype assignments as suggested, then applied your code (first section) right above the function $E() line, but getting error.If I run this inside firebug, I get only the 2 functions I added manually (_up and _enhance) -- and if I remove those lines of code, I get an empty result:for(var name in String.prototype) { console.log(name); }Any ideas?
OneNerd
Apparently, the methods in `String.prototype` are not marked as enumerable. Therefore, you should call `addToProto` with a hard-coded set of method names. (eg, `addToProto('replace')`)
SLaks
Yikes-ok, that's what I figured. Will give it a go.
OneNerd
Ok - i created an array of function names, then iterated through it to add them - worked fine. Thanks -
OneNerd
A: 
$__STRING__$.prototype = String.prototype;
Tgr
This will not work correctly.
SLaks
Nor will it work if you try the correct way of inheritance for other defined functions: `$__STRING__$.prototype = new String();`.
Harmen
@Harmen: sharing a prototype is perfectly valid if you want the two objects to have the same functions. Of course, a string function won't work when applied to a non-string object - that was stupid of me.
Tgr