I'm working on a bit of JavaScript code that will be used to build constructors for "classes" (specifically View Models) for our enterprise-level software. One of the things that I'm doing is establishing a code-enforced design pattern, where the implementer will explicitly define what functions they want exposed by the instance of the class, and the code will add them to the prototype of the constructor, as opposed to properties of each instance of the class. This of course has the advantage of there only being one instance of each of these functions per type, as opposed to an instance for every instance.
Here's a CodePen of most of my examples.
The problem is that under certain conditions, I am dealing with binding loss issues. For example, with this constructor:
function Foo(myName) {
this.firstName = myName;
}
Foo.prototype.greet = function(yourName) {
alert("Hello, " + yourName + ". I am " + this.firstName + ".");
}
...this will work:
var sam = new Foo("Sam");
// Alerts "Hello, Denny. I am Sam."
sam.greet("Denny");
...but this will not:
var sad = new Foo("Sad");
// This changes the context of greet. :(
var sadGreet = sad.greet;
// Alerts "Hello, Denny. I am undefined."
sadGreet("Denny");
This is because when we do var sadGreet = sad.greet we are changing the context of the greet function to window, so this.firstName does not exist when calling sadGreet("Denny").
So, a solution that I came up with was to overwrite the prototype with a property that calls that prototype, wrapping it in Function.prototype.bind():
function Bar(myName) {
this.firstName = myName;
// Here's where the magic happens.
this.greet = Bar.prototype.greet.bind(this);
}
Bar.prototype.greet = function(yourName) {
alert("Hello, " + yourName + ". I am " + this.firstName + ".");
}
var happy = new Bar("Happy");
// Since each instance of Bar overwrites the context for greet, this will work. :)
var happyGreet = happy.greet;
// Alerts "Hello, Denny. I am Happy."
happyGreet("Denny");
My question is this: I assume that Bar is not quite as efficient as Foo, but does this completely void any benefit of declaring greet as a prototype method? Under the covers is it just duplicating greet as a property, anyway, or is my call to bind simply adding a "wrapper" for Bar.prototype.greet? In other words, is this effectively exactly the same as the above definition for Bar?
function Bar(myName) {
this.firstName = myName;
// Here's where the magic happens.
this.greet = function(yourName) {
alert("Hello, " + yourName + ". I am " + this.firstName + ".");
}
}
Bonus points if you can not only answer the question but tell me how to test it!
happyGreet(...)in general to be a mistake. If a function is on an instance, you probably shouldn't go assigning it to a variable, if if you do, you should.bindit there at the assignment location, or dohappyGreet.call(happy, "Denny")[[Prototype]]has used zero additional memory and suffered no performance loss. So what's the point? :-/