Three.js: Evaluate ES6 classes

Created on 19 Jun 2017  ·  92Comments  ·  Source: mrdoob/three.js

Why is this 'idiom' of inheritance being introduced into the code:

PointLight.prototype = Object.assign( Object.create( Light.prototype ), {

Seriously? Function(nested Function(ParentPrototype) COMMA SHOTGUN BRACKET?

The faithful two line style still in, for example, the Materials classes are much clearer and cleaner. Assign the prototype and then set the constructor. The end. Please don't ruin the library by catching JavaScript disease - the bizarre need to masturbate the way objects and inheritance are coded. One style throughout the library. No need to change it.

Suggestion

Most helpful comment

Until browsers can execute natively TypeScript, I prefer to keep using JavaScript.

All 92 comments

So you're suggesting using this pattern instead?

PointLight.prototype = Object.create( Light.prototype );
Object.assign( PointLight.prototype, {
class PointLight extends Light

hehe 😄 And no problems...

@sasha240100 someday...

@mrdoob not quite - the two ways you mention are directly equivalent. I think the OP is comparing

PointLight.prototype = Object.assign( Object.create( Light.prototype ), { 
    constructor: PointLight,
    prop1: 'something',
    method1: function someFunction() { .. },
    ...
});

with

function PointLight () { ... };

PointLight.prototype = Object.create( Light.prototype );

PointLight.prototype.constructor = PointLight;

PointLight.prototype.prop1 = 'something';

PointLight.prototype.method1 = function someFunction() { .. };

...

Which is the way it's done here for example.
As far as I can see these styles are equivalent - is there something I'm missing?
Or was the style changed to use Object.Assign once it became available and not updated across the codebase?

@looeee @bfred-it @mrdoob Why not just using rollup-babel ?

Comparison:
Current. es5 + es6 harmony modules.

import { LineBasicMaterial } from './LineBasicMaterial';
import { Color } from '../math/Color';

function LineDashedMaterial( parameters ) {

    LineBasicMaterial.call( this );

    this.type = 'LineDashedMaterial';

    this.scale = 1;
    this.dashSize = 3;
    this.gapSize = 1;

    this.setValues( parameters );

}

LineDashedMaterial.prototype = Object.create( LineBasicMaterial.prototype );
LineDashedMaterial.prototype.constructor = LineDashedMaterial;

LineDashedMaterial.prototype.isLineDashedMaterial = true;

LineDashedMaterial.prototype.copy = function ( source ) {

    LineBasicMaterial.prototype.copy.call( this, source );

    this.scale = source.scale;
    this.dashSize = source.dashSize;
    this.gapSize = source.gapSize;

    return this;

};


export { LineDashedMaterial };

ES2015+. Same code, but es2015+ with babel-plugin-transform-class-properties:

import { LineBasicMaterial } from './LineBasicMaterial';
import { Color } from '../math/Color';

export class LineDashedMaterial extends LineBasicMaterial {
  type = 'LineDashedMaterial';

  scale = 1;
  dashSize = 3;
  gapSize = 1;
  isLineDashedMaterial = true;

  constructor(parameters) {
    super();
    this.setValues( parameters );
  }

  copy(source) {
    super.copy(source);

    this.scale = source.scale;
    this.dashSize = source.dashSize;
    this.gapSize = source.gapSize;

    return this;
  }
}

ES6 features that would simplify three.js code:

I'm all for moving to ES2015+, we just need to find a way to output similar code than what we currently have out of it, so the performance stays the same in all cases.

I have a question in context of classes. How would we transfer methods like Vector3.unproject into the class syntax? The method actually uses a closure in order to create a new scope. This is an important mechanism that keeps the amount of object creations as low as possible.

Do we need Object.assign in these cases?

@Mugen87 @mrdoob Some interesting info on es6 performance. Especially on Object.assign:
image
From this article

How would we transfer methods like Vector3.unproject into the class syntax? The method actually uses a closure in order to create a new scope.

@Mugen87 Can't they just be non-exported module scoped objects? Something like this;

const tempMatrix = new Matrix();    

export default class Vector3{
    unproject() {
        // uses tempMatrix
    }
}

Ah yes, i think this should work 😊

@mrdoob Wow! It seems already working. Can't we make a branch, transform some classes to es6 and see how it compiles?


@satori99 As an idea of how to keep tempMatrix inside Vector3 code to avoid problems with globals:

export default class Vector3 {
    static tempMatrix = new Matrix();

    unproject() {
        // uses Vector3.tempMatrix
    }
}

@mrdoob Wow! It seems already working. Can't we make a branch, transform some classes to es6 and see how it compiles?

Sound good to me! I'm currently focusing on WebVR so it'll need to be someone else than me.

@sasha240100 The benefit of using module scoped vars is that they remain hidden from regular user code, which seems appropriate for temp variables.

I don't mind the "We are all adults here" pythonistic approach in regards to private vars, but temp variables shouldn't really pollute the namespace unnecessarily.

Also, It would be nice if those of us who have enabled native module support in our browsers could load the src files directly. This is a much more pleasant way to develop, without needing watchers and transpiling after every edit. Using class properties means this isn't possible as they are not part of the current class spec.

Apologies for butting in. We should probably change the issue title to something like - "Evaluate ES6 classes", since now the thread has changed to something completely different.

Why change the pattern @Mugen87 @satori99 ?

method =(()=>{ 
    const vec3forThisScope =...; 
    return (arg)=>{...}
})()

Why not try typescript, it can be compiled into other versions of js

TypeScript really would be a great option to think about because it's a transpiler + type checker and a superset of JavaScript, so it's easy to move a code base over to .ts files and gradually refactor to ES6 with type checking.

It may sound scary if you've never used TypeScript, but it's really not a big learning curve and would be a small price to pay for the benefits it would bring. The TypeScript community would be very happy to help with this transition and creating performance testing against the current library to make sure it's not being downgraded.

A few useful articles:

To quote of core developer Anders Hejlsberg, TypeScript was born in response to complaints from clients and internal teams that JavaScript didn't lend itself well to large applications.

The goal was to "strengthen JavaScript with things like classes, modules, and static typing", without sacrificing the advantage of its being open-standards and cross-platform; the result was a "language for application scale javascript development", built as a superset of the language.

Until browsers can execute natively TypeScript, I prefer to keep using JavaScript.

@mrdoob

I can't see that as a valid reason for not using TypeScript solely on the reason it can't be run directly in the browser. You wouldn't want it to run in the browser because of all the extra lines of code that is intended only for compile time checking. It's not currently a runtime checking language. So if it was ever used in the browser it's more than likely going to have all the typed code stripped away because it impacts performance and this would then be vanilla JavaScript code.

I think you're totally missing the point of using a typed language and the benefits it has in development in a large code base. You are still writing and using JavaScript, the whole point of TypeScript is that it is a superset of JavaScript. You write JavaScript with types, that's compiled into JavaScript in the specified ECMAScript target version, which is configurable in the compiler options, the permitted values are 'es3', 'es5', 'es2015', 'es2016', 'es2017' or 'esnext'.

Because Typescript is JavaScript it makes it possible to progressively migrate without having a massive headache of refactoring everything at once. It can be gradually done and improved by the community. It's no more work than what's being discussed here with refactoring to use ES6 classes. That's the only reason I mention it here instead of opening a new issue.

See TypeScript playground links below for great examples:

@joejordanbrown

Can you think of anyone that could possibly disagree with you about typescript being the best solution to this particular problem?

If you type in typescript vs into google, a few terms pop up, one of them being Flow. Searching for that, seems to yield a number of articles where people are debating the pros and cons of these two.

No types seems like more of a compromise than choosing one of these.

Save Typescript for projects that are more complicated than the result they create -- specially frontend frameworks that could have been implemented in HTML in the first place. My original point was to get rid of the JavaScript disease, not make it worse. JavaScript is a simple almost toy language that sometimes is used for complex results like three.js. Typescript is pointless.

On Sep 6, 2017, at 1:55 PM, Joe notifications@github.com wrote:

@mrdoob

I can't see that as a valid reason for not using TypeScript solely on the reason it can't be run directly in the browser. You wouldn't want it to run in the browser because of all the extra lines of code that is intended only for compile time checking. It's not currently a runtime checking language. So if it was ever used in the browser it's more than likely going to have all the typed code stripped away because it impacts performance and this would then be vanilla JavaScript code.

I think you're totally missing the point of using a typed language and the benefits it has in development in a large code base. You are still writing and using JavaScript, the whole point of TypeScript is that it is a superset of JavaScript. You write JavaScript with types, that's compiled into JavaScript in the specified ECMAScript target version, which is configurable in the compiler options, the permitted values are 'es3', 'es5', 'es2015', 'es2016', 'es2017' or 'esnext'.

Because Typescript is JavaScript it makes it possible to progressively migrate without having a massive headache of refactoring everything at once. It can be gradually done and improved by the community. It's no more work than what's being discussed here with refactoring to use ES6 classes. That's the only reason I mention it here instead of opening a new issue.

See TypeScript playground links for examples:

Classic JavaScript Example
Adding Types Example
Adding Types with error Example
Using Classes Example
Using Classes with error Example

You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or mute the thread.

^ i'd be fine even with the monstrous pattern, as long as it's consistent.

@joejordanbrown sounds like you're in love with typescript. feel free to fork the project and port it to typescript. three.ts! 🙌

@pailhead

That's a matter of choice, I'm sure many will agree and disagree, that's normal though right! You're always going to see "this vs that", "mine is better than yours". I understand that each has their own benefits. It's about weighing up the options available and seeing if they can benefit the project. Comparisons are a good thing, it pushes projects further.

You mention Flow, the problems I see with that are:

  • Flow licensing is BSD 3-clause "Facebook BSD+Patents License", Apache Software Foundation banned the use of this license in new projects. You can read more details here.

  • IDE support is lacking compared to TypeScript.

  • User base is small compared to TypeScript,

  • Available typings for public libraries are incomplete, TypeScript has plenty of well-maintained typings.

  • Documentation and resources are hard to find and are vague compared to TypeScript you will find great documentation, books, videos and lots of other e-learning resources.

  • Flow uses .js files that are flagged with // @flow, this can be confusing because you see the .js extension so expect JavaScript but in fact, it's FlowType. Where as TypeScript uses its own extension .ts. This also allows you to have the TypeScript and JavaScript output files with identical names in the same directory, which is ideal for a small project, obviously, that wouldn't be the case on a large project though because you'd be using a build system to manage the build process.

Even google is backing TypeScript in a big way, that shows the confidence they have in TypeScript. Read the post here or here.

Typescript has become allowed for unrestricted client development as of March 2017. TypeScript and Angular on TypeScript are used in Google Analytics, Firebase, and Google Cloud Platform and critical internal tools such as bug tracking, employee reviews, and product approval and launch tools.

I just thought I'd open the discussion about using a Typed language and see what everyone else thinks about the idea. It does seem that @mrdoob is totally against even discussing the idea though.


@arctwelve
I don't see how this project isn't complicated and how using a Typed language would affect it negatively.


@mrdoob
Not at all, I can just see the benefits it could have especially if a new branch is being created to update to ES6 classes. I think to answer with create your own fork called three.ts is just being silly. That's really against good OSS practices if everyone just forked OSS projects and modified their own source code instead of focusing on the 1 project and making it the best it can be. You would end up with really poor software or communities splitting and focusing on the project they preferer for whatever reason. Why can't you have an open discussion about the pros and cons?

Not to play the devils advocate, but it seems like he did

have an open discussion

it was just really short :)

I share a similar standpoint, it's a JS library and JS is standardized. You can't go wrong with choosing JS for a JS library, while you can if you choose something else. I just took Flow as one of the alternatives to typescript, dunno if there are others.

Anyway, it seems like we really went off topic.

Mugen87 changed the title from
Remove JavaScript Disease to Evaluate ES6 classes

The original title referred to (as far as i understand) to the lack of style consistency. In particular using Object.assign() in some places, and another pattern in others.
If i'd evaluate anything here, it would be the current title of the issue. Why is the issue of consistency being elevated to a discussion about using a new language?

I imagine that with both typescript and es6, the code should be fairly consistent.

I'd address this issue by updating this page:

https://github.com/mrdoob/three.js/wiki/Mr.doob's-Code-Style%E2%84%A2

and adding either:

A) "...use Object.assign ..."
B) "...dont use Object.assign"

One style throughout the library. No need to change it.

The faithful two line style still in, for example, the Materials classes are much clearer and cleaner.

It's in the first post.

I suggest:

  1. edit the title to reflect this sentence, discuss having one style throughout the library, editing the style guide etc.
  2. start a new discussion titled "evaluate es6 classes", where es6 classes would be evaluated
  3. start a new discussion titled "evaluate having three written in a typed language", where typescript and such would be discussed

Anyway, it seems like we really went off topic.

Indeed. @joejordanbrown feel free to create a new topic to discuss TypeScript.

Btw, it's also bad OSS practice to ignore previous conversations... https://github.com/mrdoob/three.js/issues/341#issuecomment-47000692

Back to the topic. I thought we already solved this issue?

https://github.com/mrdoob/three.js/issues/11552#issuecomment-319449068

We just need someone to give it a try.

Ok so... first of all

First pattern ( the best IMO ):

function MyClass() {...}

MyClass.prototype = Object.assign( Object.create( MyClassToInherit.prototype ), {

    constructor: MyClass,

    prop1: 'something',

    method1: function someFunction() { .. },

    ...

});

The second pattern:

function MyClass() {...}

MyClass.prototype = Object.create( MyClassToInherit.prototype );

MyClass.prototype.constructor = PointLight;

MyClass.prototype.prop1 = 'something';

MyClass.prototype.method1 = function someFunction() { .. };

...

@arctwelve This pattern was introduce for many reason. This is not masturbate !

First of all it allow a clear reading about the object inheritance. The Object.assign is clearly here about the object inheritance. Then you can't lose the inherited object in many and many line of MyClass.prototype.
Second, in case of multiple inheritance, this is much clearer too.
Third, the constructor of the class is readable and is not lose in many line like the first point.
Fourth, this allow to group properties and methods in the same location ( inside bracket ) which is very much clearer when you have 3, 4, 5... etc classes in the same file.
Fifth, this pattern allow to check correct copy for some "inherited" properties.

Finally ( @looeee ), this is for performance too. The first version is more optimized when file parsing occur, instead of multiple and multiple call to prototype.

Any way !

Nowaday, we should pass to ES6 syntax !

@mrdoob

Sound good to me! I'm currently focusing on WebVR so it'll need to be someone else than me.
We just need someone to give it a try.

Would you create an es6 branch ? Or it's on our own ?

The first version is more optimized when file parsing occur, instead of multiple and multiple call to prototype.

Are you sure about that? I'd expect Object.Assign would be slower, if anything. But in either case I doubt it's enough of a performance overhead to worry about.

Absolutely: https://jsperf.com/inline-prototype-vs-assign-prototype/1

Under chrome version 61.0.3163.100 (Build officiel) (64 bits) Assigned prototype are around 60% faster

Interesting. Thanks for making the test.

That result doesn't hold up for all browsers though. On Firefox they are nearly the same (Object.Assign ~3% faster), while on Edge Object.Assign is ~33% slower.

But in any case I still don't think this is relevant as an argument for which style is better - even the slowest overall (inline proto in Chrome) is still running at >180,000 operations a second, and there's maybe a couple of thousand of these setups done in the code. So we are talking about a difference of a few milliseconds here, probably.

For me, the Object.Assign style is cleaner and easier to read, and that's the main argument in favour of it.

Yes this is about few miliseconds per file and only when file is parsed the first time by javascript engine... but for a big library like threejs the gain could be reasonably around 200 miliseconds on page load.
Do not forgot the limit of 3scd for front user that aren't able to wait more !

I'm trying to make Angular project with threejs and it always looks like I'm hacking every part of threejs.
First of all it is es5 syntax with THREE constant that must exist, for example, if I need OrbitalControls.
We have threejs typings, but it will be more convenient to have them in the same package. The typings have OrbitalControls, but we can't simply import like import { OrbitalControls } from 'three;.
Webpack have tree shaking, so in case of es6 we could include all we need inside one project and not to move them to separated one.
@mrdoob so why Typescript is so bad? It will be compiled to ES any version with typings anyway.
It is also used in many other frameworks like React.

@FriOne I'm not sure if @mrdoob really thinks Typescript is bad but I think he is against discussing Typescript in this issue/thread because it's not the topic of this thread. I think ES6 classes don't work against or for Typescript. If you transform the codebase to ES6 it's even easier to port the codebase over to Typescript because it's syntax is very similar. Yes I think Typescript could enable a lot of new options. For example, it could help to "transpile" a more optimized code, it helps new developers to learn the library faster, maybe it opens doors for experiments in the future e.g.: https://github.com/AssemblyScript/assemblyscript. I think there are a lot of advantages with Typescript @joejordanbrown described the pros in detail.

But back to topic: I think adopting ES6 classes would be a step forward and after that, we could discuss the Typescript thing. So let's do one step after each other

@tschoartschi, think typescript can help with migration to es6 classes and other refactoring. I don't have such migration experience, it might be I'm wrong.

@FriOne it depends 😉 of course Typescript could help because the compiler could tell you all your errors and mistakes but you would need to set up the whole build pipe first to work with Typescript. Furthermore, you need to assess if Typescript is the correct fit or not. I think it's fine to convert to ES6 classes first and then think about Typescript.

Hey, we're a group of 5 students from KTH looking to contribute to an open source project for a course and would like to try and convert a part of the project to the new ES6 syntax.

I ported Three.js to TypeScript a while back (r82) along with a few of the examples, as a proof-of-concept.

https://github.com/flyover/three.ts

The examples take a bit to load, as the TypeScript source is transpiled on the fly. They load as fast as the originals when using the transpiled JavaScript.

I'd love to see three.js ported to typescript. I feel the project badly needs to be modernized if it wants to stand the test of time for the next web generations. One day, we may even see it working with AssemblyScript and running in WASM. If not threejs, then something else surely will.

This is completed @WestLangley and @mrdoob

@bhouston what is the conclusion here?

@pkieltyka yes I also think TypeScript would make a lot of sense for a library like Three.js. Beside all the technical pros it would also ease the use of the library and helps newbies to explore the API. But I also think it's important to finish all the ES6 Stuff first and then consider TypeScript. I think from the latest JavaScript it's not too complicated to add types in form of TypeScript.

@flyover nice too see a proof-of-concept for a TypeScript version of Three.js. Did you got feedback from the maintainers of Three.js?

I also support typescript for three.js. typescirpt is basically best
practice and raw JavaScript isn't any more.

On Sat, Jan 5, 2019, 4:13 AM tschoartschi <[email protected] wrote:

@pkieltyka https://github.com/pkieltyka yes I also think TypeScript
would make a lot of sense for a library like Three.js. Beside all the
technical pros it would also ease the use of the library and helps newbies
to explore the API. But I also think it's important to finish all the ES6
Stuff first and then consider TypeScript. I think from the latest
JavaScript it's not too complicated to add types in form of TypeScript.

@flyover https://github.com/flyover nice too see a proof-of-concept for
a TypeScript version of Three.js. Did you got feedback from the maintainers
of Three.js?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/mrdoob/three.js/issues/11552#issuecomment-451639995,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAj6_bkdND7I0_F4AJcBV0DYLpToUIVhks5vAGykgaJpZM4N9vH8
.

@bhouston I agree that TypeScript is a great technology but I wouldn't put it the way you did. I think we should always stay as close as possible to raw JavaScript and add the TypeScript features on top. Since TypeScript follows the JavaScript spec very closely, TypeScript really reads like ES6 with types.

For a library like three.js TypeScript could be really beneficial and could be adopted gradually. So we wouldn't need a "big-bang" rewrite, especially not after all the ES6 refactors are finished.

Would be interesting to hear if @mrdoob 's stance on TypeScript changed. TypeScript seems to become the "defacto" standard for "typed JavaScript" (note that I put my claims under apostrophe since this are no hard facts)

The first step should be adopting ES6 features, especially classes, arrow function, 'let', and 'const'.

Once we've done that we can properly discuss typescript support since, as @roomle-build points out, it's easy to incrementally add typescript features on top of ES6 code gradually if we decide to.

Doing both at once seems like it would over-complicate things, to me.

Great to hear that TypeScript could be an option at some point in the future :-) maybe we could reuse some of the work done by @flyover

But I totally agree with @looeee to finish all the ES6 stuff first and then focus on the next steps

finish all the ES6 stuff

I'd be happy if we could at least start it 😅

A good half way step to TypeScript woudl be to add type files beside every JavaScript file. Thus there would be both:

Vector3.js
Vector3.d.ts

This gives us all the benefits of TypeScript as a sidecar files.

Right now there is a @types/three file but it is out of date and maintained separately -- thus it will be always out of data.

The main competitor to Three.JS is Babylong and it is fully typescript and I believe it benefits from this.

But killing @types/three and integrated it as side cars type definition files into Three proper would be a great first step.

We also need to get all all examples integrated into /src with some type of tree shaking.

Right now the code structure for Three.JS is so 2014 and just a pain to work with.

All of the examples?

Examples/js

On Mon, Jan 7, 2019, 10:25 AM Dusan Bosnjak <[email protected] wrote:

All of the examples?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/mrdoob/three.js/issues/11552#issuecomment-451970482,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAj6_Q3Kakb5Qn2DqGbMVvLkW_28cOyaks5vA2b5gaJpZM4N9vH8
.

@bhouston

A good half way step to TypeScript woudl be to add type files beside every JavaScript file. Thus there would be both:

Vector3.js
Vector3.d.ts

Would .d.ts files act as .h files in c?

Would .d.ts files act as .h files in c?

That is a very good analogy. Interesting someone has done most of this already here: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/three Thus really it would just taking ownership of these type files and integrating it into Three.js. If you want we can create a PR that integrates this into Three.js and splits its up proper.

To show how popular Typescript is look at how many @Types/three is downloaded per week:

https://www.npmjs.com/package/@types/three - 63,000 downloads per week.

Impressive!

If you want we can create a PR that integrates this into Three.js and splits its up proper.

Sounds good to me 👍

Is it possible to run a commandline check, similar to eslint, ensuring that the type files and source .js files align? If we're taking ownership of the d.ts files, it would be strongly preferable to have some way of regularly verifying that they match.

To show how popular Typescript is look at how many @Types/three is downloaded per week:

https://www.npmjs.com/package/@types/three - 63,000 downloads per week.

I would attribute this more to the popularity of Visual Studio Code, because it happens automatically when you use Visual Studio Code, via their Automatic Type Acquisition feature.
That said, it can't be ignored.

@flyover @bunnybones1 @mrdoob @looeee @donmccurdy @bhouston @roomle-build @pkieltyka @FriOne @joejordanbrown since you all seem to be interested in TypeScript I wanted to note that I created a new issue concerning TypeScript. I think it makes sense to move all the TS discussions there. If you are interested you can find it here: https://github.com/mrdoob/three.js/issues/15545

I am willing to commit time to port to ES6 classes. I think this will be a good bridge solution to one day supporting Typescript.

The first step is to convert some of the add-ons to ES6.

https://github.com/mrdoob/three.js/tree/dev/examples/jsm

Controls, Loaders and Exporters are good candidates. Feel free to help!

@mrdoob just to confirm, you mean converting them to ES modules but not using other ES6 features yet, right?

I assume the process of doing that conversion is:

  1. Use modularize.js script to convert the original file to an ES module.
  2. Clean up issues if needed.
  3. At some point (this release, or in the near future) we'll start using rollup-examples.config.js to convert the ES modules to UMD modules, at which point the version in examples/js is no longer maintained by hand.
  4. Once that is stable, we can consider other changes like introducing ES6 features.

@mrdoob just to confirm, you mean converting them to ES modules but not using other ES6 features yet, right?

Yeah, sorry. I should have specified.

I and @bhouston have created the promised PR that contributes the *.d.ts files for most of the Three.JS project. https://github.com/mrdoob/three.js/pull/15597

I just can‘t wait for ES6 classes and Typescript definitions. Everytime I‘m working with three.js i have to copy paste hundred of code lines to reimplement one function that lies in an unaccessable scope. It’s really not an elegant way to work if you have to optimize your scenes. It would take the workflow to a new level to finaly be able to extend and override functions.
So please make class functions at least „protected“ and accessable without exception 🤗

Everytime I‘m working with three.js i have to copy paste hundred of code lines to reimplement one function that lies in an unaccessable scope. ... So please make class functions at least „protected“ and accessable without exception.

@dionysiusmarquis I'm not sure I understand what you mean here ... the existing TS typings have functions you want to use incorrectly marked as private? Or something about the current prototype-style class definitions makes use in TS hard? Could you share an example?

Everytime I‘m working with three.js i have to copy paste hundred of code lines to reimplement one function that lies in an unaccessable scope. ... So please make class functions at least „protected“ and accessable without exception.

@dionysiusmarquis I'm not sure I understand what you mean here ... the existing TS typings have functions you want to use incorrectly marked as private? Or something about the current prototype-style class definitions makes use in TS hard? Could you share an example?

I just wanted to implement a specific shadow map for a specific scenario. It would have helped to be able to override getDepthMaterial for example. At the moment i inject my own WebGLShadowMap in the build process with a copy/paste version including my desired changes.
It would be so nice if every single three.js class would expose as much as possible. With typescript you can easily flag functions as private or protected to describe the intended purpose. My ES6 Class/Typescript ShadowMap for example looks like this:

interface MaterialCache {
  [uuid: string]: {[uuid: string]: MeshDepthMaterial}
}

class ShadowMap {
  public enabled: boolean = false
  public autoUpdate: boolean = true
  public needsUpdate: boolean = false
  public type: ShadowMapType

  …

  protected depthMaterials: MeshDepthMaterial[]
  protected materialCache: MaterialCache

  constructor (renderer: WebGLRenderer, objects: WebGLObjects, maxTextureSize: any) {
    …
  }

  protected getDepthMaterial (object: Object3D, material: Material) {
    …
  }
}

export { ShadowMap as WebGLShadowMap }

It just feels like home if you are allowed to modify "low level" classes

I have a question in context of classes. How would we transfer methods like Vector3.unproject into the class syntax? The method actually uses a closure in order to create a new scope. This is an important mechanism that keeps the amount of object creations as low as possible.

Do we need Object.assign in these cases?
@Mugen87

You can do closures with classes now. It's just not obvious how to navigate in that syntactic sugar. See my answer on StackOverflow: https://stackoverflow.com/questions/39297258/iife-in-es6-class-literal/56077521#56077521 (I must be humble and admit that I don't fully understand how/why it works.)

Sorry, but this code looks like an anti-pattern and we should not adapt this in the project. There are proper solutions for managing variables in module scope. I suggest to go this route.

@Mugen87 yes I also think we should make good use of module-scope. This would also help for things like tree-shaking. This was already discussed in many other issues like this one: https://github.com/mrdoob/three.js/issues/6241#issuecomment-398703521

Sorry, but this code looks like an anti-pattern and we should not adapt this in the project. There are proper solutions for managing variables in module scope. I suggest to go this route.

No problem. I just mentioned the possibility. If you have a better/cleaner solution under way, I am eager to see it in action. The Three.js project (usage, source, discussions etc.) has been my main source of JS knowledge, so introducing new and better programming patterns to Three.js will probably benefit me and my projects too. Nothing to be sorry for . ;-)

@Mugen87 Can you elaborate a little on why you consider class-IIFEs an anti pattern if only so I can learn and understand a bit more? I understand that modules scope variables inherently but that's no different than how core was being built previously. And with so many functions using cache variables it's going to be really important to make sure none of the functions collide or use variables at the same time -- the function scoping makes this easier to manage and maintain, which is why I don't see it as an anti-pattern (at least when compared to throwing all the cache variables in the module scope).

Can you elaborate a little on why you consider class-IIFEs an anti pattern

I would not use an approach that potentially impede tree-shaking like mentioned here: https://github.com/mrdoob/three.js/pull/14695. Besides, I think the entire pattern looks like a hack. Using less "fancy" approaches usually work better. Avoiding unnecessary closures should also improve the code execution performance in general (can't prove this with a reference but I've heard it on a talk some time ago).

And with so many functions using cache variables it's going to be really important to make sure none of the functions collide or use variables at the same time.

The IIFEs are mainly used in the math classes. Since we have a good amount of test coverage there (@gero3 did a great job lately by adding more unit tests), it should be easier to make the removal of them robust.

It is fine to remove IIFEs. I think I am responsible for it back in 2013. It was to get rid of a static variable pattern that was hard to maintain. It was done in this PR:

https://github.com/mrdoob/three.js/pull/2941

https://github.com/mrdoob/three.js/issues/2936

And discussed even earlier here:

https://github.com/mrdoob/three.js/pull/2920#issuecomment-12217793

Interesting fact, when we put these in, there was not a code performance difference.

I assume the process of doing that conversion is:

  1. Use modularize.js script to convert the original file to an ES module.
  2. Clean up issues if needed.
  3. At some point (this release, or in the near future) we'll start using rollup-examples.config.js to convert the ES modules to UMD modules, at which point the version in examples/js is no longer maintained by hand.
  4. Once that is stable, we can consider other changes like introducing ES6 features.

hey. might've missed it but what was this our incremental steps towards classes?

  1. [x] Use modularize.js script to convert the original file to an ES module.
  2. [x] Clean up issues if needed.
  3. [ ] At some point (this release, or in the near future) we'll start using rollup-examples.config.js to convert the ES modules to UMD modules, at which point the version in examples/js is no longer maintained by hand.
  4. [ ] Once that is stable, we can consider other changes like introducing ES6 features.

In the steps above, we've essentially finished steps (1) and (2).

Step (3) we are not doing that way any more, but will instead deprecate and remove the examples/js folder at some point (see https://github.com/mrdoob/three.js/pull/18749).

I think that means that step (4), introducing ES6 classes to examples/jsm can only be done (for now) on the very few examples that aren't generated from examples/js source versions. Once examples/js is removed, we can do the rest.

^But I might be reading too much between the lines here, maybe @Mugen87 or @mrdoob can confirm?

IMO, the project should focus on the deprecation and removal of examples/js in order to achieve a module only code base. In the next step, I would recommend to move on with the class migration. Starting with the examples and then migrating the core.

perfect. thank you. will take a closer look those ways

Reposted from closed issue.
Hi there,

I wanted to create this issue to try and tease out everyone's thoughts on how we'd like to move forward with class migration.

My quick summary of what I've come across so far:

  • We should deprecate the examples folder before doing anything else
  • There are parts of src/ that aren't extended by any of the examples that could be converted
  • With some changes to the modularize.js script we could get started on examples/js/ which generates the corresponding examples/jsm scripts.

Have I missed anything?

We should deprecate the examples folder before doing anything else

I'm not sure who is responsible for any specific next step on the examples/js folder. Unless we can articulate that plan, I'd prefer that this didn't block converting things to ES classes. For that reason, I somewhat disagree with https://github.com/mrdoob/three.js/issues/11552#issuecomment-592768708. 🙂

further to that, it appears that we're just waiting on a date to be set

Also, as part of a bit of revision I did, I found this comment about how other ES2015 features could be introduced via the current rollup build process. There is even an example given.

edit: here's a gist of a another quick example

@DefinitelyMaybe I've got some time and I'd love to help. Can I take some items on the dependencies.json list?

@DefinitelyMaybe What is the best way to deal with inheritance from a deep ancestor like Object3D? For example, I ran into breakage converting src/audio/AudioListener.js:

[ROLLUP] bundles src/Three.js → build/three.js...
[ROLLUP] (!) Error when using sourcemap for reporting an error: Can't resolve original location of error.
[ROLLUP] src/audio/AudioListener.js: (137:1)
[ROLLUP] [!] Error: 'return' outside of function
[ROLLUP] src/audio/AudioListener.js (137:1)
[ROLLUP] 135:   };
[ROLLUP] 136: 
[ROLLUP] 137:   return AudioListener;
[ROLLUP]        ^
[ROLLUP] 138: }(Object3D));
[ROLLUP] Error: 'return' outside of function

we were going to make a note at the top of the file about any issues we run into, if Im remembering correctly.

I misunderstood what was happening with AudioListener... After some debugging, I think there is a matching issue in the bubleCleanup transform. See https://github.com/mrdoob/three.js/pull/19934#issuecomment-667411997 for some more info. I ran into this with a few different classes, so we'll probably have to get it sorted before going much further.

this will be the case for some of the files but not all. We were anticipating such an issue.

For those following along, please have a quick read of the discussion over here.

In the mean time, dibs on src/loaders!

Was this page helpful?
0 / 5 - 0 ratings