Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

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.



Since this is usually downplayed because the "code is shared", to be more specific, for example in V8 in x64 (node.js, Google Chrome), the theoretical minimum memory used by a function object is 72 bytes:

- Map pointer: 8

- Properties pointer: 8

- Elements pointer: 8

- Code entry pointer: 8

- Initial Map/prototype pointer: 8

- SharedFunctionInfo pointer: 8

- Context pointer: 8

- Literals/Bindings pointer: 8

- Weak fields pointer: 8

So if you have a Person class with 30 methods, the methods are taking 30 * 72 = 2160 bytes at the very least. The actual data for a person might take 200 bytes, if we store e.g. full name, age and address so in this case there is like 10x overhead. If you print the details of 200 people per page request and there are 100 people connecting to your server, you are wasting 200 * 100 * 2160 =~ 40 megabytes on memory on storing all these useless function objects at that moment. That is just crazy.

And in GC language it's never just memory, a GC will eat exponentinally more CPU time when the amount of memory you use reaches closer and closer to limits.


> 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.


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]


It's especially weird that the author did this after:

(a) showing awareness that polluting the global namespace is bad

(b) demonstrating a technique for being able to use a constructor with apply (the "newless" constructor he shows earlier)

but I think a lot of the article is less "here's a set of good practices" and more "let's play with some of the less-familiar corners of JS and demonstrate how flexible it is."


"In the first version, each time you create a person, a new sayHi function will be created for him, where as in the second version, only one sayHi function is ever created, and is shared amongst all persons that are created - because Person.prototype is their parent. Thus, declaring methods on the prototype is more memory efficient."


Ah, you're right, I missed that. Small amount of bile revoked.

But it should have been MUCH more strongly emphasized as the only way to do it, unless you have a very good reason, not tucked down in the bottom as "oh, if you feel like it, here's a trick to make your code a little better." Especially in a tutorial that's meant for beginners in JS.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: