Proxy
If you are against using __proto__ or anything related to it but still want the inheritability, you could make use of Proxies.
var ObjectCallable_handler = {
get: function get(self, key) {
if (self.hasOwnProperty(key)) {
return self[key];
} else { return self.__inherit__[key]; }
},
apply: function apply(self, thisValue, args) {
return (self.__call__ || self.__inherit__.__call__).apply(self, args);
}
};
function ObjectCallable(cls) {
var p = new Proxy(function() { }, ObjectCallable_handler);
p.__inherit__ = cls;
return p;
}
Pros
- Maintains inheritability.
- Does not involve a traditional prototype chain.
- ES6 draft.
Cons
setPrototypeOf
If the lack of support for Proxies dismays you, you could try a setPrototypeOf polyfill instead.
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
obj.__proto__ = proto;
return obj;
}
function ObjectCallable(cls) {
var f = function() { return f.__call__.apply(f, arguments); };
Object.setPrototypeOf(f, cls);
return f;
}
Pros
- Likely to work the best right now and in the future.
- ES6 draft.
Cons
- Makes use of the non-standard
__proto__ through the polyfill and will require testing across the engines/browsers you want to support.
setPrototypeOf is not currently implemented in any browser.
copy
This is the simplest solution with the least amount of functionality, but it's guaranteed to work almost anywhere. Create a new function and clone an object's properties onto it.
function ObjectCallable(cls) {
var f = function() { return f.__call__.apply(f, arguments); }, k;
for (k in cls) {
f[k] = cls[k];
}
return f;
}
Pros
- Works virtually everywhere.
Cons
- Does not maintain any semblance of an inheritance or prototype structure whatsoever.
Conclusion
What you want to do with replicating Python's __call__ functionality in JavaScript will require either more time as ES6 develops and becomes implemented in more engines or relying on non-standard functionality such as __proto__.
.__proto__is the same as.constructor.prototype.__proto__, not when setting it. Setting the constructor's prototype after the fact has no effect on the already-created object, to change the prototype used by created objects you have to set those directly. And while you could probably remove all the properties in.constructor.prototypeand then add new ones, that would mean the change in prototype properties would affect all objects created by that constructor, not just the given function (which can be desirable, of course, but may not be in this situation).