Understanding Irakli’s code ("new" protocol)
For current JavaScript, yes. But to complement the “prototypes as classes” proposal, you need to handle non-function objects.
On Friday, 2011-06-24 at 21:13 , Axel Rauschmayer wrote:
I’m trying to understand all the implications of Irakli’s code.
gist.github.com/d33befccbe1e0ed492d5
Object.prototype.new = function new() { return new this.constructor(); }; Object.prototype.extend = function extend(properties) { var constructor = new Function(), descriptor = {}; properties = properties || {}; Object.getOwnPropertyNames(properties).forEach(function(name) { descriptor[name] = Object.getOwnPropertyDescriptor(properties, name); }); descriptor.constructor = { value: constructor }; return Object.freeze(constructor.prototype = Object.create(this, descriptor)); };
Object.new = function new() { return Object.prototype.new(); }; Object.extend = function extend(properties) { return Object.prototype.extend(properties); };
Questions:
- Do "new" and "extend" really work properly on any built-in other than Object?
- E.g., I would expect String.extend() to lead to the invocation of Object.prototype.extend() which assumes "this" to be a prototype, but it is a constructor.
- This could be fixed by checking whether "this" is a function and then acting accordingly. Or by setting up a constructor/class method "extend" for each built-in.
I have not tried or thought much about other build-ins as showing idea was a main point. Yeah I was thinking that similar methods could be created per build-ins if necessary.
- I don’t like that new() methods have to keep the result of super.new() and return it. I would prefer something like the code below, which enables a constructor behavior like in Allen’s example.
I personally think that whole prototype, constructor and new keyword relationships add a lot of complexity to a language, and since this was fitting discussion nicely I though I'd share idea how this can be evolved to something more straight forward, that fits language much more IMO. I like new as a method because code is becomes simple and obvious.
- Are my changes really an improvement or do they impede proper interaction with built-ins, somehow?
===== Library code =====
// Optional, possibly faster: split the method below into Object.prototype.new and Function.prototype.new (the latter overriding the former) Object.prototype.new = function new() { if (this instanceof Function) { return new this(); } else { return new this.constructor(); } }; // Performance is less of an issue for this method Object.prototype.extend = function extend(properties) { var superProto; if (this instanceof Function) { superProto = this.prototype; } else { superProto = this; } var descriptor = {}; properties = properties || {}; Object.getOwnPropertyNames(properties).forEach(function(name) { descriptor[name] = Object.getOwnPropertyDescriptor(properties, name); }); var thisProto = Object.create(superProto, descriptor); if (thisProto.constructor) { thisProto.constructor.prototype = thisProto; } thisProto.super = superProto; return thisProto; };
// Optional: split the method below into Object.prototype.hasInstance and Function.prototype.hasInstance Object.prototype.hasInstance = function extend(instance) { if (this instanceof Function) { return instance instanceof this; } else { return this.isPrototypeOf(instance); } };
In my library I just use Foo.isPrototypeOf(bar); Gozala/selfish
Correction: The code below ignores the method arguments.
Object.prototype.new = function new() { if (this instanceof Function) { return new this(); } else { return new this.constructor(); } };
Fix (not very elegant...):
Object.prototype.new = function new() { var constr = (this instanceof Function ? this : this.constructor); var instance = Object.create(constr.prototype); constr.apply(instance, arguments); };
I’m trying to understand all the implications of Irakli’s code.
gist.github.com/d33befccbe1e0ed492d5
Questions:
Do "new" and "extend" really work properly on any built-in other than Object?
I don’t like that new() methods have to keep the result of super.new() and return it. I would prefer something like the code below, which enables a constructor behavior like in Allen’s example.
Are my changes really an improvement or do they impede proper interaction with built-ins, somehow?
===== Library code =====
// Optional, possibly faster: split the method below into Object.prototype.new and Function.prototype.new (the latter overriding the former) Object.prototype.new = function new() { if (this instanceof Function) { return new this(); } else { return new this.constructor(); } };
// Performance is less of an issue for this method Object.prototype.extend = function extend(properties) { var superProto; if (this instanceof Function) { superProto = this.prototype; } else { superProto = this; } var descriptor = {}; properties = properties || {}; Object.getOwnPropertyNames(properties).forEach(function(name) { descriptor[name] = Object.getOwnPropertyDescriptor(properties, name); }); var thisProto = Object.create(superProto, descriptor); if (thisProto.constructor) { thisProto.constructor.prototype = thisProto; } thisProto.super = superProto; return thisProto; };
// Optional: split the method below into Object.prototype.hasInstance and Function.prototype.hasInstance Object.prototype.hasInstance = function extend(instance) { if (this instanceof Function) { return instance instanceof this; } else { return this.isPrototypeOf(instance); } };
/**** Not necessary, any more (Object instanceof Object) Object.new = function new() { return Object.prototype.new(); }; Object.extend = function extend(properties) { return Object.prototype.extend(properties); }; ****/
===== Using the library =====
const SkinnedMesh = THREE.Mesh.extend({ constructor: function SkinnedMesh(geometry, materials) { SkinnedMesh.super.constructor.apply(this, arguments); // initialize instance properties this.identityMatrix = THREE.Matrix4.new(); this.bones = []; this.boneMatrices = []; }, update: function(camera) { ... // call base version of same method SkinnedMesh.super.update.call(this); } });