

Javascript Constructors and Prototypes - nccong
http://tobyho.com/2010/11/22/javascript-constructors-and/

======
GeneralMayhem
This article does two things that are very dangerous, and never mentions the
reasons why they're dangerous.

* Messing with Function's prototype is very strongly frowned upon. It's tantamount to setting global variables. At the very least, give that definition an if (!Function.new) guard to prevent redefining another implementation that another script (or the browser!) has already given.

* Adding methods in the constructor is awful even if you're not using inheritance, because it burns memory like crazy. If you define sayHi on Person.prototype, then every Person gets a reference to the _same_ sayHi method. If you define this.sayHi in the constructor, then each Person you create gets _its own copy_ of the function, making every Person heavier in memory. Not a big deal for a simple console.log, but if you have more complicated objects with a few dozen methods that you're using a bunch of you can really make things chug.

~~~
ludwigvan
> If you define this.sayHi in the constructor, then each Person you create
> gets its own copy of the function, making every Person heavier in memory.

Does anyone know whether this is true for more sophisticated JS engines like
v8? I seem to recall reading something like that, that it detects that it is
the same function, but I might be misremembering it.

~~~
GeneralMayhem
I don't know, but I would suspect it's still the case, because doing anything
else would require a lot of analysis of the code's behavior. In order to
safely merge all those definitions into one, the engine would have to know:

* That the function in question never accesses anything in its closure that could be different for each construction

* That hasOwnProperty() is never called on the object to check which way the method is declared (alternatively, remember that it had done this optimization, and spoof the return value)

* That the function is never used as a constructor. Otherwise, Person could update sayHi's prototype and wind up changing its effects for all Persons, where it was supposed to create a customized inner class.

And if you think any of these are easy, remember that you can get a pointer to
that function via the following:

    
    
      var a = 'say', b = 'hi'
      var sayHi = (new Person)[a + b]

------
gregorkas
I wanted to read this because I believe it's a great article, but the fact
that he doesn't use semicolons pretty much turned me off.

While I was looking at the JavaScript code my brain almost exploded.

~~~
pests
Did you see the article he linked about why he doesn't use them?

[http://mislav.uniqpath.com/2010/05/semicolons/](http://mislav.uniqpath.com/2010/05/semicolons/)

Not reading an article because of that would be like not reading other code
because it uses 2 spaces for tabs instead of 4. Makes no difference to the
language, only to you.

~~~
wldlyinaccurate
The thing is, semicolons _do_ make a difference with JavaScript.

It's now 404ing, but there was a pretty well-known argument on one of Twitter
Bootstrap's GitHub issues[1]. The Bootstrap guys didn't use semicolons, and it
caused problems when minifying the code. It's an edge case, I know, but it
_was_ a problem.

I never understood the whole anti-semicolon thing anyway. It just seems really
hipster to me. Use CoffeeScript, if you don't want semicolons.

    
    
      [1] https://github.com/twitter/bootstrap/issues/3057

~~~
marrs
Why does it seem hipster to use a language feature? Is it hipster to use null
coalesce as well?

The article linked above explains the reasons to omit semi-colons very well. I
would personally prefer if JS forced you to terminate all statements with a
semi-colon to avoid any ambiguity, but there you go.

Speaking of ambiguity caused by whitespace, Coffeescript is a 1st degree
offender for this. All you have to do is indent the wrong block of code, and
you completely change the scope of a nested function, and you have no visual
indication of your mistake whatsoever.

~~~
tlrobinson
CoffeeScript certainly has its syntax quirks, but after some forced time using
it I've found I can't go back to happily writing plain old JavaScript.

The first week or two you'll regularly need to peek at the compiled source to
be sure it's doing what you want, but you'll quickly get a feel for it.

Also, a lot of the weird ambiguous cases are disambiguated by including
parenthesis, so if you're unsure just include the parens and you should be ok.

~~~
derefr
Oddly,

> Also, a lot of the weird ambiguous cases are disambiguated by including
> parenthesis, so if you're unsure just include the parens and you should be
> ok.

this is _exactly_ the parallel I was going to make to the discussion we're
currently having here. People who want semicolons on every line, even when
they don't matter, are the same people who want to parenthesize every infix
operation, even when the natural precedence the expression has without
parentheses is already correct. I'm not sure I understand it in either case--
are you afraid that someone might edit the code without understanding the
"implicit defaults" of the language's syntax? Why are you letting that person
near your codebase?

~~~
lmm
Some programmers are good at remembering large numbers of arbitrary rules.
Others aren't, and I've worked with good and bad programmers of both kinds.

So yes, I'm worried that a colleague might read the code and not know what the
precedence is, and they will have to waste their time looking it up
(thankfully some IDEs now have an command to add parentheses quickly, but it's
still a distraction from their actual task).

Pretty much all languages have some features that are more confusing than
helpful, and good codebases avoid using those features (whether via formal
policy or not). IMO most precedence rules fall into that category; it would be
better if e.g. "a && b || c" were a syntax error until bracketed properly.

~~~
derefr
Why would they be looking up the precedence?

If the code currently works, then they can _read_ it, and infer that whatever
precedence the operators have is the _correct_ one for producing the result
the code produces. If "a + b * c" is producing 17 where (a=2,b=3,c=5), then
you know that your language makes multiplication precede addition.

If the code doesn't currently work, then they'll have to figure out via some
external method (looking up the original formula used in the code, say) what
the precedence _needs_ to be, in order to parenthesize to _make_ it work.

On a separate note,

> it would be better if e.g. "a && b || c" were a syntax error until bracketed
> properly.

this reminds me of the horribly-confusing practice of using "a && b || c" to
mean "a ? b : c" in shell-scripting. It _almost_ works, too... unless
(a=true,b=false), in which case you unintentionally get the side-effects of c.

~~~
lmm
>If the code currently works, then they can read it, and infer that whatever
precedence the operators have is the correct one for producing the result the
code produces. If "a + b * c" is producing 17 where (a=2,b=3,c=5), then you
know that your language makes multiplication precede addition.

By that logic why bother using a font in which * and + look like different
symbols? Heck, why read the code at all? If the code is working you can infer
what it must be doing by observing what comes out when you feed it different
inputs.

You read code precisely because you don't know what it does for every input,
or don't know how it implements the algorithm; you want to be able to look at
a line and see what it does, without having to fire up a repl and run through
several examples. I mean, the idea that code should be readable - i.e. that
you should be able to tell what a given line of code does without having to
run it or look it up - is about as fundamental a good coding principle as it
gets.

~~~
derefr
There is a fundamental difference.

When you _read_ "a + b * c"\--and then test your assumption of what it does in
a REPL--the result is a _learning moment_ where that knowledge now sticks to
you; from then on, you _know_ which of the two operators come first. You only
have to do it once.

On the other hand, "using a font in which * and + look like [the same symbol]"
means never being able to recognize the pattern, which means never learning
anything and having to check every time.

Also,

> the idea that code should be readable - i.e. that you should be able to tell
> what a given line of code does without having to run it or look it up - is
> about as fundamental a good coding principle as it gets.

I would agree that that is a good coding principle _for low-level C /C++/Java
code_; in these languages, you can't separate the abstract meaning of code
from its implementation, so it's better to just keep the two things together.

But on the other hand, the equivalent coding principle for Lisp is "create a
set of macros which form a DSL to perfectly articulate your problem domain--
and then specify your solution in that DSL." The equivalent for Haskell is
"find a Mathematical domain isomorphic to your problem domain; import a set of
new operators which match the known Mathematical syntax of that domain; and
then state your problem in terms of that Mathematical domain by using those
operators." In either of _these_ cases, nobody can really be expected to just
jump in and read the code without looking something, or a lot of somethings,
up.

This isn't because the code is "objectively bad", but rather that it has
externalized abstractions which in a lower-level language like C would have to
be written explicitly into the boilerplate-structure of your code. These are
just two different cultures.

~~~
lmm
>When you read "a + b * c"\--and then test your assumption of what it does in
a REPL--the result is a learning moment where that knowledge now sticks to
you; from then on, you know which of the two operators come first. You only
have to do it once.

If you're good at memorizing essentially arbitrary rules then you only have to
do it once. For +/* you can argue that the precedence is standard in the
domain language (mathematics), but in C-like languages there are often a dozen
operators with their order, and there's no natural reason why >> should be
higher or lower than /.

>The equivalent for Haskell is "find a Mathematical domain isomorphic to your
problem domain; import a set of new operators which match the known
Mathematical syntax of that domain; and then state your problem in terms of
that Mathematical domain by using those operators." In either of these cases,
nobody can really be expected to just jump in and read the code without
looking something, or a lot of somethings, up.

Sure - code is written in the language of the domain. If you don't know what
an interest rate calculation is then even the best-written implementation
won't be readable to you. But that domain terminology should make sense
(indeed a large part of understanding a field is understanding its
terminology), whereas many language precedence rules don't - they're
completely arbitrary, there's no way to derive them from first principles if
you forget.

I'd argue that a language where you don't have to memorize a precedence list
(such as Lisp - the precedence is always explicit from the syntax, one simply
can't write (+ a b * c - so I'm kind of surprised you mention it). I'm
surprised if Haskell is different in this regard) is, all other things being
equal, better than a language where you do have to memorize a precedence list.
But rather than having to write a whole new language, it's more lightweight to
form a "dialect" by declaring "we will write C (or whatever), but only use
constructs that do not require memorizing the precedence table".

------
M4v3R
I used the prototype mechanism several times when debugging a script that I
couldn't easily change source code for. I just typed:

SomeClass.prototype.someMethod

Which outputs the methods source. Then you can add some debug code like
console.log(xxx) or even fix a simple bug, copy the whole thing and set it
again:

SomeClass.prototype.someMethod = function ...

Voila! Hot code push without an IDE. While that's obviously not ideal, it
certainly works in some cases.

~~~
GeneralMayhem
My favorite trick along those lines:

    
    
      var cache = SomeClass.prototype.someMethod;
      SomeClass.prototype.someMethod = function () {
        debugger; // or `throw "stacktrace"`
        cache.apply(this, arguments)
      }
    

Inserts a stack trace right in the middle of executing someone else's code,
which is nice for tracking down why/when that function gets called if
documentation is poor, without changing functionality.

------
acjohnson55
I'm bracing for the downvote, but I'm going to say this anyway: are we all
taking crazy pills?

I understand that we're more or less stuck with JavaScript, but this is nuts.
JS has got to be one of the least intuitive, cobbled together languages I've
ever had to work with. In what other language are people still having holy
wars over how to separate statements?

Why the heck would a constructor be a regular function if calling it without
new pollutes the global namespace? JS gives us prototypes so that we can have
inheritance! Great, but that doesn't actually give us a simple ability to call
super. Using functions as everything is fine and good, until you have to
shoehorn all the functionality you actually need into mysterious constructs
like prototype, new, and apply. When the symmetries are so half-assed, at some
point, it seems to me to make way more since to stop overloading the same
language construct and make separate constructs for separate uses.

And don't even get me started on implicit variable declaration, function
hoisting, the double-equals, for...in, the necessity of self-calling
functions, etc.

Thank God we have libraries and alternative syntaxes now that more or less
smooth over these issues and coerce the programmer into writing reasonable JS
code, because green field JS is a complete quagmire. Are we seriously
incapable of doing better?

~~~
wwweston
> In what other language are people still having holy wars over how to
> separate statements?

Probably most of the languages in popular use. It's just that the fight is
usually directed towards another language.

JS happens to be one of the minority where the standard allows some
flexibility and debate about usage.

> mysterious constructs like prototype, new, and apply

They're well-defined, so they're not particularly mysterious if you take the
time.

> Are we seriously incapable of doing better?

Really depends on how much effort you're willing to invest in learning
JavaScript, instead of trying to write the code you're used to from
$OTHER_OO_LANGUAGE. Everyone unwilling to do that is probably never going to
be capable of doing better.

This isn't to say there's anything wrong with finding another familiar set of
abstractions friendly or personally productive, but personally, I find
greenfield JS is pretty fun, flexible, and capable.

Then again, I think the same thing about Perl (which everyone _knows_ is one
of the worst languages ever), so YMMV.

~~~
acjohnson55
Maybe mysterious wasn't the best choice of word, but my point is that it's
quite possible for something to be well-defined and yet poorly usable.

I long since gave up on trying to write the code I'm used to in other OO
languages in JS. When I'm in JS Land, I put a lot of effort into using best
practices. I just find it frustrating that so many of those best practices do
not at all seem to adhere to any sort of principle of least surprise. And I
wouldn't say that my problem is being too tightly bound to a particular OO
paradigm; I've had great fun programming in functional styles and Prolog.

------
wwweston
"Someone pointed out though, that you can prevent this polluting of the
namespace (those are just big words for creating global variables) by using
this trick:

    
    
         function Person(name){
            if (!(this instanceof Person))
                return new Person(name)
            this.name = name
         }"
    

This is useful for situations where you might want to be able to create
anonymous objects and call a series of methods on them, which can be a nice
API as any user of jQuery knows.

It's also an arguably cleaner way of achieving the constructor+apply thing he
does later -- no global modification of the Function prototype.

A lot of the time, though, I'll just consider this my default "class"
definition:

    
    
        function Person (args) { this.init(args); }
    

If you want to be warned (or want others to be warned) when Person is called
w/o new, this will do it (unless someone has defined init on the global
namespace, so avoid that).

This also makes it easier to hand around the method that does the actual
construction, which is helpful for cases like constructor + apply:

    
    
        var p = new Person;
        p.init.apply(p,args);
    

(again, without global modification of the Function prototype) or for cases
where you want to refer to "superclass" methods down an inheritance hierarchy.

------
alexdf
>>Cat.prototype = new Mammal()

This is only useful if Mamaml has only methods and no properties which does
not happen that often.

>> Cat.prototype.constructor = Cat

I never saw a practical usage of this one :-)

I think these days the standard way of doing inheritance should be ->
MyClass.prototype = Object.create(baseClass.prototype)

------
raju
I wrote a similar (2 series) article a while back that attempts to explain the
same thing, but with a few diagrams. I know sketching out what was going on
really helped me ...

[http://www.looselytyped.com/blog/2012/08/18/on-prototypal-
in...](http://www.looselytyped.com/blog/2012/08/18/on-prototypal-inheritance/)

[http://www.looselytyped.com/blog/2012/08/22/on-prototypal-
in...](http://www.looselytyped.com/blog/2012/08/22/on-prototypal-inheritance-
part-ii/)

------
itsbits
oh God!! why do some people have problem with adding semicolon?

------
kybernetikos
I wrote an interactive guide to javascript that covers a lot of the same
material.

[http://caplin.github.io/new2JS/](http://caplin.github.io/new2JS/)

~~~
stefanve
Looks nice and it helpful, thanks. Maybe you could change the output so the
latest output is on top instead on the bottom (more blog style). Also it would
be nice to reset the output altogether

------
aleclarsoniv
If the arguments you wanted to pass to a constructor were variable in length,
why not just send them in an array normally instead of having to jury-rig
this? Are there benefits I'm missing?

------
drunken_thor
The article could have addressed overriding methods and calling the super
method. Because that isn't simple.

------
warbastard
It's breathe not breath...

