On this and new
29 April, 2014This is the pattern that a lot things recommend
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.name = function() { return this.firstName + " " + this.lastName;}
this.printName = function() { console.log("my name is", this.name())}
}
var george = new Person("George", "Mauer");
What do you think that new
does? It’s not what you think. This is what new
actually does
var fn = new Function();
fn.__proto__ = Person; //chrome only - each browser has a different syntax for this step
var res = Person.call(fn, firstName, lastName);
return (res === undefined) ? fn : res;
Huh? But that’s insane…..exactly.
What about this
? How can that be confusing.
The problem is that despite what you think this
doesn’t reference scope. It is really just a parameter like any other. This becomes a lot more clear if you realize that
myFunc(1,2,3);
is really just a facade for how javascript really invokes methods
myFunc.call(whateverThisIs, 1, 2, 3);
So all that this
is, is just the first parameter passed into .call()
. That’s it - no magic, no language-support, it’s just a parameter.
Worse, if you use the simpler syntax, javascript will pick what the value of that parameter should be at runtime. The rules aren’t terribly complex but they’re often not what you want. As an example:
george.printName(); //equivalent to george.prototype.printName.call(george)
var printName = george.printName();
printName(); //equivalent to george.prototype.printName.call(window);
And it gets wierder if you consider strict mode.
A lot of libraries also take guesses at what this
should be.
$('button').click(george.printName); //this won't work because "this" is the button element and doesn't have name()
this
is therefore a parameter like any other and by using it you’re trusting people to always pass in the correct value. And other people know less about this
than you do!
so you could always use the fn.call()
syntax. Or you could
Not Use this or new
The same code but without these keywords:
function createPerson(firstName, lastName) {
var _p = {
firstName: firstName
,lastName: lastName
,name: function() { return _p.firstName + " " + _p.lastName}
,printName: function() { console.log("My name is", _p.name())}
};
return _p; // I like an underscore prefix to indicate "this is what I'm buiding here"
}
var betterGeorge = createPerson("George", "Mauer");
And bam! No more wierd implicit parameters, callbacks and everything else works great forever!
To Be Fair
Sometimes you can’t get away from it. If you’re in a jQuery callback for example you might have to use this
because that’s how the framework works. But please, please don’t write your own code to use it.
Also, there ARE a few benefits to the new
/this
thing.
- In some cases referencing
this
is slightly faster than referencing a variable in a parent scope. This difference is very slight and basically matters only if you’re writting the next underscore or jQuery. Your project doesn’t need it. Seriously Mario, your bottleneck is in another castle. - If you need to support a browser without
Object.create
there is no other way to wire up prototype inheritence cross-browser. This is another thing that unless you’re building an extensible framework you probably don’t need. If you need to share properties or methods every library has a version of the extend helper that will work for this. - Finally, there’s the fact that when logged to a console, things with a prototype look slightly nicer and are labeled as their prototype rather than as “Object”. This is valid but a tiny, tiny issue.
As a whole these benefits certainly don’t excuse introducing two whole new concepts into your code, especially concepts that are as confusing and poorly understood as new
and this
.
I think it is probably only fair to amend that I’m usually opposed to javascript class systems in general. They require the use of new
(though not necessarily of this
- I’ll write up how later)
and between function composition and the extend pattern are generally not necessary for either organizing or building performant code in 90% of cases.