Object.create() instead of new THREE.Object3D()? #2080

Closed
mrdoob opened this Issue Jun 19, 2012 · 15 comments

Projects

None yet

7 participants

@mrdoob
Owner

I found an interesting suggestion in StackOverflow. One benefit I can see is that, in theory, THREE.GeometryCount wouldn't be increasing only at load time (because of the geometry constructors). I don't know if it would still work or if there would be any side-effects to this.

@tJener

When taking heap snapshots in Chrome, it appears that objects created with Object.create are just listed as "Objects." It seems that maybe the constructor is used to categorize the objects. This doesn't apply, I misread.

Also, in terms of bypassing side effects of constructors (especially if you're going to call the superclass's constructor in the subclass), would doing something like this work for you:

function extend( sub, base ) {
  // A dummy constructor function.
  function F() {}
  F.prototype = base.prototype;

  // Create a new dummy object, while avoiding side effects in the base constructor.
  sub.prototype = new F();
}

var globalCount = 0;
var Super = function() {
  globalCount += 1;
};

Super.prototype = { toString: function() { return 'Super'; } };

var Sub = function() {
  Super.call( this );
};

extend( Sub, Super );

Sub.prototype.toString = function() { return 'Sub'; };

globalCount === 0; // true
@alteredq

One benefit I can see is that, in theory, THREE.GeometryCount wouldn't be increasing only at load time (because of the geometry constructors).

I'm also bugged by these extra counts.

As for syntax, I find the current way prettier.

IMHO this looks nicer:

THREE.Camera.prototype = new THREE.Object3D();
THREE.Camera.prototype.constructor = THREE.Camera;

than this:

THREE.Camera.prototype = Object.create(THREE.Object3D.prototype);
THREE.Camera.prototype.constructor = THREE.Camera;

I'm not sure I understood why Object.create is supposed to be "better".

What if THREE.Object3D expects arguments without which the constructor would not work?

This is hypothetical than doesn't hold for us. And even if we had some constructor parameters in Object3D, I'm not sure how it would work with Object.create syntax.

@Benvie

For reference, this is the code for V8's Object.create (most JS interfaces in V8 are implemented in JS):

// ES5 section 15.2.3.5.
function ObjectCreate(proto, properties) {
  if (!IS_SPEC_OBJECT(proto) && proto !== null) {
    throw MakeTypeError("proto_object_or_null", [proto]);
  }
  var obj = new $Object();
  obj.__proto__ = proto;
  if (!IS_UNDEFINED(properties)) ObjectDefineProperties(obj, properties);
  return obj;
}

So it's easy to see why the an empty constructor would be faster (should be almost tied with an object literal).

@mrdoob
Owner

So it's easy to see why the an empty constructor would be faster (should be almost tied with an object literal).

Uh, doesn't seem that easy for my poor little mind :S
Do you mind explaining a bit?

@gero3

Object.create is without a doubt slower then just calling a lightweight constructor like THREE.Geometry

http://jsperf.com/new-vs-object-create-including-polyfill/2

and internet explorer 8 still has no support for it.

http://kangax.github.com/es5-compat-table/#

But it has a very advantageous effect that it doesn't call the constructor of the object that needs to be created and becuase of that, has no sideeffects.
And the disadvantage of it being slower, shouldn't outweigh becuase they only get called a few times when the library gets loaded.
Also Object.create can be partly polyfilled.

if (typeof Object.create !== 'function') {
 Object.create = function(o, props) {
  function F() {}
  F.prototype = o;

  if (typeof(props) === "object") {
   for (prop in props) {
    if (props.hasOwnProperty((prop))) {
     F[prop] = props[prop];
    }
   }
  }
  return new F();
 };
}

So as end solution, I'm actually for changing it to Object.create if it could be fully polyfilled. (This is becuase other people can be using different polyfills.)

Which is why I suggest using an alternative which does what we need of Object.create but with full compability of all browsers.

THREE.extend = function(obj){
    var f = function(){}
    f.prototype = obj.prototype;
    return new f();
};

which would mean that the code would change to

 THREE.Camera.prototype = THREE.extend(THREE.Object3D);
 THREE.Camera.prototype.constructor = THREE.Camera;
@mrdoob
Owner

Object.create is without a doubt slower then just calling a lightweight constructor like THREE.Geometry

Thanks for testing this. It's slower indeed but, hmmm, these extra counts... :D

and internet explorer 8 still has no support for it.

Is three.js any use for IE8?

@gero3

oops indeed, I taught that ie8 had canvas support.
Then I indeed am for changing it too in object.create

@tJener
THREE.extend = function(obj){
    var f = function(){}
    f.prototype = obj.prototype;
    return new f();
};

But now you can't have side effects in the constructor. THREE.GeometryCount would have to be implemented differently.

@gero3

@tJener
this is the call that should generate sideeffects. So this one will not be changed.
https://github.com/mrdoob/three.js/blob/master/src/extras/geometries/PlaneGeometry.js#L8

This is the call that has the unneccessary sideeffects which suggested be implemented with THREE.extend
https://github.com/mrdoob/three.js/blob/master/src/extras/geometries/PlaneGeometry.js#L63

@tJener

@gero3
Ah ok, I see now. Thanks.

@niloy

Object.create will be better than new THREE.Object3D() since in the latter case, the constructor Object.3d will be executed. Object.create will simply establish the inheritance which is what we need. Also, Object.create was 30% faster on my chrome 19.

@gero3

http://jsperf.com/new-vs-object-create-including-polyfill/3

update to performance test to show difference between a simple and a complex constructor.

I think we should update it based on this.

@mrdoob
Owner

Sounds good to me!

I've started implementing the pattern and seems like just with this is enough:

THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype );

Do we still need this?

THREE.Mesh.prototype.constructor = THREE.Mesh;
@mrdoob mrdoob closed this Jun 26, 2012
@Ricket

This change seems to have broken CoffeeScript's convenient object oriented keywords. Namely, I have defined a class "ThingCamera" that extends THREE.PerspectiveCamera, and in its constructor I call the super, and the constructor of THREE.PerspectiveCamera is not called. Code basically looks like this:

class ThingCamera extends THREE.PerspectiveCamera
  constructor: (fov, aspect, near, far) ->
    super fov, aspect, near, far

I'm having a wicked time comprehending exactly what's going on (or not, as the case may be). It's looking like I'll need to switch to mimicking how PerspectiveCamera "extends" Camera and do away with the CoffeeScript keywords. Does this sound correct?

@gero3

@Ricket, Can you create a issue and a jsfiddle ??

@wvl wvl pushed a commit to wvl/three.js that referenced this issue Nov 28, 2012
@mrdoob (threejs src) Implemented Object.create( *.prototype ) pattern as per…
… discussion in #2080.
08b4f95
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment