Substitute __proto__ of DOM Element Object - javascript

So basically I would like to extend a certain type of DOM elements by the following code:
var element = document.createElement("div");
var proto = Object.create(HTMLDivElement.prototype);
proto.newMethod = function() {console.log("Good.");};
proto.newConst = Math.PI / 2;
element.__proto__ = proto;
This code works in Chrome, Firefox and IE11 (IE10 not tested, but it will probably work), but I'm not sure whether it is proper JavaScript and whether it will continue to work in the future, because anyway this code is hacking DOM elements which is partially outside JavaScript. Could someone give explanation on how it works? I don't fully understand that, and I need to know if this method is robust. Thanks.
OK, to make things clearer, I know I should use Object.create() to specify prototype, but the real problem is that element objects are special and it's impossible to do that. The above code is more like a workaround, and this is why I'm asking this question.
Google's Polymer mutates __proto__ of DOM objects (code, line 259):
function implement(element, definition) {
if (Object.__proto__) {
element.__proto__ = definition.prototype;
} else {
customMixin(element, definition.prototype, definition.native);
element.__proto__ = definition.prototype;
}
}
So, should I trust this method because Google uses it?

From Mozilla Developer Network:
The __proto__ property is deprecated and should not be used. Object.getPrototypeOf should be used instead of the __proto__ getter to determine the [[Prototype]] of an object. Mutating the [[Prototype]] of an object, no matter how this is accomplished, is strongly discouraged, because it is very slow and unavoidably slows down subsequent execution in modern JavaScript implementations. However, Object.setPrototypeOf is provided in ES6 as a very-slightly-preferred alternative to the __proto__ setter.
In general, it is a bad practice to modify native prototypes like Array, String and even HTMLElement, details are described here, but if you control everything in the current context you can modify the prototypes by adding, on your own risk, some additional functional to achieve what you want. If you can guarantee that your code is not in conflict with some other code and the performance footprint is negligible then you are free to choose your path.
Your approach:
SomeHTMLElementInstance.__proto__ = newPrototype;
// or a general case like:
SomeHTMLElementPrototypeConstructor.prototype.newMethod = function () {
// Do something here
}
Recommended approach:
var SomeElementWrapper = function (someParams) {
this.container = document.createElement('SomeHTMLElement');
}
SomeElementWrapper.prototype.someMethod = function () {
// Do something with this.container without modifying its prototype
}

Related

Alternative methods for extending object.prototype when using jQuery

Some time ago I tried to extend Object.prototype... I was surprised when later I saw errors in the console which comes from jQuery file. I tried to figured out what is wrong and of course I found information that extending Object.prototype is a "evil", "you shouldn't do that because JS is dynamic language and your code will not work soon" and information that jQuery will now add hasOwnProperty method to their for in loops.
Because I didn't want to leave jQuery, I drop the idea about extending Object.prototype.
Till now. My project getting bigger and I am really annoyed because I have to repeat many times some parts of the code. Below is a bit of the structure which I am using in my projects:
charts.js:
CHARTS = {
_init: function () {
this.monthlyChart();
/*
*
* more propertys goes here
*
*/
return this;
},
monthlyChart: function () {
//create my chart
return {
update: function () {
// update chart
}
};
}()
/*
*
* more propertys goes here
*
*/
}._init;
dashboard.js
NAVBAR = {
_init: function () {
/*
*
* more propertys goes here
*
*/
return this;
},
doSomething: function(){
$(document).ready(function(){
$('.myButton').on('click', function(){
var data = [];
// calling property from charts.js
CHARTS.monthlyChart.update(data);
});
});
}
}._init
As I mentioned project is really big now - it's over 40 js files and some of them has a few thousands line of code. It is really annoying that I have to repeat _init section every time, as well as I many functions I have to repeat $(document).ready && $(window).load.
I tried to find another solution for my problem. I tried to create class with init property (more you can find here) but I this solution forced me to add another "unnecessary" piece of the code to every file and accessing other file object property makes it to complicated too (return proper objects everywhere etc). As advised in the comment I started reading about getters and setters in JS.
After all I created something like that:
//Auto initialization
if (typeof $document === 'undefined') {
var $document = $(document),
$window = $(window),
$body = $('body');
}
Object.defineProperty(Object.prototype, '_init', {
get: function () {
// if object has no property named `_init`
if (!this.hasOwnProperty('_init')) {
for (var key in this) {
// checking if name of property does starts from '_' and if it is function
if (this.hasOwnProperty(key) && key[0] === '_' && typeof this[key] === 'function') {
if (key.indexOf('_ready_') > -1) {
//add function to document ready if property name starts from '_ready_'
$document.ready(this[key].bind(this));
} else if (key.indexOf('_load_') > -1) {
//add function to window load if property name starts from '_load_'
$window.load(this[key].bind(this));
} else {
// else execute function now
this[key].bind(this)();
}
}
}
return this;
}
}
});
and my object:
var DASHBOARD = {
_runMe: function(){
},
_ready_runMeOnReady: function(){
},
_load_runMeOnLoad: function(){
},
iAmAString: ''
}._init
It seems that this solution works with jQuery. But is it safe to use? I don't see any problem the code can cause and I don't see any further problems that it may cause. I will be really happy if somebody will tell me why I shouldn't use this solution.
Also I'm trying to understand how it works in details. Theoretically I defined property for the Object.prototype by defineProperty, without assigning value to it. Somehow it doesn't cause any errors in jQuery fore in loop, why? Does that mean that property _init is not defined at some point or at all because I am defined only getter of it?
Any help will be appreciated :)
By not including the descriptor in Object.defineProperty(obj, prop, descriptor) JavaScript defaults all the Boolean descriptor attributes to false. Namely
writable, enumerable, and configurable. Your new property is hidden from the for in iterators because your _init property is enumerable:false.
I am not a fan of JQuery so will not comment on why in regard to JQuery
There is no absolute rule to adding properties to JavaScript's basic type and will depend on the environment that your code is running. Adding to the basic type will add it to the global namespace. If your application is sharing the namespace with 3rd party scripts you can potentially get conflicts, causing your code or the third party code or both to fail.
If you are the only code then conflicts will not be an issues, but adding to object.prototype will incur an addition overhead on all code that uses object.
I would strongly suggest that you re examine the need for a global _init. Surely you don't use it every time you need a new object. I am a fan of the add hock approach to JavaScript data structures and try to keep away from the formal OOP paradigms
Your question in fact contains two questions.
It seams that this solution works with jQuery. But is it safe to use? I don't see any problem the code can cause and I don't see any further problems that it may cause. I will be really happy if somebody will tell me why I shouldn't use this solution.
First of all, there are three main reasons to avoid modification of built-in prototypes.
For-in loops
There is too much code using for-in loop without hasOwnProperty check. In your case that is jQuery code that does not perform check.
Solutions
Don't use for-in loop without .hasOwnProperty check.
Doesn't apply in this case because it's third-party code and you can't modify it.
for-in loop traverses only enumerable keys.
You have used that solution. Object.defineProperty creates non-enumerable properties by default (ECMAScript 5.1 specification)
Not supported by IE8.
Conflicts
There is risk of property name. Imagine that you use jQuery plugin that checks for existence of ._init property on objects - and it can lead to subtle and hard to debug bugs. Names prefixed with underscore are widely used in modern JavaScript libraries for indicating private properties.
Encapsulation violation (bad design)
But you have worser problem. Definining global ._init property suggests that every object have universal initialization logic. It breaks encapsulation, because your objects don't have full control over their state.
You can't rely on presence of _init method due to this. Your coworkers can't implement their own class with
Alternative designs
Global initializer
You can create global function initialize and wrap all your objects that require initialization in it.
Decouple view and logic
Your objects should not merge logic and view in one object (it violates single responsibility principle) and you are victim of spaghetti code.
Moreover - object initialization should not bind it to DOM, some controller objects should be a proxy between your logic and display.
It can be good idea to inspect how popular client-side MVC frameworks have solved this problem (Angular, Ember, Backbone) have solved this problem.
Is it safe to use getters and setters?
Yes. But if you only support IE9+.
Is it safe to modify Object.prototype?
No. Create another object to inherit all of your application objects from.
Why extending basic JavaScript objects is eval evil?
Because EVERY SINGLE object created on the webpage where your script is loaded will inherit that property or method.
There is a lot cons like collisions and performance overhead if you do it that way.
There is a lot of ways to make it better, let me show you the one I use.
// Here we create the base object:
var someBaseObject = {};
someBaseObject.someMethod = function () {
// some code here
}
someBaseObject.someProperty = "something";
// And inherit another object from the someBaseObject
someObject = Object.create(someBaseObject);
someObject.someAnotherMethod = function () {
// some code here
}
This approach allow us to leave the Object prototype alone, and build a prototype chain where someObject inherits from someBaseObject, and someBaseObject inherits from Object.
The only thing I want to say by this post: leave base objects alone and build your own, so you will have much less headache.
Note: Object.create is supported in IE9+. Here is shim for IE8 and lower by Douglas Crockford:
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}

Object.prototype is Verboten?

RECAP:
Ok, it's been a while since I asked this question. As usual, I went and augmented the Object.prototype anyway, in spite of all the valid arguments against it given both here and elsewhere on the web. I guess I'm just that kind of stubborn jerk.
I've tried to come up with a conclusive way of preventing the new method from mucking up any expected behaviour, which proved to be a very tough, but informative thing to do. I've learned a great many things about JavaScript. Not in the least that I won't be trying anything as brash as messing with the native prototypes, (except for String.prototype.trim for IE < 9).
In this particular case, I don't use any libs, so conflicts were not my main concern. But having dug a little deeper into possible mishaps when playing around with native prototypes, I'm not likely to try this code in combination with any lib.
By looking into this prototype approach, I've come to a better understanding of the model itself. I was treating prototypes as some form of flexible traditional abstract class, making me cling on to traditional OOP thinking. This viewpoint doesn't really do the prototype model justice. Douglas Crockford wrote about this pitfall, sadly the pink background kept me from reading the full article.
I've decided to update this question in the off chance people who read this are tempted to see for themselves. All I can say to that is: by all means, do. I hope you learn a couple of neat things, as I did, before deciding to abandon this rather silly idea. A simple function might work just as well, or better even, especially in this case. After all, the real beauty of it is, that by adding just 3 lines of code, you can use that very same function to augment specific objects' prototypes all the same.
I know I'm about to ask a question that has been around for quite a while, but: Why is Object.prototype considered to be off limits? It's there, and it can be augmented all the same, like any other prototype. Why, then, shouldn't you take advantage of this. To my mind, as long as you know what you're doing, there's no reason to steer clear of the Object prototype. Take this method for example:
if (!Object.prototype.getProperties)
{
Object.prototype.getProperties = function(f)
{
"use strict";
var i,ret;
f = f || false;
ret = [];
for (i in this)
{
if (this.hasOwnProperty(i))
{
if (f === false && typeof this[i] === 'function')
{
continue;
}
ret.push(i);
}
}
return ret;
};
}
Basically, it's the same old for...in loop you would either keep safe in a function, or write over and over again. I know it will be added to all objects, and since nearly every inheritance chain in JavaScript can be traced back to the Object.prototype, but in my script, I consider it the lesser of two evils.
Perhaps, someone could do a better job at telling me where I'm wrong than this chap, among others. Whilst looking for reasons people gave NOT to touch the Object's prototype, one thing kept cropping up: it breaks the for..in loop-thingy, but then again: many frameworks do, too, not to mention your own inheritance chains. It is therefore bad practice not to include a .hasOwnProperty check when looping through an object's properties, to my mind.
I also found this rather interesting. Again: one comment is quite unequivocal: extending native prototypes is bad practice, but if the V8 people do it, who am I to say they're wrong? I know, that argument doesn't quite stack up.
The point is: I can't really see a problem with the above code. I like it, use it a lot and so far, it hasn't let me down once. I'm even thinking of attaching a couple more functions to the Object prototype. Unless somebody can tell me why I shouldn't, that is.
The fact is, it's fine as long as you know what you're doing and what the costs are. But it's a big "if". Some examples of the costs:
You'll need to do extensive testing with any library you choose to use with an environment that augments Object.prototype, because the overwhelming convention is that a blank object will have no enumerable properties. By adding an enumerable property to Object.prototype, you're making that convention false. E.g., this is quite common:
var obj = {"a": 1, "b": 2};
var name;
for (name in obj) {
console.log(name);
}
...with the overwhelming convention being that only "a" and "b" will show up, not "getProperties".
Anyone working on the code will have to be schooled in the fact that that convention (above) is not being followed.
You can mitigate the above by using Object.defineProperty (and similar) if supported, but beware that even in 2014, browsers like IE8 that don't support it properly remain in significant use (though we can hope that will change quickly now that XP is officially EOL'd). That's because using Object.defineProperty, you can add non-enumerable properties (ones that don't show up in for-in loops) and so you'll have a lot less trouble (at that point, you're primarily worried about name conflicts) — but it only works on systems that correctly implement Object.defineProperty (and a correct implementation cannot be "shimmed").
In your example, I wouldn't add getProperties to Object.prototype; I'd add it to Object and accept the object as an argument, like ES5 does for getPrototypeOf and similar.
Be aware that the Prototype library gets a lot of flak for extending Array.prototype because of how that affects for..in loops. And that's just Arrays (which you shouldn't use for..in on anyway (unless you're using the hasOwnProperty guard and quite probably String(Number(name)) === name as well).
...if the V8 people do it, who am I to say they're wrong?
On V8, you can rely on Object.defineProperty, because V8 is an entirely ES5-compliant engine.
Note that even when the properties are non-enumerable, there are issues. Years ago, Prototype (indirectly) defined a filter function on Array.prototype. And it does what you'd expect: Calls an iterator function and creates a new array based on elements the function chooses. Then ECMAScript5 came along and defined Array.prototype.filter to do much the same thing. But there's the rub: Much the same thing. In particular, the signature of the iterator functions that get called is different (ECMAScript5 includes an argument that Prototype didn't). It could have been much worse than that (and I suspect — but cannot prove — that TC39 were aware of Prototype and intentionally avoided too much conflict with it).
So: If you're going to do it, be aware of the risks and costs. The ugly, edge-case bugs you can run into as a result of trying to use off-the-shelf libraries could really cost you time...
If frameworks and libraries generally did what you are proposing, it would very soon happen that two different frameworks would define two different functionalities as the same method of Object (or Array, Number... or any of the existing object prototypes). It is therefore better to add such new functionality into its own namespace.
For example... imagine, you would have a library that would serialize objects to json and a library that would serialize them to XML and both would define their functionality as
Object.prototype.serialize = function() { ... }
and you would only be able to use the one that was defined later. So it is better if they don't do this, but instead
JSONSerializingLibrary.seralize = function(obj) { ... }
XMLSerializingLibrary.seralize = function(obj) { ... }
It could also happen that a new functionality is defined in a new ECMAscript standard, or added by a browser vendor. So imagine that your browsers would also add a serialize function. That would again cause conflict with libraries that defined the same function. Even if the libraries' functionality was the same as that which is built in to the browser, the interpreted script functions would override the native function which would, in fact, be faster.
See http://www.websanova.com/tutorials/javascript/extending-javascript-the-right-way
Which addresses some, but not all, the objections raised. The objection about different libraries creating clashing methods can be alleviated by raising an exception if a domain specific method is already present in Object.prototype. That will at least provide an alert when this undesirable event happens.
Inspired by this post I developed the following which is also available in the comments of the cited page.
!Object.implement && Object.defineProperty (Object.prototype, 'implement', {
// based on http://www.websanova.com/tutorials/javascript/extending-javascript-the-right-way
value: function (mthd, fnc, cfg) { // adds fnc to prototype under name mthd
if (typeof mthd === 'function') { // find mthd from function source
cfg = fnc, fnc = mthd;
(mthd = (fnc.toString ().match (/^function\s+([a-z$_][\w$]+)/i) || [0, ''])[1]);
}
mthd && !this.prototype[mthd] &&
Object.defineProperty (this.prototype, mthd, {configurable: !!cfg, value: fnc, enumerable: false});
}
});
Object.implement (function forEach (fnc) {
for (var key in this)
this.hasOwnProperty (key) && fnc (this[key], key, this);
});
I have used this primarily to add standard defined function on implementation that do not support them.

Can you extend an object that has access to private properties with a function that can also access those private properties?

I am creating an object inside of an enclosure. Also in the enclosure are private properties that the object's functions can access - and this works as expected.
My issue: I want others to be able to extend my object with functions of their own (functions from a different context), but those functions will also need access to the same private properties - and I have not been able to find a way to make this work.
I've tried various configurations of .call, and also wrapping their function in a new function, amongst other things. I feel like I've gotten close to a solution, but have just fallen short.
Here's a bit of simplified example code that accurately reflects my situation:
//extension object
//fn2 can be any function, with any number of arguments, etc.
var obj1 = {};
obj1.fn2 = function (s1, s2){ console.log(priv); };
//actual object
var obj2 = (function (){
//private property
var priv = "hello world";
//return object
var obj3 = {};
//return object's native fn (works)
obj3.fn = function (s){ console.log(priv); };
//extension happens here - but is obviously not correct
obj3.fn2 = obj1.fn2;
//return object
return obj3;
})();
//try output
obj2.fn("goodbye world"); //works
obj2.fn2("goodbye world", "thx 4 teh phish"); //fails
Any insight would be appreciated. And I totally understand if what I want just isn't possible - but it sure seems like it should be :P
EDIT: Thank you all for the responses. I fully understand that the properties are more easily accessed as public, and that normally inherited objects won't have access to them otherwise. However, since the new function is being attached to the original object I have to believe there's a way to use the original context and not the context the new function was created in.
Now, I'm the first to say that eval is evil - and, in fact, I've never used it, or even considered using it, before. However, I'm trying everything I can think of to make this work - and I stumbled across this (seemingly) working solution:
obj3.fn2 = eval(obj1.fn2.toString());
So, if I check to make sure that obj1.fn2 is a typeof function, is there any way this could be harmful to my code? It doesn't execute the function, so I can't see how - but maybe I'm missing something?
Javascript doesn't have a "protected" analog. You either get super private or completely public. From here you can choose to:
Reconsider your class design, and have the subclasses depend only on the public interface of the parent class.
Add getter and setter functions to the public interface. Not necessarily the best thing though as you might just as well make the properties public (besides best practice issues and whatnot)
Just use public properties instead. This is the "natural" way to do OO inheritance in Javascript and is usually not a problem if you use a donvention like adding an underscore to the beggining of the name. As a bonus you can use the prototypal inheritance feature (it is nice knowing how to use this instead of only closure-based classes)
function Base(){
this._priv = "Hello world"
};
Base.prototype = {
fn: function(){
console.log(this._priv);
}
}
var obj2 = new Base();
obj2.fn = function(){ ... }
I hate to answer my own question - seems like a bit of a faux pas - but c'est la vie. (because I woke up French today?)
So, while I found that the eval() solution I presented last night in the edit to my original question does seem to be a valid solution, and a proper use of eval for retaining the object's context within the new function, it is far from perfect.
Firstly, it works in FF, but both IE and Chrome seem to hate it (those were the next ones I tried, and I quit trying others after they both failed). Though I'm sure it could probably be made to work across browsers, it seems like a hassle.
Secondly, it does give quite a bit of power to the new function, and as I look at my code more I do like the idea of controlling exactly what these new functions being added to my object get access to.
Thirdly, .eval() is typically pretty slow - and it turns out that .apply() (which is typically faster) just may work well enough.
This is because I realized at some point last night that no new functions on this object will need to set any of the private variables (at least, I'm fairly certain they won't) - and .apply() works fine to pass the values through for them to read.
I'm sure there's more to it than just those 3 things, but for now I think I'm going to go with more of a 'wrapper' solution - something like this:
var f = function (){
var fauxThis = {};
fauxThis.priv = priv;
obj1.fn2.apply(fauxThis, arguments);
};
obj3.fn2 = f;
//(To be placed where I had "obj3.fn2 = obj1.fn2;")
I am certainly willing now to consider the use of eval() in very specific cases - and may even revisit this specific use of it before I make my final decision of which direction to take. (especially if I can think of a case where the private value would need to be set)
Thanks all for your input!
The quickest and easiest solution is to prefix any supposedly private properties with the underscore (_).
Personally I like to bottle my private properties into a single object which would be placed on the object, like so:
obj.publicProp = 20;
obj._.privateProp = true;
I wouldn't worry so much about it though, the underscore is basically a universal symbol for private so those using the script will know that it's private and shouldn't be touched. Or, better yet, just leave it out of the public documentation ;)
There are other methods and you can use which do emulate "true" protected variables, but they're not the best as they avoid garbage collection, and can be clunky to use.

Is John Resig's OO JavaScript implementation production safe?

For a long time I have been throwing around the idea of making my JavaScript more object oriented. I have looked at a few different implementations of this as well but I just cannot decide if it is necessary or not.
What I am trying to answer are the following questions
Is John Resig's simple inheritance structure safe to use for production?
Is there any way to be able to tell how well it has been tested?
Besides Joose what other choices do I have for this purpose? I need one that is easy to use, fast, and robust. It also needs to be compatible with jQuery.
Huh. It looks much more complicated than it needs to be, to me.
Actually looking more closely I really take exception to what it is doing with providing this._super() whilst in a method, to call the superclass method.
The code introduces a reliance on typeof==='function' (unreliable for some objects), Function#toString (argh, function decomposition is also unreliable), and deciding whether to wrap based on whether you've used the sequence of bytes _super in the function body (even if you've only used it in a string. and if you try eg. this['_'+'super'] it'll fail).
And if you're storing properties on your function objects (eg MyClass.myFunction.SOME_PRIVATE_CONSTANT, which you might do to keep namespaces clean) the wrapping will stop you from getting at those properties. And if an exception is thrown in a method and caught in another method of the same object, _super will end up pointing at the wrong thing.
All this is just to make calling your superclass's method-of-the-same name easier. But I don't think that's especially hard to do in JS anyway. It's too clever for its own good, and in the process making the whole less reliable. (Oh, and arguments.callee isn't valid in Strict Mode, though that's not really his fault since that occurred after he posted it.)
Here's what I'm using for classes at the moment. I don't claim that this is the “best” JS class system, because there are loads of different ways of doing it and a bunch of different features you might want to add or not add. But it's very lightweight and aims at being ‘JavaScriptic’, if that's a word. (It isn't.)
Function.prototype.makeSubclass= function() {
function Class() {
if (!(this instanceof Class))
throw 'Constructor function requires new operator';
if ('_init' in this)
this._init.apply(this, arguments);
}
if (this!==Object) {
Function.prototype.makeSubclass.nonconstructor.prototype= this.prototype;
Class.prototype= new Function.prototype.makeSubclass.nonconstructor();
}
return Class;
};
Function.prototype.makeSubclass.nonconstructor= function() {};
It provides:
protection against accidental missing new. The alternative is to silently redirect X() to new X() so missing new works. It's a toss-up which is best; I went for explicit error so that one doesn't get used to writing without new and causing problems on other objects not defined like that. Either way is better than the unacceptable JS default of letting this. properties fall onto window and mysteriously going wrong later.
an inheritable _init method, so you don't have to write a constructor-function that does nothing but call the superclass constructor function.
and that's really all.
Here's how you might use it to implement Resig's example:
var Person= Object.makeSubclass();
Person.prototype._init= function(isDancing) {
this.dancing= isDancing;
};
Person.prototype.dance= function() {
return this.dancing;
};
var Ninja = Person.makeSubclass();
Ninja.prototype._init= function() {
Person.prototype._init.call(this, false);
};
Ninja.prototype.swingSword= function() {
return true;
};
var p= new Person(true);
p.dance(); // => true
var n = new Ninja();
n.dance(); // => false
n.swingSword(); // => true
// Should all be true
p instanceof Person &&
n instanceof Ninja && n instanceof Person
Superclass-calling is done by specifically naming the method you want and calling it, a bit like in Python. You could add a _super member to the constructor function if you wanted to avoid naming Person again (so you'd say Ninja._super.prototype._init.call, or perhaps Ninja._base._init.call).
JavaScript is prototype based and not class based. My recommendation is not to fight it and declare subtypes the JS way:
MyDerivedObj.prototype = new MySuperObj();
MyDerivedObj.prototype.constructor = MyDerivedObj;
See how far you can get without using inheritance at all. Treat it as a performance hack (to be applied reluctantly where genuinely necessary) rather than a design principle.
In an a highly dynamic language like JS, it is rarely necessary to know whether an object is a Person. You just need to know if it has a firstName property or an eatFood method. You don't often need to know if an object is an array; if it has a length property and some other properties named after integers, that's usually good enough (e.g. the Arguments object). "If it walks like a duck and quacks like a duck, it's a duck."
// give back a duck
return {
walk: function() { ... },
quack: function() { ... }
};
Yes, if you're making very large numbers of small objects, each with dozens of methods, then by all means assign those methods to the prototype to avoid the overhead of creating dozens of slots in every instance. But treat that as a way of reducing memory overhead - a mere optimisation. And do your users a favour by hiding your use of new behind some kind of factory function, so they don't even need to know how the object is created. They just need to know it has method foo or property bar.
(And note that you won't really be modelling classical inheritance in that scenario. It's merely the equivalent of defining a single class to get the efficiency of a shared vtable.)

Javascript Getting Objects to Fallback to One Another

Here's a ugly bit of Javascript it would be nice to find a workaround.
Javascript has no classes, and that is a good thing. But it implements fallback between objects in a rather ugly way. The foundational construct should be to have one object that, when a property fails to be found, it falls back to another object.
So if we want a to fall back to b we would want to do something like:
a = {sun:1};
b = {dock:2};
a.__fallback__ = b;
then
a.dock == 2;
But, Javascript instead provides a new operator and prototypes. So we do the far less elegant:
function A(sun) {
this.sun = sun;
};
A.prototype.dock = 2;
a = new A(1);
a.dock == 2;
But aside from elegance, this is also strictly less powerful, because it means that anything created with A gets the same fallback object.
What I would like to do is liberate Javascript from this artificial limitation and have the ability to give any individual object any other individual object as its fallback. That way I could keep the current behavior when it makes sense, but use object-level inheritance when that makes sense.
My initial approach is to create a dummy constructor function:
function setFallback(from_obj, to_obj) {
from_obj.constructor = function () {};
from_obj.constructor.prototype = to_obj;
}
a = {sun:1};
b = {dock:2};
setFallback(a, b);
But unfortunately:
a.dock == undefined;
Any ideas why this doesn't work, or any solutions for an implementation of setFallback?
(I'm running on V8, via node.js, in case this is platform dependent)
Edit:
I've posted a partial solution to this below, that works in the case of V8, but isn't general. I'd still appreciate a more general solution.
You could just use Object.create. It's part of ES5 so it's already available natively in some browsers. I believe it does exactly what you want.
Okay, some more research and cross-platform checking and there's some more information (though not a general solution).
Some implementations have basically what I did for my __fallback__. It is called __proto__ and is about perfect:
a = {sun:1};
b = {dock:2};
a.__proto__ = b;
a.dock == 2;
It seems that, what happens in when a new object is constructed is roughly this:
a = new Constructor(...args...);
produces behavior roughly equivalent to:
object.constructor = constructor;
object.__proto__ = constructor.prototype;
constructor.call(this, ...args...);
So it is no wonder that coming along later and adjusting an object's constructor or constructor.prototype has no effect, because the __proto__ setting is already set.
Now for my v8 application, I can just use __proto__, but I understand it that this isn't exposed on the IE VM (I don't run windows, so I can't tell). So it is not a general solution to the problem.

Categories

Resources