Implementing instance members/methods in JavaScrip

2019-08-02 15:39发布

问题:

This question stems from a problem I was trying to solve regarding the ability to have "private" instance variables in JavaScript. You may want to read this, prior to my question.

For the sake of completeness, I have illustrated my entire problem, before asking the question. My hope is that this will provide a complete example of how to implement instance members and methods correctly in JavaScript, and for any developer who lands here, to understand the pitfalls of the various implementations.

Consider the following JavaScript object:

var MessageBox = (function() {
    function MessageBox(message) {
        this.message = message;
    }

    MessageBox.prototype.Show = function() {
        alert(this.message);
    }
})();

This object was modelled using TypeScript, and can be used as follows:

var msg1 = new MessageBox("Hello World");
msg1.Show(); // alerts "Hello World"

var msg2 = new MessageBox("Bye World");
msg2.Show(); // alerts "Bye World"

But I can still call:

msg1.message; // "Hello World"
msg2.message; // "Bye World"

So clearly this.message is NOT private.

Now consider the following JavaScript object:

var MessageBox = (function() {
    return function MessageBox(message) {
        var message = message;

        MessageBox.prototype.Show = function() {
            alert(message);
        }
    }
})();

This is just a modified version of the TypeScript based MessageBox object.

var msg1 = new MessageBox("Hello World");
msg1.Show(); // alerts "Hello World"

var msg2 = new MessageBox("Bye World");
msg2.Show(); // alerts "Bye World"

but wait...I'm about to throw a spanner in the works!

var msg1 = new MessageBox("Hello World");
var msg2 = new MessageBox("Bye World");
msg2.Show(); // alerts "Bye World"
msg1.Show(); // alerts "Bye World" ... wait, what!?

msg1.message // undefined
msg2.message // undefined

So I can no longer gain access to the message variable, but now, every new instance overwrites the last instances message.

Bear with me, this is the last JavaScript object to consider:

var MessageBox = (function() {
    return function MessageBox(message) {
        var message = message;

        this.Show = function() {
            alert(message);
        }
    }
}();

The above object no longer implements Show() on the prototype, so now I can:

var msg1 = new MessageBox("Hello World");
var msg2 = new MessageBox("Bye World");
msg2.Show(); // alerts "Bye World"
msg1.Show(); // alerts "Hello World"

msg1.message // undefined
msg2.message // undefined

Great! Now I have private variables, and they don't overwrite each other!

So, finally the question: What is the difference between:

MessageBox.prototype.Show = function() {
}

and

this.Show = function() {
}

回答1:

The question that you eventually get to has a simple answer: setting the function on the prototype means it can get called from any instance, while setting the function on the instance means it can be called only from that instance. Either way can access properties on the instance, but the thing that you're finding complicated is that either way the function only has access to local variables in the scope where it was declared or in containing scopes.

The following is the first way that came to mind to provide both private instance and private prototype variables.:

var MessageBox = (function() {
    var privateProtoVar = "Hello";

    function MessageBox(message) {
        var privateInstanceVar = message;

        this.instanceMethod = function() {
            alert(privateInstanceVar); // Can access private instance var
            alert(privateProtoVar);    // Can access private prototype var
        }
    }        
    MessageBox.prototype.Show = function() {
        alert(privateProtoVar); // Can access private proto var
        // but can't access privateInstanceVar
    }    
    return MessageBox;
})();

var msg1 = new MessageBox("1"),
    msg2 = new MessageBox("2");

msg1.instanceMethod();  // "1", "Hello"
msg2.instanceMethod();  // "2", "Hello"
msg1.Show();            // "Hello"
msg2.Show();            // "Hello"

The variable privateInstanceVar is accessible by any function declared inside the inner MessageBox() function, but not from functions on the prototype unless they're declared within that same scope (which I don't do in the above example).

If you add additional methods to instances later, i.e., outside the above structure then those methods do not have access to the private var because they're declared in a different scope:

msg1.newMethod = function() {
   alert(privateInstanceVar);
}
msg1.newMethod(); // error

Demo: http://jsfiddle.net/SSEga/



回答2:

With MessageBox.prototype.Show you are overriding the Show function for any instance every time you call new MessageBox(). Therefore every instance always shares the same Show function with the same scope.