views:

49

answers:

2

I'm on my way through Object Oriented Javascript, and I can't help but feel I've missed the boat on a given exercise. So what I'm looking for here is pointers on how I can improve my code or understanding of Constructors. Here was the challenge:

Imagine the String() constructor didn't exist. Create a constructor function MyString() that acts like String() as closely as possible. You're not allowed to use any built-in string methods or properties, and remember that String() doesn't exist. You can use this code to test your constructor:

--

var s = new MyString('hello');

s.length(); s[0]; s.toString(); s.valueOf(); s.charAt(1); s.charAt('2'); s.charAt('e'); s.concat(' world!'); s.slice(1,3); s.slice(0,-1); s.split('e); s.split('l'); s.reverse();

And here was my response, which fails on one or two accounts, but I'm more interested in the actual structure of it. Am I completely off-base? Is there somewhere I can view the actual String constructor implemented by browsers to compare?

function MyString(string){

    a = string.split("");

    this.length = a.length;

    this.toString = function(){
        return a.join("");
    };

    this.valueOf = function(){
        if(a.length > 0 && string !== 0 && string !== false && string !== undefined){
            return true;
        } else {
            return false;
        }
    };

    this.charAt = function(index){
        return a[index];
    };

    this.concat = function(addition){
        return string + addition;
    };

    this.slice = function(begin, end){
        var a2 = new Array();

        if(end < 0){
            end = parseInt(a.length) + end;
        }
        for(y = begin;y<end;y++){
            a2 += a[y];
        }

        return a2;
    };

    this.split = function(splitter){
        spPos = parseInt(a.indexOf(splitter));
        var split1 = a.slice(0,spPos);
        var split2 = a.slice(spPos + 1, a.length);
        var joined = new Array();
        return joined.concat(split1.join(""), split2.join(""));
    };

    this.reverse = function(){
        var ar = a.reverse();
        return ar.join("");
    };

    return this;
}

I'm headed to bed, but I'll be up and responding in the morning. Thanks so much for any guidance you can give on this issue.

A: 

Firstly, browsers generally have the String implementation in some kind of native code. You won't be able to view that code.

Considering that you are keen on improving your code, is it ok for you not make 'a' private, or may be at least to have a function that returns a. This depends on your requirements basically. The reason I ask is that it is really important to note the number of functions created each time your constructor is called. To avoid this you should move these functions into the prototype of the MyString function, so that the functions are used by all instances of MyString. That way you won't be creating multiple instances of the same function. But again this depends on whether you are ok letting 'a' be accessible outside the function (but at the same time it makes a huge difference in the number of function objects created).

Kartik
Kartik, wouldn't laurencek's solution solve the issue of not returning a and taking advantage of prototypes? I'm not sure of a reason I'd need to keep a private, but I think this explanation provides the best of both worlds.
Joshua Cody
@Joshua - Well, I did not mean it has to be kept private, I was just making sure if it's a requirement, but again laurencek's solution does not talk about that. In fact if we just go by what's shown in that example things won't work the way you are assuming. If 'a' is declared without 'var' inside the constructor you are typically making it a global variable, and probably changing the value of some global variable 'a' that already exists. And that's how he is able to access it inside the prototype functions, which is not good at all. [continued..]
Kartik
You could still make his solution work if you wrap both the constructor and the prototype declaration inside another function, where a is declared with var - function createMyStringCtr(){ var a; var MyString = function(str){....a=str.split("")} MyString.prototype = {....} return MyString; }
Kartik
Kartik, this is a great point. On the issue of whether or not to allow 'a' to remain private, do you have any guesses as to how browsers might implement this? As a is simply the conversion of the string to an array, I see no method for it to remain private. So perhaps rather than polluting the global namespace, it would be better to simply return the array, then prototype out the requisite methods?
Joshua Cody
I am not sure if I understand what you mean by "how browsers implement this". Anyways, I agree that 'a' need not remain private really. You could set the array to something like this.array in the constructor, so that it's available for the required methods defined in prototype.
Kartik
Actually I meant to use a as this.a. Just missed it when copying from the original post.That would be better then wrapping the whole thing in a new function. Wouldn't that just make using prototype to limit the overhead of creating the new object a bit pointless.
laurencek
Ok, now that function would only be called just once to create the constructor function and the prototype object. In fact it can be treated more as an anonymous function that is just used once. This technique can be used to create and use private variables that are still available to functions defined in the prototype, but can't be accessed/modified externally.
Kartik
+1  A: 

A quick point about the structure of your class, as mentioned before.

Your class is written in such a way so that all the methods are inside the constructor. So when you initialise a new class all the methods are regenerated. Thus if you have more than one instance of class MyString on the page this won't be the most efficient technique. You should consider using the prototyping technique instead. So your constructor would simply become:

function MyString(string){
    a = string.split("");
    this.length = a.length;
}

Your methods are then declared outside the constructor and as such are not regenerated each time a new instance of MyString is created.

MyString.prototype.toString = function(){ return a.join(""); }

MyString.prototype.charAt = function(index){ return a[index]; }
laurencek