-
-
Notifications
You must be signed in to change notification settings - Fork 35.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
instanceof operator considered harmful #6362
Comments
@kumavis Is there any situation you can think of where |
While most of the uses of instance of are for class-specific code, which belongs in the specific class, as noted in the browserify issue, instanceof works perfectly fine in all cases I have used it (ignoring the case of sharing objects between frames, as this is wrong). If you want to add a thing to the threejs world, it needs to extend THREE.Object3D, just like everything else. If you want to create a custom mesh, it should extend THREE.Mesh. If you are creating a custom cube camera with slightly different functionality, it should extend THREE.CubeCamera (all of these are my own personal use cases). An instance (foo) of the following will pass all of these:
|
@coballast thats a good point, and I don't have a good example of when we need to do class checking. |
@HMUDesign so the case I'm imagining is if you have the following dep graph
Both depA and depB produce instances of Object3D subclasses. If there is class detection going on then it will fail. One way to solve this is passing the three.js global down to the deps, but thats lame and breaks the point of a nested dep graph. |
this seems to be heavily related to #6241 |
@makc to clarify, this is not a necessary change for #6241, but it is related to the principles of modularity. Abstraction is the greatest tool we have in programming -- the framework approach puts an endcap to abstraction, whereas the modular approach allows us to build even taller pyramids, to stand on the shoulders of even larger giants. |
+1 – this is an excellent perspective. |
I am currently observing an issue related to instanceof which I find strange: in WebGLRenderer.renderObjects() I can observe in the debugger that in this part: if ( buffer instanceof THREE.BufferGeometry ) {
_this.renderBufferDirect( camera, lights, fog, material, buffer, object );
} else {
_this.renderBuffer( camera, lights, fog, material, buffer, object );
} the check does not always work. I see objects which have a .type="BufferGeometry" that are not recognized by instanceof THREE.BufferGeometry and the code executes .renderBuffer() instead of renderBufferDirect(). In the same test other BufferGeometry objects are recognized. All the geometries in this case had been created using |
how is that possible? fromGeometry sits in BufferGeometry.prototype and returns |
@Quasimondo Oh, that's weird. Any chance you could create a jsfiddle that reproduces the issue? |
It can happen if you include three.js twice. Modify the getting started example to include |
Ohm... Whyyyyyy |
Basically this:
|
Interesting... I wish Javascript had some sort of if ( THREE !== undefined ) return; |
if ( THREE !== undefined ) throw new Error('THREE already defined -- carry on'); |
@dubejf I guess I don't understand why you're loading |
Oh! that works? We should add it then. |
I mean, how does someone even think of that? |
Oh well - I'm very sorry but it looks like now I cannot recreate the conditions under which it happened. Now it works as expected again. |
@kumavis To clarify, I'm not loading Another possibility to explain
One way to prevent the script from executing twice would be to wrap the content of
|
I am pretty sure that I am not loading three.js twice. And no iframes in use either. The setup where I observed that behavior was a combination of loading a Collada scene, merging several geometries and then converting them to BufferGeometry. |
I just could reproduce the situation and it turns out that it's a glitch in the Firefox debugger. As you can see in the screenshot I set a breakpoint at the ´_this.renderBuffer(´ line - which should not be called if buffer is instanceof BufferGeometry, but FF stopped there. Setting a breakpoint inside renderBuffer itself though does not trigger a break, so it is actually never called. Sorry for the fuss. |
@dubejf you're right about the closure, thats the best way to handle early top-level returns (and also avoid accidently implicit creating some globals). |
@Quasimondo thats a weird one -- if it turns out to be actually related to three.js we can reopen this issue. |
The
instanceof
operator is a nice and easy way of doing class detection in javascript in a single project. However side effects creep in when we look at this at the ecosystem level.A project composed of three.js-based libraries will run into difficulties doing class detection. This is because they may have their own copies of the classes. One instance of Object3D may fail the
instanceof
test against the other module's Object3D class.There are a number of issues originating from the use of this pattern:
It would also be a requirement for the proposed module-focused architecture change:
The primary solution is to moving to a type detection via feature detection as proposed by @substack.
The usage would look something like this:
It should return true if the obj appears to be an instance of the class or a subclass. It will perform the detection by checking for the presence of properties and methods on the obj associated with the class. We can still use
instanceof
to short circuit and fallback to feature detection for performance.This would become part of the standard three.js class definition pattern.
Please suggest any other approaches that could work.
The text was updated successfully, but these errors were encountered: