Understanding Irakli’s code ("new" protocol)

# Axel Rauschmayer (14 years ago)

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:

  1. 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.
  2. 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.

  3. 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); } });

# Axel Rauschmayer (14 years ago)

For current JavaScript, yes. But to complement the “prototypes as classes” proposal, you need to handle non-function objects.

# Irakli Gozalishvili (14 years ago)

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:

  1. 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.

  1. 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.

  1. 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

# Axel Rauschmayer (14 years ago)

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); };