4 ms·
Understanding Prototypical Inheritance in JavaScript
- demallien 16y agoAm I alone in really not liking the use of the .prototype property in Javascript? For starters, it doesn't play well with native classes, so if you're trying to bind native objects to Javascript, it's going to create problems. For example, when using .prototype, the constructor creates a pure Javascript object - for JavascriptCore at least, pure Javascript objects don't have the possibility to be linked to a native object, with JSObjectSetPrivate() returning an error. Secondly, it just generates all sorts of problems with handling of member variables - every instance of a subclass objects links to just one copy of the parent class, which means that all of the subclass instances share state - this is generally not what is wanted in subclassing. Crockford suggests using prototyping in a more free-form manner, as follows: function MySubclass() { var that = new MyParentClass(); that.newMemberFunction = function() { } return that; } which corrects both of the problems that I mentioned above. Personally I use this style a lot in Ruby as well as JavaScript, which shows that it is more portable than .prototype as well.
- jashkenas 16y agoJust because Crockford suggests it, doesn't make it a good idea. Manufacturing objects with a factory function, avoiding the "prototypal" part of JavaScript, is slow, wastes memory, and prevents you from using "instanceof" to compare objects. The point of a prototype is to share common functions and properties that don't change among instances. With "MySubclass", you manufacture a new copy of every function in the class, each time you create a new instance, with all of the closure-related overhead that entails. Last time I benchmarked it, comparing a simple class with ten methods, in both prototypal style, and factory style, in Chrome: Empty page, no objects: 0.9 MB 1 mil constructor objects: Brief pause, 32.4 MB 1 mil factory objects: About a minute pause, 368.1 MB That said, if you know you're only going to have a dozen instances of the object on the page, then it hardly matters.
- praptak 16y ago"Just because Crockford suggests it, doesn't make it a good idea." On top of that Crockford does not actually state that closure-based objects should be preferred to prototypal inheritance. See: http://javascript.crockford.com/prototypal.html http://javascript.crockford.com/prototypal.html
- jashkenas 16y agoTo repeat myself: Just because Crockford suggests it, doesn't make it a good idea. Here's a set of benchmarks for creating a new instance, using the pattern you just linked to (called "Crockford Create" within): http://jsperf.com/object-create-vs-crockford-vs-jorge-vs-constructor/9 http://jsperf.com/object-create-vs-crockford-vs-jorge-vs-con... In Safari, it's about 5X slower than using a regular constructor function. In Chrome, it's about 20X slower. In JS, just using prototypes as Eich intended is the way to go.
- praptak 16y ago"To repeat myself: Just because Crockford suggests it, doesn't make it a good idea." I did not suggest anything like this. My point was that Crockford pretty much enumerates every sensible way to do inheritance in JS, with the conclusion quite similar to yours - Crockford suggests (in fact: describes) many things, so don't draw too much from this. Btw, he suggests the regular constructor too: http://javascript.crockford.com/inheritance.html http://javascript.crockford.com/inheritance.html
- demallien 16y agoI don't have the time to go looking for it, but he does in fact make precisely this claim in one of his video talks that I watched a few weeks back. Make of that what you will...
- demallien 16y agoLol, yup, you are of course spot on about the problems with factory functions - of course when you are using custom JS bindings, you can correct for this by pushing objects with many instances down into the native layer - which I had to do on the project I'm doing at the moment, precisely because it blew out the memory. I think that what we are looking at here is that there are two different use cases of Javascript that require two different strategies - if you want to produce subclasses of native objects, then you're going to be using factory functions, because no other solution exists in Javascript (well, at least not for JavascriptCore that I'm aware of). If you don't, then the use of a prototypal solution is more efficient.
- xutopia 16y agoWhat are the benefits of JSObjectSetPrivate? When is it useful?
- demallien 16y agoIf you have an native (non-javascript) object that has been bound to a Javascript object, this is where the link between the two is stored. Basically on the C side of the bindings, when a native function is called, you receive the "this" object, and you can simply call JSObjectGetPrivate() to retrieve a pointer to the corresponding native object - assuming that you have previously called JSObjectSetPrivate...
- deleted 16y ago[deleted]
- praptak 16y agoIf you set a function's 'prototype' property to a new object, it's a good practice to set the object's 'constructor' property back to the function, so Triangle.prototype = new Shape(3, 3); should be followed by: Triangle.prototype.constructor = Triangle
- regularfry 16y agoWhy is this?
- praptak 16y agoThat's the default on any function you create in JS. This prints 'true': var f = function() {}; document.write(f.prototype.constructor == f); This alone isn't perhaps a very important reason to keep this invariant, but it makes some basic reflection possible. This is (among other things) useful for debugging. It's just sometimes useful to be able to check the problematic object's "class", or at least something as close to class as you can get in a classless language.