My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Friday, December 28, 2012

A Cross Platform Inherit Function

This (apparently non working) gist gave me the hint. Stuff I've been dealing with for a while, finally used to bring an Object.create(firstArgOnly) cross platform/engine/client/server code that works. The name? inherit()
/*!(C) WebReflection *//** @license Mit Style */
// inspired by https://gist.github.com/4395291
this.inherit || (this.inherit = function(create){
if (!create) {
if ({__proto__:null} instanceof Object) {
for (var
Null = function Null() {},
doc = document,
html = doc.documentElement,
iframe = html.insertBefore(
doc.createElement("iframe"),
html.lastChild
),
// works down to IE7, it does NOT work in IE6
NullPrototype = Null.prototype =
(iframe.src = "javascript:", iframe.contentWindow.Object.prototype),
/* this would work in IE6 too
idoc = iframe.contentWindow.document ||
iframe.contentDocument ||
iframe.document,
NullPrototype = Null.prototype = (
idoc.open(),
idoc.write(
"<script>parent.inherit=Object.prototype<" +
"/script>"
),
idoc.close(),
inherit
),
//*/
xtend = html.removeChild(iframe) && function xtend(object) {
return xtend.prototype === object ?
(xtend.prototype = xtend, this) :
new xtend(xtend.prototype = object)
;
},
proto = [
"hasOwnProperty",
"isPrototypeOf",
"propertyIsEnumerable",
"valueOf",
"toString",
"toLocaleString",
"constructor"
],
i = proto.length; i--;
) delete NullPrototype[proto[i]];
// you know ... IE, leaks, and shit ...
create = doc = html = iframe = NullPrototype = proto =
function(object) {
return object == null ? new Null : xtend(object);
}
;
} else {
create = function(object) {
return {__proto__: object};
};
}
}
return function(object) {
return create(object);
};
}(Object.create));
view raw inherit.js hosted with ❤ by GitHub

More Reliable Than Object.create()

The reason I didn't even try to polyfill the ES5 Object.create() method is quite easy: it is not possible to shim it in a cross platform way due second argument which requires descriptors features, highly improbable to simulate properly. Even if browsers support the create() method, inherit() guarantee that no second argument will ever be used so we are safe from inconsistencies within the function.

Forget hasOwnProperty !

Yeah, one of the coolest things about being able to inherit from null is the fact not even Object.prototype is inherited so we can create real empty objects without worrying about surrounding environment, 3rd parts obtrusive libraries, and the boring and slow obj.hasOwnProperty(key) check.
// some obtrusive code
Object.prototype.troll = function (up) {
  throw up;
};

// empty object in every browser/env
var dict = inherit(null);
dict.anyKey = anyValue;

// later on ...
for (var key in dict) {
  // only 'anyKey'
}
That's correct, objects that inherit from null are not affected by the Object.prototype ... really, we cannot even print them without defining a toString method!

Update: On IE DontEnum Bug

Unfortunately the fact IE does not enumerate toString and other native Object.prototype names is not solved here for the simple reason here we are not solving that problem, here we are solving inheritance. However,I think is a must know that even objects created via this function needs an extra loop or something like this:
this.forIn = function (){
var
propertyIsEnumerable = "propertyIsEnumerable",
toString = "toString",
OK = {toString:1}[propertyIsEnumerable](toString),
keys = OK || [
propertyIsEnumerable,
toString,
"hasOwnProperty",
"isPrototypeOf",
"valueOf",
"toLocaleString",
"constructor"
],
num = OK || function(obj, callback, self){
for (var
key,
i = keys.length;
i--;
(key = keys[i]) in obj &&
callback.call(
self,
obj[key],
key,
obj
)
);
}
;
return function forIn(obj, callback, self) {
for(var key in obj)
callback.call(
self,
obj[key],
key,
obj
)
;
OK || num(obj, callback, self);
};
}();
/* example
forIn({toString:123}, function(value, key, obj) {
alert(value === 123 && key === "toString");
});
//*/
view raw forIn.js hosted with ❤ by GitHub

Not Only Null

This function should just work with anything we might need to inherit and we can also inherit from inheriting objects without problems. Consider this an end of the year tiny present just to remind some basic concept about JS that is still valid: Objects inherit from Objects and that's pretty much it :) Enjoy and see you next year!

1 comment:

Christopher said...

This is really great, as usual. Thanks Andrea!