JavaScript: Difference between revisions

From David's Wiki
No edit summary
 
(38 intermediate revisions by the same user not shown)
Line 1: Line 1:
JavaScript is a single-threaded programming language. In the browser, JS is event-driven, meaning that after initialization, the JS engine is idle until an event occurs such as a button being pressed in the DOM.
This page is a mostly about browser-based JavaScript or ECMAScript usage and interaction with the HTML DOM (window).
This page is a mostly about browser-based JavaScript or ECMAScript usage and interaction with the HTML DOM (window).
For server and desktop application JavaScript usage, please see the [[NodeJS]] page.
For server and desktop application JavaScript usage, please see the [[NodeJS]] page.


==Usage==
==Language Syntax==
===Arrays===
General Ecmascript syntax. Parts of this section applies to browser-side JS and server-side JS (Node.js, Deno), though details may vary between runtimes.
<syntaxhighlight lang="javascript">
let arr = [1,2,3];
 
// Map
arr.map(x => x > 2); // [false, false, true]
 
// Reduce or Fold
// Note that if you do not provide an initial accumulator,
// then the first element will be your accumulator
// I.e. the first call to your function will be (arr[0], arr[1])
arr.reduce((acc, x) => acc + x, 0); // 6
</syntaxhighlight>


===Canvas===
===Video===
===Regular Expressions (Regex)===
===Regular Expressions (Regex)===
[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions Reference]
[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions MDN Guide]
<syntaxhighlight lang="javascript">
<syntaxhighlight lang="javascript">
var myRegex = /(\d+),(\d+)/;
const myRegex = /(\d+),(\d+)/;
var myStr = "124,52";
const myStr = "124,52";
var match = myStr.match(myRegex);
const match = myStr.match(myRegex);
// Captures
// Captures
console.log(match[1], match[2]);
console.log(match[1], match[2]);
console.table(match);
console.table(match);
</syntaxhighlight>
</syntaxhighlight>


===Classes===
===Classes===
Line 44: Line 31:
// To add to the prototype
// To add to the prototype
Object.assign(Rectangle.prototype, {
Object.assign(Rectangle.prototype, {
  constructor: Rectangle,
   getSize: function() {
   getSize: function() {
     return this.height * this.width;
     return this.height * this.width;
Line 63: Line 51:
   }
   }
}
}
</syntaxhighlight>
===Promises===
Added in ES2015 (ES6)<br>
See [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise Promises]<br>
In general, a function returns a promise if it needs to perform asyncronous tasks.<br>
<syntaxhighlight lang="javascript">
function myFunc() {
  // Do syncronous things here
  return new Promise((resolve, reject) => {
    // Do asyncronous things here
    // Return data
    resolve(myData);
    // Or if we have an error
    reject(myError);
  });
}
myFunc()
  .then(data => {
    console.log(data);
  }).catch(err => {
    console.error(err);
  });
</syntaxhighlight>
===Async-await===
Added in ES2017
See [https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await Async_await]<br>
Async functions return implicit promises. They allow you to use keyword <code>await</code> to get results from other promises or functions.
===Enums===
See [https://stackoverflow.com/questions/287903/what-is-the-preferred-syntax-for-defining-enums-in-javascript Stack overflow]
Ecmascript doesn't have a built-in way to define ENUMS. One way is to use <code>Object.freeze</code>:
<syntaxhighlight lang="js">
const WORKERSTATUS = Object.freeze({
  ACTIVE: "ACTIVE",
  DISABLED: "DISABLED",
});
</syntaxhighlight>
; Notes
* This does not enforce any kind of type. If you need that, use [https://www.typescriptlang.org/docs/handbook/enums.html typescript enums].
===<code>var</code> vs <code>let</code>, <code>const</code>===
* <code>var</code> is the old way of declaring variables. It has function-scope rather than block-scope. Note that <code>var</code> can also be redefined which can hide errors.
* <code>let</code> and <code>const</code> are post-ES6 ways which have block-scope.
These days you should never use <code>var</code>. 
The Google [https://google.github.io/styleguide/jsguide.html JS Style Guide] and [https://google.github.io/styleguide/tsguide.html TS Style Guide] suggest using <code>const</code> by default.
==Browser Usage==
For basic dom manipulation, see [https://htmldom.dev/ https://htmldom.dev/].
===Inputs===
====Showing an input image====
<syntaxhighlight lang="javascript">
const myInput = document.getElementById("myInput");
const myImage = document.getElementById("myImage");
myInput.addEventListener('change', function() {
    if (myInput.files.length !== 1) {
        return;
    }
    let image = myInput.files[0];
    const reader = new FileReader();
    reader.onload = function(e) {
        myImage.src = e.target.result;
    };
    reader.readAsDataURL(image);
});
</syntaxhighlight>
===Canvas===
===Picture===
===Video===
Your HTML:
<syntaxhighlight lang="html">
<video id="myVideoElt" controls>
</video>
</syntaxhighlight>
Your JS:
<syntaxhighlight lang="javascript">
const myVideo = document.getElementById("myVideoElt");
// Create a new source
const newSource = document.createElement("source");
const myVideoUrl = "https://interactive-examples.mdn.mozilla.net/media/examples/flower.webm";
newSource.setAttribute("src", myVideoUrl);
myVideo.appendChild(newSource);
myVideo.play();
</syntaxhighlight>
</syntaxhighlight>


==Compilation==
==Compilation==
===Webpack===
===Babel===
==Websockets==
How to use Websockets
===Getting Started===
<syntaxhighlight lang="javascript">
let ws = new WebSocket(this.SERVER_URL);
ws.onopen = function(event) {
  console.log("Websocket opened");
  ws.send("Hi");
};
ws.onmessage = function(event) {
  console.log("message received");
  console.log(event.data);
};
ws.onclose = function() {
  console.log("WS Closed");
};
</syntaxhighlight>
==Data Structures==
JavaScript traditionally has arrays and objects (hashmap) data structures.<br>
ES2015 (ES6) adds several collections including: Map, Set, WeakMap, WeakSet<br>
[https://www.sitepoint.com/es6-collections-map-set-weakmap-weakset/ ES6 Collections]
===Arrays===
<syntaxhighlight lang="javascript">
let arr = [1,2,3];
// Map
arr.map(x => x > 2); // [false, false, true]
// Reduce or Fold
// Note that if you do not provide an initial accumulator,
// then the first element will be your accumulator
// I.e. the first call to your function will be (arr[0], arr[1])
arr.reduce((acc, x) => acc + x, 0); // 6
</syntaxhighlight>
===Objects===
Objects are maps in JavaScript. They are typically implemented as hashmaps by the JS engine.<br>
Note that you can only use numbers and strings as keys.<br>
Learn more about the implementation at [https://v8.dev/blog/hash-code https://v8.dev/blog/hash-code]<br>
<syntaxhighlight lang="javascript">
let my_map = {};
// or my_map["my_key"]
my_map.my_key = "my_value";
"my_key" in my_map;
// Loop over keys
for (let key in a) {
  console.log("Key:", key);
}
</syntaxhighlight>
===Map===
[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map MDN Map] 
[https://www.ecma-international.org/ecma-262/10.0/index.html#sec-map-objects ES2019 (ES10) Map Specification] 
This is your typical hashmap.
<syntaxhighlight lang="js">
let myMap = new Map();
let keyObj = {};
// Set value
myMap.set(keyObj, 'value associated with keyObj');
// Get size
myMap.size;
// Get value
myMap.get(keyObj);
// Check if key is in the map
myMap.has(keyObj);
// Delete
myMap.delete(keyObj);
</syntaxhighlight>
;Notes
* You can mix and match types of keys
* The hash for objects are randomly generated under the hood
* Do not use <code>[]</code> to get or set from the map
* Iterating over a Map will be in order of insertion
;Saving as json
See [https://2ality.com/2015/08/es6-map-json.html es6 map json]<br>
<syntaxhighlight lang="javascript">
const data = JSON.stringify([...myMap]);
myMap = new Map(JSON.parse(data));
</syntaxhighlight>
===Set===
[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set MDN Set]
<syntaxhighlight lang="js">
let my_set = new Set();
// Returns the set itself
my_set.add(key);
my_set.has(key);
my_set.clear();
// Returns true if key was in the set
my_set.delete(key);
</syntaxhighlight>
===WeakMap===
Unlike map, weakmap holds weak references to its keys. This allows keys to be garbage collected when no reference to it remains.
Note that once keys are garbage collected, they are also removed from the weakmap since you can no longer access them.
You can not iterate through a weakmap.
===WeakSet===
==WebXR==
<syntaxhighlight lang="javascript">
// Check for VR support
if ("xr" in navigator && "isSessionSupported" in navigator.xr) {
  navigator.xr.isSessionSupported('immersive-vr').then((supported) => {
    console.log("immersive-vr supported:", supported);
  });
}
</syntaxhighlight>


==Modules==
==Modules==
These days, we can use modules for everything. 
Note that in most instances, you should compile these using Webpack, browserify or similar. 
For Node.js, you will need to transpile using Babel. 
However some modern browsers now support importing modules directly using script tags. 
* [[Caniuse: Modules]]
===Getting Started===
[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules MDN Guide to Modules]
Example Module
<syntaxhighlight lang="javascript">
// Import three.js as a module. webpack will resolve this.
import * as THREE from 'three';
// Import MyClass as a module. webpack will resolve this.
import {MyClass} from "./MyClass.js";
// Pretend we're writing another class
export class MyOtherClass {
  constructor() {}
}
</syntaxhighlight>
;Notes
* The Google style guide suggests always using named exports rather than using <code>export default</code>.
==Web Workers==
By default, JavaScript is a single-threaded language. For things that take a long time, people usually use asynchronous JS through promises and callbacks. However, if you have a heavy calculation that needs to be performed, you can use spawn web workers which will run in background threads. NodeJS also has a similar concept called [https://nodejs.org/api/worker_threads.html Worker Threads].
===Getting Started===
===Getting Started===
[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules MDN Guide to Modules]<br>
 
;Your main JS file:
 
;Your worker JS file:
 
 
Resources
* [https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers MDN: Using web workers]
* [https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API Web Workers API]
 
 
==Documentation==
Use [https://jsdoc.app/ JSDoc] for documentation. 
Example:
<syntaxhighlight lang="js">
 
</syntaxhighlight>
 
==Testing==
Frameworks for testing JS:
* [https://jestjs.io/ https://jestjs.io/]
 
==Useful Packages==
Packages which run in the browser and may be useful for developing web applications.
Note that some of these may overlap with the NodeJS page.
 
===lerp, clamp===
 
<syntaxhighlight lang="bash">
npm i lerp clamp
</syntaxhighlight>
 
<syntaxhighlight lang="javascript">
<syntaxhighlight lang="javascript">
Math.lerp = require("lerp");
Math.clamp = require("clamp");
</syntaxhighlight>
===pako===
[https://github.com/nodeca/pako pako github]<br>
"zlib port to javascript, very fast!"
<syntaxhighlight lang="bash">
npm install pako
</syntaxhighlight>
===url-join===
Basically <code>path.join</code> for the browser.<br>
<syntaxhighlight lang="bash">
npm install url-join
</syntaxhighlight>


===mathjs===
[https://mathjs.org/ https://mathjs.org/]<br>
Some useful math things for JavaScript in the browser and in NodeJS
<syntaxhighlight lang="bash">
npm install mathjs
</syntaxhighlight>
</syntaxhighlight>
[[Category:Programming languages]]

Latest revision as of 14:21, 5 February 2024

JavaScript is a single-threaded programming language. In the browser, JS is event-driven, meaning that after initialization, the JS engine is idle until an event occurs such as a button being pressed in the DOM.

This page is a mostly about browser-based JavaScript or ECMAScript usage and interaction with the HTML DOM (window). For server and desktop application JavaScript usage, please see the NodeJS page.

Language Syntax

General Ecmascript syntax. Parts of this section applies to browser-side JS and server-side JS (Node.js, Deno), though details may vary between runtimes.

Regular Expressions (Regex)

MDN Guide

const myRegex = /(\d+),(\d+)/;
const myStr = "124,52";
const match = myStr.match(myRegex);
// Captures
console.log(match[1], match[2]);
console.table(match);

Classes

Traditionally, classes are done using functions in JavaScript.

Traditional JS Classes
function Rectangle(height, width) {
  this.height = height;
  this.width = width;
}

// To extend some Shape class
Rectangle.prototype = Object.create(Shape.prototype);
// To add to the prototype
Object.assign(Rectangle.prototype, {
  constructor: Rectangle,
  getSize: function() {
    return this.height * this.width;
  }
});

Mozilla Reference
ES2015 adds syntax for classes in JavaScript.

class Rectangle extends Shape {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }

  getSize() {
    return this.height * this.width;<br />
  }
}

Promises

Added in ES2015 (ES6)
See Promises
In general, a function returns a promise if it needs to perform asyncronous tasks.

function myFunc() {
  // Do syncronous things here
  return new Promise((resolve, reject) => {
    // Do asyncronous things here
    // Return data
    resolve(myData);
    // Or if we have an error
    reject(myError);
  });
}

myFunc()
  .then(data => {
     console.log(data);
  }).catch(err => {
     console.error(err);
  });

Async-await

Added in ES2017

See Async_await
Async functions return implicit promises. They allow you to use keyword await to get results from other promises or functions.

Enums

See Stack overflow

Ecmascript doesn't have a built-in way to define ENUMS. One way is to use Object.freeze:

const WORKERSTATUS = Object.freeze({
  ACTIVE: "ACTIVE",
  DISABLED: "DISABLED",
});
Notes

var vs let, const

  • var is the old way of declaring variables. It has function-scope rather than block-scope. Note that var can also be redefined which can hide errors.
  • let and const are post-ES6 ways which have block-scope.

These days you should never use var.
The Google JS Style Guide and TS Style Guide suggest using const by default.

Browser Usage

For basic dom manipulation, see https://htmldom.dev/.

Inputs

Showing an input image

const myInput = document.getElementById("myInput");
const myImage = document.getElementById("myImage");
myInput.addEventListener('change', function() {
    if (myInput.files.length !== 1) {
        return;
    }
    let image = myInput.files[0];
    const reader = new FileReader();
    reader.onload = function(e) {
        myImage.src = e.target.result;
    };
    reader.readAsDataURL(image);
});

Canvas

Picture

Video

Your HTML:

<video id="myVideoElt" controls>
</video>

Your JS:

const myVideo = document.getElementById("myVideoElt");

// Create a new source
const newSource = document.createElement("source");
const myVideoUrl = "https://interactive-examples.mdn.mozilla.net/media/examples/flower.webm";
newSource.setAttribute("src", myVideoUrl);
myVideo.appendChild(newSource);
myVideo.play();

Compilation

Webpack

Babel

Websockets

How to use Websockets

Getting Started

let ws = new WebSocket(this.SERVER_URL);
ws.onopen = function(event) {
   console.log("Websocket opened");
   ws.send("Hi");
};
ws.onmessage = function(event) {
   console.log("message received");
   console.log(event.data);
};
ws.onclose = function() {
   console.log("WS Closed");
};

Data Structures

JavaScript traditionally has arrays and objects (hashmap) data structures.
ES2015 (ES6) adds several collections including: Map, Set, WeakMap, WeakSet
ES6 Collections

Arrays

let arr = [1,2,3];

// Map
arr.map(x => x > 2); // [false, false, true]

// Reduce or Fold
// Note that if you do not provide an initial accumulator,
// then the first element will be your accumulator
// I.e. the first call to your function will be (arr[0], arr[1])
arr.reduce((acc, x) => acc + x, 0); // 6

Objects

Objects are maps in JavaScript. They are typically implemented as hashmaps by the JS engine.
Note that you can only use numbers and strings as keys.
Learn more about the implementation at https://v8.dev/blog/hash-code

let my_map = {};
// or my_map["my_key"]
my_map.my_key = "my_value";
"my_key" in my_map;

// Loop over keys
for (let key in a) {
  console.log("Key:", key);
}

Map

MDN Map
ES2019 (ES10) Map Specification
This is your typical hashmap.

let myMap = new Map();

let keyObj = {};

// Set value
myMap.set(keyObj, 'value associated with keyObj');

// Get size
myMap.size;

// Get value
myMap.get(keyObj);

// Check if key is in the map
myMap.has(keyObj);

// Delete
myMap.delete(keyObj);
Notes
  • You can mix and match types of keys
  • The hash for objects are randomly generated under the hood
  • Do not use [] to get or set from the map
  • Iterating over a Map will be in order of insertion
Saving as json

See es6 map json

const data = JSON.stringify([...myMap]);
myMap = new Map(JSON.parse(data));

Set

MDN Set

let my_set = new Set();

// Returns the set itself
my_set.add(key);

my_set.has(key);

my_set.clear();

// Returns true if key was in the set
my_set.delete(key);

WeakMap

Unlike map, weakmap holds weak references to its keys. This allows keys to be garbage collected when no reference to it remains. Note that once keys are garbage collected, they are also removed from the weakmap since you can no longer access them. You can not iterate through a weakmap.

WeakSet

WebXR

// Check for VR support
if ("xr" in navigator && "isSessionSupported" in navigator.xr) {
  navigator.xr.isSessionSupported('immersive-vr').then((supported) => {
    console.log("immersive-vr supported:", supported);
  });
}

Modules

These days, we can use modules for everything.
Note that in most instances, you should compile these using Webpack, browserify or similar.
For Node.js, you will need to transpile using Babel.

However some modern browsers now support importing modules directly using script tags.

Getting Started

MDN Guide to Modules

Example Module

// Import three.js as a module. webpack will resolve this.
import * as THREE from 'three';
// Import MyClass as a module. webpack will resolve this.
import {MyClass} from "./MyClass.js";

// Pretend we're writing another class
export class MyOtherClass {
  constructor() {}
}
Notes
  • The Google style guide suggests always using named exports rather than using export default.

Web Workers

By default, JavaScript is a single-threaded language. For things that take a long time, people usually use asynchronous JS through promises and callbacks. However, if you have a heavy calculation that needs to be performed, you can use spawn web workers which will run in background threads. NodeJS also has a similar concept called Worker Threads.

Getting Started

Your main JS file
Your worker JS file


Resources


Documentation

Use JSDoc for documentation.
Example:

Testing

Frameworks for testing JS:

Useful Packages

Packages which run in the browser and may be useful for developing web applications. Note that some of these may overlap with the NodeJS page.

lerp, clamp

npm i lerp clamp
Math.lerp = require("lerp");
Math.clamp = require("clamp");

pako

pako github
"zlib port to javascript, very fast!"

npm install pako

url-join

Basically path.join for the browser.

npm install url-join

mathjs

https://mathjs.org/
Some useful math things for JavaScript in the browser and in NodeJS

npm install mathjs