123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- <!DOCTYPE html><html lang="en"><head>
- <meta charset="utf-8">
- <title>Prerequisites</title>
- <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
- <meta name="twitter:card" content="summary_large_image">
- <meta name="twitter:site" content="@threejs">
- <meta name="twitter:title" content="Three.js – Prerequisites">
- <meta property="og:image" content="https://threejs.org/files/share.png">
- <link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
- <link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
- <link rel="stylesheet" href="../resources/lesson.css">
- <link rel="stylesheet" href="../resources/lang.css">
- <script type="importmap">
- {
- "imports": {
- "three": "../../build/three.module.js"
- }
- }
- </script>
- </head>
- <body>
- <div class="container">
- <div class="lesson-title">
- <h1>Prerequisites</h1>
- </div>
- <div class="lesson">
- <div class="lesson-main">
- <p>These articles are meant to help you learn how to use three.js.
- They assume you know how to program in JavaScript. They assume
- you know what the DOM is, how to write HTML as well as create DOM elements
- in JavaScript. They assume you know how to use
- <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import">es6 modules</a>
- via import and via <code class="notranslate" translate="no"><script type="module"></code> tags. They assume you know how to use import maps.
- They assume you know some CSS and that you know what
- <a href="https://developer.mozilla.org/en-US/docs/Learn/CSS/Introduction_to_CSS/Selectors">CSS selectors are</a>.
- They also assume you know ES5, ES6 and maybe some ES7.
- They assume you know that the browser runs JavaScript only via events and callbacks.
- They assume you know what a closure is.</p>
- <p>Here's some brief refreshers and notes</p>
- <h2 id="es6-modules">es6 modules</h2>
- <p>es6 modules can be loaded via the <code class="notranslate" translate="no">import</code> keyword in a script
- or inline via a <code class="notranslate" translate="no"><script type="module"></code> tag. Here's an example</p>
- <pre class="prettyprint showlinemods notranslate lang-html" translate="no">
- <script type="importmap">
- {
- "imports": {
- "three": "./path/to/three.module.js",
- "three/addons/": "./different/path/to/examples/jsm/"
- }
- }
- </script>
- <script type="module">
- import * as THREE from 'three';
- import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
- ...
- </script>
- </pre>
- <p>See more details at the bottom of <a href="fundamentals.html">this article</a>.</p>
- <h2 id="-document-queryselector-and-document-queryselectorall-"><code class="notranslate" translate="no">document.querySelector</code> and <code class="notranslate" translate="no">document.querySelectorAll</code></h2>
- <p>You can use <code class="notranslate" translate="no">document.querySelector</code> to select the first element
- that matches a CSS selector. <code class="notranslate" translate="no">document.querySelectorAll</code> returns
- all elements that match a CSS selector.</p>
- <h2 id="you-don-t-need-onload-">You don't need <code class="notranslate" translate="no">onload</code></h2>
- <p>Lots of 20yr old pages use HTML like</p>
- <pre class="prettyprint showlinemods notranslate notranslate" translate="no"><body onload="somefunction()">
- </pre><p>That style is deprecated. Put your scripts
- at the bottom of the page.</p>
- <pre class="prettyprint showlinemods notranslate lang-html" translate="no"><html>
- <head>
- ...
- </head>
- <body>
- ...
- </body>
- <script>
- // inline javascript
- </script>
- </html>
- </pre>
- <p>or <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script">use the <code class="notranslate" translate="no">defer</code> property</a>.</p>
- <h2 id="know-how-closures-work">Know how closures work</h2>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function a(v) {
- const foo = v;
- return function() {
- return foo;
- };
- }
- const f = a(123);
- const g = a(456);
- console.log(f()); // prints 123
- console.log(g()); // prints 456
- </pre>
- <p>In the code above the function <code class="notranslate" translate="no">a</code> creates a new function every time it's called. That
- function <em>closes</em> over the variable <code class="notranslate" translate="no">foo</code>. Here's <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures">more info</a>.</p>
- <h2 id="understand-how-this-works">Understand how <code class="notranslate" translate="no">this</code> works</h2>
- <p><code class="notranslate" translate="no">this</code> is not magic. It's effectively a variable that is automatically passed to functions just like
- an argument is passed to function. The simple explanation is when you call a function directly
- like</p>
- <pre class="prettyprint showlinemods notranslate notranslate" translate="no">somefunction(a, b, c);
- </pre><p><code class="notranslate" translate="no">this</code> will be <code class="notranslate" translate="no">null</code> (when in strict mode or in a module) where as when you call a function via the dot operator <code class="notranslate" translate="no">.</code> like this</p>
- <pre class="prettyprint showlinemods notranslate notranslate" translate="no">someobject.somefunction(a, b, c);
- </pre><p><code class="notranslate" translate="no">this</code> will be set to <code class="notranslate" translate="no">someobject</code>.</p>
- <p>The parts where people get confused is with callbacks.</p>
- <pre class="prettyprint showlinemods notranslate notranslate" translate="no"> const callback = someobject.somefunction;
- loader.load(callback);
- </pre><p>doesn't work as someone inexperienced might expect because when
- <code class="notranslate" translate="no">loader.load</code> calls the callback it's not calling it with the dot <code class="notranslate" translate="no">.</code> operator
- so by default <code class="notranslate" translate="no">this</code> will be null (unless the loader explicitly sets it to something).
- If you want <code class="notranslate" translate="no">this</code> to be <code class="notranslate" translate="no">someobject</code> when the callback happens you need to
- tell JavaScript that by binding it to the function.</p>
- <pre class="prettyprint showlinemods notranslate notranslate" translate="no"> const callback = someobject.somefunction.bind(someobject);
- loader.load(callback);
- </pre><p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this"><em>this</em> article might help explain <code class="notranslate" translate="no">this</code></a>.</p>
- <h2 id="es5-es6-es7-stuff">ES5/ES6/ES7 stuff</h2>
- <h3 id="-var-is-deprecated-use-const-and-or-let-"><code class="notranslate" translate="no">var</code> is deprecated. Use <code class="notranslate" translate="no">const</code> and/or <code class="notranslate" translate="no">let</code></h3>
- <p>There is no reason to use <code class="notranslate" translate="no">var</code> <strong>EVER</strong> and at this point it's considered bad practice
- to use it at all. Use <code class="notranslate" translate="no">const</code> if the variable will never be reassigned which is most of
- the time. Use <code class="notranslate" translate="no">let</code> in those cases where the value changes. This will help avoid tons of bugs.</p>
- <h3 id="use-for-elem-of-collection-never-for-elem-in-collection-">Use <code class="notranslate" translate="no">for(elem of collection)</code> never <code class="notranslate" translate="no">for(elem in collection)</code></h3>
- <p><code class="notranslate" translate="no">for of</code> is new, <code class="notranslate" translate="no">for in</code> is old. <code class="notranslate" translate="no">for in</code> had issues that are solved by <code class="notranslate" translate="no">for of</code></p>
- <p>As one example you can iterate over all the key/value pairs of an object with</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">for (const [key, value] of Object.entries(someObject)) {
- console.log(key, value);
- }
- </pre>
- <h3 id="use-foreach-map-and-filter-where-useful">Use <code class="notranslate" translate="no">forEach</code>, <code class="notranslate" translate="no">map</code>, and <code class="notranslate" translate="no">filter</code> where useful</h3>
- <p>Arrays added the functions <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach"><code class="notranslate" translate="no">forEach</code></a>,
- <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map"><code class="notranslate" translate="no">map</code></a>, and
- <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter"><code class="notranslate" translate="no">filter</code></a> and
- are used fairly extensively in modern JavaScript.</p>
- <h3 id="use-destructuring">Use destructuring</h3>
- <p>Assume an object <code class="notranslate" translate="no">const dims = {width: 300, height: 150}</code></p>
- <p>old code</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const width = dims.width;
- const height = dims.height;
- </pre>
- <p>new code</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const {width, height} = dims;
- </pre>
- <p>Destructuring works with arrays too. Assume an array <code class="notranslate" translate="no">const position = [5, 6, 7, 1]</code>;</p>
- <p>old code</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const y = position[1];
- const z = position[2];
- </pre>
- <p>new code</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const [, y, z] = position;
- </pre>
- <p>Destructuring also works in function arguments</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const dims = {width: 300, height: 150};
- const vector = [3, 4];
- function lengthOfVector([x, y]) {
- return Math.sqrt(x * x + y * y);
- }
- const dist = lengthOfVector(vector); // dist = 5
- function area({width, height}) {
- return width * height;
- }
- const a = area(dims); // a = 45000
- </pre>
- <h3 id="use-object-declaration-short-cuts">Use object declaration short cuts</h3>
- <p>old code</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no"> const width = 300;
- const height = 150;
- const obj = {
- width: width,
- height: height,
- area: function() {
- return this.width * this.height
- },
- };
- </pre>
- <p>new code</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no"> const width = 300;
- const height = 150;
- const obj = {
- width,
- height,
- area() {
- return this.width * this.height;
- },
- };
- </pre>
- <h3 id="use-the-rest-parameter-and-the-spread-operator-">Use the rest parameter and the spread operator <code class="notranslate" translate="no">...</code></h3>
- <p>The rest parameter can be used to consume any number of parameters. Example</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no"> function log(className, ...args) {
- const elem = document.createElement('div');
- elem.className = className;
- elem.textContent = args.join(' ');
- document.body.appendChild(elem);
- }
- </pre>
- <p>The spread operator can be used to expand an iterable into arguments</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const position = [1, 2, 3];
- someMesh.position.set(...position);
- </pre>
- <p>or copy an array</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const copiedPositionArray = [...position];
- copiedPositionArray.push(4); // [1,2,3,4]
- console.log(position); // [1,2,3] position is unaffected
- </pre>
- <p>or to merge objects</p>
- <pre class="prettyprint showlinemods notranslate notranslate" translate="no">const a = {abc: 123};
- const b = {def: 456};
- const c = {...a, ...b}; // c is now {abc: 123, def: 456}
- </pre><h3 id="use-class-">Use <code class="notranslate" translate="no">class</code></h3>
- <p>The syntax for making class like objects pre ES5 was unfamiliar to most
- programmers. As of ES5 you can now <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes">use the <code class="notranslate" translate="no">class</code>
- keyword</a>
- which is closer to the style of C++/C#/Java.</p>
- <h3 id="understand-getters-and-setters">Understand getters and setters</h3>
- <p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get">Getters</a> and
- <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set">setters</a> are
- common in most modern languages. The <code class="notranslate" translate="no">class</code> syntax
- of ES5 makes them much easier than pre ES5.</p>
- <h3 id="use-arrow-functions-where-appropriate">Use arrow functions where appropriate</h3>
- <p>This is especially useful with callbacks and promises.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">loader.load((texture) => {
- // use texture
- });
- </pre>
- <p>Arrow functions bind <code class="notranslate" translate="no">this</code> to the context in which you create the arrow function.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const foo = (args) => {/* code */};
- </pre>
- <p>is a shortcut for</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const foo = (function(args) {/* code */}).bind(this));
- </pre>
- <p>See link above for more info on <code class="notranslate" translate="no">this</code>.</p>
- <h3 id="promises-as-well-as-async-await">Promises as well as async/await</h3>
- <p>Promises help with asynchronous code. Async/await help
- use promises.</p>
- <p>It's too big a topic to go into here but you can <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises">read up
- on promises here</a>
- and <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await">async/await here</a>.</p>
- <h3 id="use-template-literals">Use Template Literals</h3>
- <p>Template literals are strings using backticks instead of quotes.</p>
- <pre class="prettyprint showlinemods notranslate notranslate" translate="no">const foo = `this is a template literal`;
- </pre><p>Template literals have basically 2 features. One is they can be multi-line</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const foo = `this
- is
- a
- template
- literal`;
- const bar = "this\nis\na\ntemplate\nliteral";
- </pre>
- <p><code class="notranslate" translate="no">foo</code> and <code class="notranslate" translate="no">bar</code> above are the same.</p>
- <p>The other is that you can pop out of string mode and insert snippets of
- JavaScript using <code class="notranslate" translate="no">${javascript-expression}</code>. This is the template part. Example:</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const r = 192;
- const g = 255;
- const b = 64;
- const rgbCSSColor = `rgb(${r},${g},${b})`;
- </pre>
- <p>or</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const color = [192, 255, 64];
- const rgbCSSColor = `rgb(${color.join(',')})`;
- </pre>
- <p>or</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const aWidth = 10;
- const bWidth = 20;
- someElement.style.width = `${aWidth + bWidth}px`;
- </pre>
- <h1 id="learn-javascript-coding-conventions-">Learn JavaScript coding conventions.</h1>
- <p>While you're welcome to format your code any way you chose there is at least one
- convention you should be aware of. Variables, function names, method names, in
- JavaScript are all lowerCasedCamelCase. Constructors, the names of classes are
- CapitalizedCamelCase. If you follow this rule you code will match most other
- JavaScript. Many <a href="https://eslint.org">linters</a>, programs that check for obvious errors in your code,
- will point out errors if you use the wrong case since by following the convention
- above they can know when you're using something incorrectly.</p>
- <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const v = new vector(); // clearly an error if all classes start with a capital letter
- const v = Vector(); // clearly an error if all functions start with a lowercase letter.
- </pre>
- <h1 id="consider-using-visual-studio-code">Consider using Visual Studio Code</h1>
- <p>Of course use whatever editor you want but if you haven't tried it consider
- using <a href="https://code.visualstudio.com/">Visual Studio Code</a> for JavaScript and
- after installing it <a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint">setup
- eslint</a>.
- It might take a few minutes to setup but it will help you immensely with finding
- bugs in your JavaScript.</p>
- <p>Some examples</p>
- <p>If you enable <a href="https://eslint.org/docs/rules/no-undef">the <code class="notranslate" translate="no">no-undef</code> rule</a> then
- VSCode via ESLint will warn you of many undefined variables. </p>
- <div class="threejs_center"><img style="width: 615px;" src="../resources/images/vscode-eslint-not-defined.png"></div>
- <p>Above you can see I mis-spelled <code class="notranslate" translate="no">doTheThing</code> as <code class="notranslate" translate="no">doThing</code>. There's a red squiggle
- under <code class="notranslate" translate="no">doThing</code> and hovering over it it tells me it's undefined. One error
- avoided.</p>
- <p>If you're using <code class="notranslate" translate="no"><script></code> tags to include three.js you'll get warnings using <code class="notranslate" translate="no">THREE</code> so add <code class="notranslate" translate="no">/* global THREE */</code> at the top of your
- JavaScript files to tell eslint that <code class="notranslate" translate="no">THREE</code> exists. (or better, use <code class="notranslate" translate="no">import</code> 😉)</p>
- <div class="threejs_center"><img style="width: 615px;" src="../resources/images/vscode-eslint-not-a-constructor.png"></div>
- <p>Above you can see eslint knows the rule that <code class="notranslate" translate="no">UpperCaseNames</code> are constructors
- and so you should be using <code class="notranslate" translate="no">new</code>. Another error caught and avoided. This is <a href="https://eslint.org/docs/rules/new-cap">the
- <code class="notranslate" translate="no">new-cap</code> rule</a>.</p>
- <p>There are <a href="https://eslint.org/docs/rules/">100s of rules you can turn on or off or
- customize</a>. For example above I mentioned you
- should use <code class="notranslate" translate="no">const</code> and <code class="notranslate" translate="no">let</code> over <code class="notranslate" translate="no">var</code>.</p>
- <p>Here I used <code class="notranslate" translate="no">var</code> and it warned me I should use <code class="notranslate" translate="no">let</code> or <code class="notranslate" translate="no">const</code></p>
- <div class="threejs_center"><img style="width: 615px;" src="../resources/images/vscode-eslint-var.png"></div>
- <p>Here I used <code class="notranslate" translate="no">let</code> but it saw I never change the value so it suggested I use <code class="notranslate" translate="no">const</code>.</p>
- <div class="threejs_center"><img style="width: 615px;" src="../resources/images/vscode-eslint-let.png"></div>
- <p>Of course if you'd prefer to keep using <code class="notranslate" translate="no">var</code> you can just turn off that rule.
- As I said above though I prefer to use <code class="notranslate" translate="no">const</code> and <code class="notranslate" translate="no">let</code> over <code class="notranslate" translate="no">var</code> as they just
- work better and prevent bugs.</p>
- <p>For those cases where you really need to override a rule <a href="https://eslint.org/docs/user-guide/configuring#disabling-rules-with-inline-comments">you can add comments
- to disable
- them</a>
- for a single line or a section of code.</p>
- <h1 id="if-you-really-need-to-support-legacy-browsers-use-a-transpiler">If you really need to support legacy browsers use a transpiler</h1>
- <p>Most modern browsers are auto-updated so using all these features will help you
- be productive and avoid bugs. That said, if you're on a project that absolutely
- must support old browsers there are <a href="https://babeljs.io">tools that will take your ES5/ES6/ES7 code
- and transpile the code back to pre ES5 Javascript</a>.</p>
- </div>
- </div>
- </div>
- <script src="../resources/prettify.js"></script>
- <script src="../resources/lesson.js"></script>
- </body></html>
|