Skip to content

Instantly share code, notes, and snippets.

@marcoscaceres
Last active December 10, 2015 07:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marcoscaceres/4402084 to your computer and use it in GitHub Desktop.
Save marcoscaceres/4402084 to your computer and use it in GitHub Desktop.
Exports a function to the global scope in a way that conforms to WebIDL.
/**
* Exports a function to the global scope in a way that conforms to Web IDL.
*
* These objects are indistiguishable from host objects. That is, you can create
* objects like the ones that are exposed by the browser, like window.Event,
* window.Node, etc.
*
* Copyright (c) 2012 Marcos Caceres / François REMY
* Licensed under the MIT license.
*
**/
// emulate Function.name if needed
if(Function.name != "Function") {
Object.defineProperty(Function.prototype, "name", {
get: function() {
try {
var name = (""+this).match(/function(.*?)\(/)[1].replace(/\s+$|^\s+/g, "");
Object.defineProperty(this, "name", {value:name,writable:false});
return name;
} catch (ex) {
return "anonymous";
}
}
});
}
function DOMConstructor(privateConstructor, prototypeDefinition, constructorDefinition) {
var name = privateConstructor.name || 'DOMObject';
//emulate native code toString()
var toStringMaker = function toStringMaker(name) {
return function toString() {
return 'function ' + name + '() { [native code] }';
};
};
// create the prototype hierarchy
var prototypeConstructor = eval('(function '+name+'Prototype() {})');
prototypeConstructor.prototype.toString=function() { if(this instanceof privateConstructor) { return '[object '+name+']'; } else { return '[object '+name+'Prototype]'; } }
privateConstructor.prototype=new prototypeConstructor();
// add the "instance" properties
if(prototypeDefinition) {
Object.defineProperties(privateConstructor.prototype, prototypeDefinition);
}
// create the public contructor
var publicConstructor = eval('(function ' + name + '(){return new privateConstructor()})');
// the new constructor should have the same prototype
try {
Object.defineProperty(publicConstructor, 'prototype', {
writable: false,
enumerable: false,
configurable: false,
value: privateConstructor.prototype
});
} catch(ex) {
publicConstructor.prototype=privateConstructor.prototype;
}
// add the "static" properties
if(constructorDefinition) {
Object.defineProperties(publicConstructor, constructorDefinition);
}
// the functions should not leak their implementation (via toString)
privateConstructor.toString = publicConstructor.toString = toStringMaker(name);
privateConstructor.prototype.constructor=publicConstructor;
for (var i in privateConstructor.prototype) {
if (privateConstructor.prototype.hasOwnProperty(i)) {
var f = privateConstructor.prototype[i];
if(typeof f == "function") {
if(!f.hasOwnProperty("toString")) {
f.toString = toStringMaker(i);
}
}
}
}
return publicConstructor;
}
</script>
</head>
<body>
<script>
(function(){
var fooInstance;
// your secret constructor
var Foo = new DOMConstructor(function Foo(){ this.date=new Date(); }, {
test: {
value: function test(){
if (!(this instanceof Foo)) {
throw new TypeError("Illegal invocation");
}
console.log("test")
}
}
},{
CONSTANT: {
value: true,
writable: false,
configurable: false
}
});
//Add it to the window object
window.Foo=Foo;
}());
//proof
console.log(window.Foo);
//check instance prototype chain
var fooInstance = new Foo();
fooInstance.test();
Foo.prototype.test2=function(){console.log('test2')};
fooInstance.test2();
//Following all throw
if(!window.setImmediate) { window.setImmediate=function(f) { setTimeout(f, 0); }; }
setImmediate(function() { console.log(Foo() instanceof Foo); });
setImmediate(function() { console.log(new Foo() instanceof Foo); });
setImmediate(function() { console.log(new Foo().constructor==Foo); });
setImmediate(function() { console.log(fooInstance=="[object Foo]"); });
setImmediate(function() { console.log(Foo.prototype=="[object FooPrototype]"); });
setImmediate(function() { try { Foo.prototype.test(); console.log(false); } catch (ex) { console.log(true); } });
setImmediate(function() { console.log(Foo.CONSTANT==true); });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment