JavaScript: General Concepts
- JavaScript is an interpreted, often transpiled, Object Oriented programming language.
- Prototype-based Object Oriented Design with optional Class syntactic sugar.
- Dynamically Typed - JavaScript uses Type Coercion to Dynamically Type values and variables at Run Time.
- Technically, "JavaScript" is a loose name for a family of dialects that implement the ECMAScript standard: https://www.ecma-international.org/technical-committees/tc39/ - officially, there is no "JavaScript" but the name persists.
- JavaScript Primitive Data Types (
Number,String,BigDecimal,Boolean, etc.) Pass by Value. Arrays and Objects Pass by Reference (one cause for the phenomenon of Shallow Copying).
Use Strict
Strict Mode enforces stricter/more secure execution of a Script at Run Time:
- Hoisting and assignment to variables that weren't declared result in syntax errors.
thisis Autoboxed more stringently.eval()is handled more securely.- Silent errors that are typically ignored are thrown verbosely.
'use strict'
//...
Refer to: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
JavaScript: Comparisons
Strict
Checks for exactly the same Value, Type, and Reference (Address in Memory):
a === b
Loose
Checks for the same Value after automatic conversion to a common Type (shared Type Coercion):
a == b
Object.is()
Special comparison for NaN and 0 along with some other important but "edgy" cases.
Refer to: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators
Type Checking
There are three primary ways to Type Check:
Primitive checking:
typeof 'Hello World' === 'string';
Check whether an Object is an instance of a Class:
var x = new Dog();
x instanceof Dog;
Via constructor:
var x = 'Hello World';
x.constructor === String;
JavaScript: This Keyword
The this keyword allows one to manipulate the current Closure/scope.
Arrow Functions
Note that JavaScript Arrow Functions don't bind this to their Closure/scope (this will refer to the next-nearest available surrounding scope).
const A = function() {
this.example = "hello, world"
const B = () => {
// Refers to the scope of A
console.log(this.example)
}
B()
}() // "hello, world"
Scope Binding
Consider the following:
var meeple = "Meeple One"
var myObj = {
meeple: "Meeple Two",
prop: {
getMeeple: function() {
return this.meeple
},
meeple: "Meeple Three"
}
}
console.log(myObj.prop.getMeeple())
var test = myObj.prop.getMeeple
console.log(test())
The output will be:
"Meeple Three"
"Meeple One"
Remember that
thisgets bound to the Scope of the function called - in the case oftest()the Scope is the outer Closure since it's defined in the top-level Scope (of the script file itself).
Bind, Call, Apply
Since
thisis relative to the scope in which it's used, it's sometimes necessary to use a JavaScript Function with a precisely specifiedthisvalue.
bind()- bindthisto a Function, its scope, and its callscall()- call a Function with a specifiedthisvalue and Argumentsapply()- call a Function with a specifiedthisvalue and Arguments passed as an Array
// Bind, React example
this.setSubmitState = this.setSubmitState.bind(this)
// Call, Prototype example
function Example(msg) {
this.msg = msg
}
function SubExample(msg, subexamplefield) {
Example.call(this, msg)
this.subexamplefield = subexamplefield
}
console.log(new SubExample('my msg', 'my subexample field'))
// Apply, native Math function example w/ null this
const max = Math.max.apply(null, [5, 6, 2, 3, 7])
Resources and Links
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
Code samples:
JavaScript: Falsy Values
Are interpreted as false:
false
0 (zero)
"" (empty string)
null
undefined
NaN
All values are truthy unless they are falsy.
JavaScript: Quirks
Some that are covered elsewhere:
- Shallow vs. Deep Copying
- Truthy and Falsy values (in general)
- Type Coercion (Auto-Boxing)
Empty Array Comparisons
Empty Array comparisons can result in some counter-intuitive consequences due to Type Coercion and Falsy Value resolution:
console.log([] === ![]) // false
console.log([] !== []) // true
console.log([] == ![]) // true
Default Numeric Sorting
One of JavaScript's oft-lamented quirks is that Number types are cast to Strings when sorted using default sorting:
const A = [1,2,3,4,5,6,7,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
A.sort()
console.log(A)
// [1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 3, 4, 5, 6, 7, 7, 8, 9]
However using a custom comparator that forces Numeric comparison:
//...
A.sort((a,b) => {
const A = parseInt(a), B = parseInt(b)
if (A < B) return -1
if (A > B) return 1
return 0
})
//...
// [1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
Numeric Object Keys
Object key values don't have the above quirk. They are treated as Numbers:
const M = {}
M[1] = true
M[21] = true
M[2] = true
M[12] = true
M[3] = true
M[11] = true
const OK_M = Object.keys(M)
for (let i = 0; i < OK_M.length; i++) {
console.log(`${OK_M[i]} ${M[OK_M[i]]}`)
}
/*
"1 true"
"2 true"
"3 true"
"11 true"
"12 true"
"21 true"
*/
JavaScript: Variables
- let - specifies a mutable variable declaration, scoped to the immediate local Closure scope.
- const - immutable once declared, specifies a Constant.
- var - original, general, not immutable, not scoped, Hoisted.
Advantages of Let vs. Var
Since let is scoped to a local context, doing the following:
for (let i = 0; i < els.length; i++) {
els[i].addEventListener('click', function() {
console.log(i)
})
}
will correctly print the right number i. If var is used instead each button will only print els.length - 1.
JavaScript: Prototypes
JavaScript's Object Oriented
Examples
var Human = function(name) {
this.name = name;
}
Human.prototype.getName = function() {
return this.name;
}
Note that Prototype names must be written using Pascal Notation.
Object Oriented Design
Define a Prototype:
var Dog = function(bark) {
this.bark = bark;
};
//Call constructor
var p = new Dog("woof");
// Add a method to Dog.prototype
Dog.prototype.tail = function(){
console.log("wags");
};
Inheritance:
function Poodle(bark, name) {
// Call the parent constructor
Dog.call(this, bark);
this.name = name;
};
Or explicitly set prototypical inheritance using Object.create():
//Explicitly set prototypical inheritance
//Allows overriding parent methods
Poodle.prototype = Object.create(Dog.prototype);
Test it out:
var d = new Poodle("bark bark", "dooder");
d.tail(); //"wags"
console.log(d instanceof Dog); // true
console.log(d instanceof Poodle); // true
JavaScript: Classes
class A {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_classes#private_fields
#privatefieldA = 2 // # affix indicates field
#privatefieldB = -1 // # affix indicates field
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_classes#static_properties
static staticfield = "I'm only accessible through Class A not an Instance of"
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_classes#accessor_fields
get privatefieldA() {
return this.#privatefieldA // Getter can expose privatefield like usual
}
}
class B extends A {
publicfieldB = "I'm publicfieldB"
constructor(constructorfield = "I'm initialized!"){
super() // Must be present before this
this.publicfieldA = "I'm publicfieldA"
this.constructorfield = constructorfield
}
}
class C extends B {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_classes#constructor
constructor(){
super("I'm initialized from the subclass!") // Must precede this
}
static staticmethod() {
return "I'm a static method!"
}
}
const a = new A()
console.log(`Class A: ${A}`)
console.log(`Object a: ${JSON.stringify(a)}`)
console.log(A.staticfield)
//console.log(a.#privatefieldA) // error
console.log(a.privatefieldA) // is accessible through getter
//console.log(a.#privatefieldB) // error
console.log(a.privatefieldB === undefined) // no getter
const b = new B()
console.log(`Class B: ${B}`)
console.log(`Object b: ${JSON.stringify(b)}`)
console.log(b.publicfieldA)
console.log(b.publicfieldB)
console.log(b.constructorfield)
//console.log(b.#privatefieldA) // error
console.log(b.privatefieldB === undefined) // no getter
const c = new C()
console.log(`Class C: ${C}`)
console.log(`Object c: ${JSON.stringify(c)}`)
console.log(c.constructorfield)
console.log(C.staticmethod())
//console.log(c.#privatefieldA) // error
Resources and Links
Code samples:
JavaScript: Promises
- Promises - an Object for handling Asynchronous, non-blocking, operations.
- Async/Await - syntactic sugar for returning and handling a Promise
Examples
- Must wrap all
awaitkeywords within anasyncmethod.
const asyncFunc = async (x) => {
return 2
}
const EX = async () => {
const V = await asyncFunc(1)
console.log(V) //2
}
EX()
const assignAsyncVal = () => new Promise((resolve, reject) => {
asyncFunc(1).then(success => {
return resolve(success)
})
})
const V = assignAsyncVal().then(success => {
console.log(success) //2
})
Using await with the Promise function assignAsyncVal() above:
const EX = async () => {
const VV = await assignAsyncVal()
console.log(VV) //2
}
EX()
Many Promises
To resolve multiple Promises, push them into an Array and use Promise.all(), Promise.allSettled(), etc.
const A = new Promise((resolve, reject) => {})
const B = new Promise((resolve, reject) => {})
const PROMISES = []
PROMISES.push(A)
PROMISES.push(B)
// Resolve all promises
Promise.all(PROMISES).then(success => {
//...
})
Async Error Handling
Use .then() and .catch() for all Error and Rejection handling:
T()
.then(success => { console.log(success) })
.catch(ex => console.error(ex.message))
Note: don't pass
, fail => { //... }inthen()when using this syntax.
Use try-catch for blocking operations:
try {
await T()
//...
} catch (ex) {
//...
}
Note:
setTimeout()and other Asynchronous operations won't get handled by thecatchblock by itself.
Use process.on('uncaughtException', //...) to catch all (Synchronous or Asynchronous) unhandled Process-level Exceptions:
try {
process.on('uncaughtException', exception => { console.error(exception}) })
} catch (ex) {
//...
}
Consult: https://github.com/Thoughtscript/async_error_node for examples.
Resources and Links
Code samples:
JavaScript: Asynchronous Loops
Given the following helper method:
const print = str => {
const el = document.getElementById("here");
const txt = document.createTextNode(str);
el.append(txt);
const br = document.createElement("br");
el.append(br);
console.log(str);
};
Synchronous Loops
A few examples.
const firstExample = () => {
let i;
for (i = 0; i < 10; i++) {
print("1st middle: " + i);
}
print("1st end: " + i);
};
firstExample();
const secondExample = () => {
let i;
for (i = 0; i < 10; i++) {
print("2nd middle: " + i);
}
print("2nd end: " + i);
return i;
};
let j = 0;
j = secondExample();
print("J 2nd: " + j);
const fourthExample = () => {
let y = 0;
for (let i = 0; i < 10; i++) {
y++;
print("4th middle: " + y);
}
print("4th end: " + y);
return y;
};
let v = 0;
v = fourthExample();
print("V 4th: " + v);
1st middle: 0
1st middle: 1
1st middle: 2
1st middle: 3
1st middle: 4
1st middle: 5
1st middle: 6
1st middle: 7
1st middle: 8
1st middle: 9
1st end: 10
2nd middle: 0
2nd middle: 1
2nd middle: 2
2nd middle: 3
2nd middle: 4
2nd middle: 5
2nd middle: 6
2nd middle: 7
2nd middle: 8
2nd middle: 9
2nd end: 10
J 2nd: 10
4th middle: 1
4th middle: 2
4th middle: 3
4th middle: 4
4th middle: 5
4th middle: 6
4th middle: 7
4th middle: 8
4th middle: 9
4th middle: 10
4th end: 10
V 4th: 10
Asynchronous Loops
Given:
const thirdExample = () => {
return setTimeout(() => {
let y = 0;
for (let i = 0; i < 10; i++) {
y++;
}
print("3rd end: " + y);
return y;
}, 1000);
};
let z = 0;
z = thirdExample();
print("Z 3rd: " + z);
Intuitively, we think that the output should be something like:
// Incorrect assumption
3rd end: 10
Z 3rd: 10
Given:
const fifthExample = () => {
let y = 0;
setTimeout(() => {
y = 10000;
}, 4000);
return y;
};
print("5th: " + fifthExample());
It's natural to think the output will be:
// Incorrect assumption
5th: 10000
The actual chronological output:
Z 3rd: 1
5th: 0
3rd end: 10
To remedy those counter-intuitive outputs wrap the methods with a Promise.
Event Stack
for (let i = 0; i < 3; i++) {
console.log(1)
setTimeout(() => console.log(i), 0)
setTimeout(() => console.log(2), 0)
setTimeout(() => console.log(3), 100)
console.log(4)
}
In chronological order:
1
4
1
4
1
4
0
2
1
2
2
2
3
3
3
Perhaps counter-intuitively:
- Both
console.log(1)andconsole.log(4)print before the rest (even thesetTimeout()calls with0delay). This has to do with the way that JavaScript handles Events on the Stack.setTimeout()calls are lower priority Events and are executed last, even with a0delay. - One might think that all
console.log(3)calls would print prior to eachconsole.log(4). They all occur after the fact. JavaScript essentially ignores any asynchronous blocking behavior within a loop.
Button Event Listeners
- Use
letinstead ofvar. - Always lookup "fresh" info within the actual event callback rather than supplying some info at initialization since it will likely both be (1) out-of-date and (2) asynchronously invalid.
Refer to the article on JavaScript Variables.
JavaScript: Deep and Shallow Copies, Merging
Deep Copying creates a copy of an object that share no references in memory.
Shallow Copying creates a copy of an object that shares a reference in memory.
Deep Merging allows the deeply nested fields of two objects to be added to, combined, or merged with one of those objects.
From the Reference Documentation
“A deep copy of an object is a copy whose properties do not share the same references (point to the same underlying values) as those of the source object from which the copy was made. As a result, when you change either the source or the copy, you can be assured you're not causing the other object to change too; that is, you won't unintentionally be causing changes to the source or copy that you don't expect. That behavior contrasts with the behavior of a shallow copy, in which changes to either the source or the copy may also cause the other object to change too (because the two objects share the same references).”
Shallow Copying
Consider the following tensor of rank 1, a single dimension array:
const X = [1,2,3,4,5,6,7,8]
const Y = [...X]
X[0] = 1000
console.log(X) // [ 1000, 2, 3, 4, 5, 6, 7, 8 ]
console.log(Y) // [ 1, 2, 3, 4, 5, 6, 7, 8 ]
When the array being duplicated is of rank > 1:
const A = [[1,2,4,5,6,7,8],[1,2,4,5,6,7,8]]
const B = [...A]
A[0][0] = 1000
console.log(A) // [ [ 1000, 2, 4, 5, 6, 7, 8 ], [ 1, 2, 4, 5, 6, 7, 8 ] ]
console.log(B) // [ [ 1000, 2, 4, 5, 6, 7, 8 ], [ 1, 2, 4, 5, 6, 7, 8 ] ]
Deep Copying
The recommended way now is to use:
JSON.parse(JSON.stringify(ingredients_list)) // Object
const A = [[1,2,4,5,6,7,8],[1,2,4,5,6,7,8]] // Tensor of rank 2, matrix, m x n array where n > 1
// By element
const R = []
for (let r = 0; r < A.length; r++) {
const row = []
for (let c = 0; c < A[r].length; c++) {
row.push(A[r][c])
}
R.push(row)
}
R[0][0] = 1000
console.log(R) // [ [ 1000, 2, 4, 5, 6, 7, 8 ], [ 1, 2, 4, 5, 6, 7, 8 ] ]
console.log(A) // [ [ 1, 2, 4, 5, 6, 7, 8 ], [ 1, 2, 4, 5, 6, 7, 8 ] ]
const A = [[1,2,4,5,6,7,8],[1,2,4,5,6,7,8]] // Tensor of rank 2, matrix, m x n array where n > 1
// By row spread operator
const R = []
for (let r = 0; r < A.length; r++) {
// Single dimensioned arrays are deep copied by the spread operator.
R.push([...A[r]])
}
R[0][0] = 1000
console.log(R) // [ [ 1000, 2, 4, 5, 6, 7, 8 ], [ 1, 2, 4, 5, 6, 7, 8 ] ]
console.log(A) // [ [ 1, 2, 4, 5, 6, 7, 8 ], [ 1, 2, 4, 5, 6, 7, 8 ] ]
Deep Merging
Object.assign({}, obj)
Resources and Links
JavaScript: Node
Example main method wrapped with try-catch clauses and Proccess exception handlers:
'use strict'
try {
process.on('warning', warning => { console.error(`Warning encountered: ${warning}`) })
process.on('unhandledRejection', rej => { console.error(`Unhandled Rejection override: ${rej}`) })
process.on('uncaughtException', exception => { console.error(`Error encountered: ${exception}`) })
process.on('exit', msg => { console.log(`Service shutting down: ${msg}`) })
//...
} catch (ex) {
console.error(`Exception ${ex}!`)
process.exit()
}
Workers
const {Worker } = require('worker_threads')
const createWorker = (filename, paramsToExecute) => new Promise((resolve, reject) => {
//Constructor
//Will pass paramsToExecute to the method executed in filename
//Must have workerData as attribute
const W = new Worker(filename, {workerData: paramsToExecute })
//Listeners in parent thread
W.on('message', message => {
console.log(`Worker message received: ${message}!`)
resolve(message)
})
W.on('error', error => {
console.error(`Worker error encountered: ${error}!`)
reject(error);
})
W.on('exit', exitCode => {
if (exitCode !== 0) {
console.error(`Worker stopped with exit code ${exitCode}`)
reject(exitCode)
} else {
console.log(`Worker stopped with exit code ${exitCode}`)
resolve(exitCode)
}
})
//Send message to worker script
W.postMessage('I am initialized...')
})
//Wrap this with a Thread Pool and/or Thread count to prevent excessive resourcing
const executeServiceUsingThread = (filename, paramsToExecute) => new Promise((resolve, reject) => {
createWorker(filename, paramsToExecute).then(success => {
console.log(`Service completed: ${success}!`)
}, failure => {
console.error(`Service completed: ${failure}!`)
})
})
module.exports = {
executeServiceUsingThread: executeServiceUsingThread
}
"use strict"
/**
* Note: the WebWorker importScripts() cannot be used here.
*
* These are essentially NodeJS compliant scripts
*/
const { workerData, parentPort } = require('worker_threads')
//console.log test
console.log('Test console log inside Worker One')
console.log(`Getting Worker One workerData: ${workerData}`)
const conversationMappings = {
"Hello!": "Goodbye!"
}
const exampleDependencyFunction = text => conversationMappings[text]
parentPort.postMessage(`Worker one: ${workerData} - message response: ${exampleDependencyFunction(workerData)}!`)
//...
WT.executeServiceUsingThread('./nodeThread/workerOne/worker_script_one.js', "Hello!")
HTTP
module.exports = {
createHttpServer: () => {
const S = require('http').createServer(require('./express').createServer())
console.log('HTTP initialized!')
console.log('REST API controller initialized!')
S.on('clientError', (err, sck) => {
const e = `HTTP/1.1 400 Bad Request! ${err}`
console.error(e)
sck.end(e)
})
S.listen(require('../config').SERVER.PORT, () => { console.log(`HTTP server started on port: ${S.address().port}`) })
return S
}
}
HTTPS
With Express:
const C = require('../config'), FS = require('node:fs')
module.exports = {
createHttpsServer: () => {
const OPTS = {
key: FS.readFileSync(C.SERVER.SSL.KEY_PATH),
cert: FS.readFileSync(C.SERVER.SSL.CERT_PATH)
}
const S = require('node:https').createServer(OPTS, require('./express').createServer())
console.log('HTTPS initialized!')
S.listen(C.SERVER.HTTPS_PORT, () => { console.log(`HTTPS server started on port: ${S.address().port}`) })
return S
}
}
const express = require('express'),
C = require('../config')
module.exports = {
createServer: () => {
const app = express()
app
.use(require('morgan')('dev'))
.use(express.json())
.use(express.urlencoded({ extended: true }))
.use(require('cookie-parser')())
.use(require('cors')({
origin: C.SERVER.CORS,
optionsSuccessStatus: 200
}))
.use('/api', require('./api'))
return app
}
}
const express = require('express'),
privateApi = express.Router(),
C = require('../config')
privateApi
.all('*', async (req, res, next) => await require('./auth').LOG_IP_FILTER(req, res, next))
.all('*', async (req, res, next) => await require('./auth').DECRYPT(req, res, next))
.all('*', async (req, res, next) => await require('./auth').AUTH_CHECK(req, res, next))
// https://localhost:8888/api/projections?auth=...
.get("/projections", async (req, res) => {
let responseData = await require('./domain/budgetprojections').BUDGET_PROJECTIONS({})
let stringData = require('./auth').SCRAMBLE(C.AUTH.V(), C.AUTH.BP(), JSON.stringify(responseData))
return res.send({ status: 200, data: stringData })
})
//...
Exports and Imports
module.exports = {
myExportedFunction: () => {
//...
}
}
const R = require('../path/to/my/file.js')
R.myExportedFunction()
Resources and Links
Code samples:
JavaScript: Imports
Named Imports
export const
export const
BASE_PATH = '/',
HOME_PATH = '/home',
API_PATH = '/api',
RAILS_API_URL = 'http://localhost:3000/examples/all'
import { BASE_PATH, HOME_PATH, API_PATH } from '../../../Constants'
Default Imports
export default
//...
export default () =>
<header>
<h2>React 2024</h2>
<Menu/>
</header>
import CustomHeader from '../../Presentation/CustomHeader'
Namespace Imports
Resolve namespace conflicts, assign a custom monniker, or get all exports from a namespace, etc.
//...
import * as my_alias from 'my_library'
Side Effects and Files
Import to load, execute, or use a script, file, or asset.
import 'my_script'
import './MyCoin.css'
CommonJS and ES Modules
Node
module.exports = {
MY_EXPORT: //..
}
const A = require('my_module').MY_EXPORT
Consult:
Resources and Links
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
- https://nodejs.org/api/esm.html#require
- https://nodejs.org/api/modules.html
Code samples:
JavaScript: Typescript
Interfaces
// Interfaces are struct-like with typed constraints
export interface Example {
fieldA: string
fieldB: number
fieldC: boolean
}
// variable types are specified via:
const E: Example = {
fieldA: "example",
fieldB: 1,
fieldC: true
}
// interfaces can be combined with class and constructor syntax
export class Example implements Example {
fieldA = ""
fieldB = 0
fieldC = false
constructor (fieldA: string, fieldB: number, fieldC: boolean) {
this.fieldA = fieldA
this.fieldB = fieldB
this.fieldC = fieldC
}
}
const EE = new Example('a', 3, true)
https://github.com/Thoughtscript/languages_2024/tree/main/typescript/src/interfaces
Types
type A = {
id: number
}
type B = {
name: string
}
// Types can be combined (intersect, union, simple polymorphism, etc.)
type C = A & B
interface D {
fieldA: string,
fieldB: string,
fieldC: number,
}
// Can pick specific fields (think subtype)
type E = Pick<D, "fieldA">;
const A: A = {
id: 1
}
const C: C = {
id: 1,
name: "pronomen"
}
const E: E = {
fieldA: 'message'
}
https://github.com/Thoughtscript/languages_2024/tree/main/typescript/src/types
Error Suppression and Inline Strictness
To be used sparingly (most Production codebases I've seen have this somewher - for example here):
// @ts-ignore
The above will suppress the specified TypeScript Error originating from the following line.
Inline Strictness and Error Suppression
Resources and Links
- https://www.typescriptlang.org/docs/
- https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-6.html#suppress-errors-in-ts-files-using--ts-ignore-comments
- https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html#ts-check
Code samples:
JavaScript: Web Workers
WorkerManager
<html>
<head>
<meta charset='UTF-8'>
<title>WebWorker Example</title>
</head>
<body>
<h3>WebWorker Request:</h3>
<h id="request">Hello!</h>
<h3>WebWorker Responses:</h3>
<p id="response"></p>
</body>
<script type="text/javascript" src="workerManager.js"></script>
<script>
var wm = new WorkerManager();
wm.startWrapperExample(document.getElementById("request").textContent);
</script>
</html>
Using a WorkerManager wrapper with two example implementations: BlobWorker and ScriptWorker.
//workerManager.js
"use strict";
/** BEGIN WorkerManager Prototype */
var WorkerManager = function () {
};
WorkerManager.prototype.Worker = null;
WorkerManager.prototype.opts = {
inline: false,
scriptWorker: {
script: "scriptWorker.js"
},
blobWorker: "self.addEventListener('message', function(e) {" +
"self.postMessage('Goodbye!');" +
"}, false);"
};
WorkerManager.prototype.createBlobWorker = function () {
var blob = window.URL.createObjectURL(new Blob([WorkerManager.prototype.opts.blobWorker]));
this.Worker = new Worker(blob);
};
WorkerManager.prototype.createScriptWorker = function () {
this.Worker = new Worker(this.opts.scriptWorker.script);
};
WorkerManager.prototype.isSupported = function () {
return window.Worker;
};
WorkerManager.prototype.startWrapperExample = function (t) {
if (this.isSupported()) {
if (this.opts.inline) this.createBlobWorker();
else this.createScriptWorker();
if (this.Worker != null) {
this.Worker.postMessage(t);
this.Worker.addEventListener('message', function (e) {
document.getElementById('response').textContent = e.data;
}, false);
}
}
};
/** END WorkerManager Prototype */
ScriptWorker
//scriptWorker.js
"use strict";
importScripts('./scriptWorkerDependency.js');
self.addEventListener('message', function (e) {
self.postMessage(exampleDependencyFunction(e.data));
}, false);
ScriptWorker Dependency
// scriptWorkerDependency.js
"use strict";
var conversationMappings = {
"Hello!": "Goodbye!"
};
var exampleDependencyFunction = function(text) {
return conversationMappings[text];
};
JavaScript: Techniques
Initialize Many EventListeners
To initialize an EventListener on a DOM Element:
const el = document.getElementById('todos')
for (let i = 0; i < el.children.length; i++) {
const c = el.children[i]
c.addEventListener('click', (e) => {
console.log(e.target.id)
e.target.style.display = 'none'
e.preventDefault
})
}
Remember that by the time the Element is clicked:
cwill lose its value - it's disassociated with the Element by that point.e.targetis still the preferred, official, way to access the Attributes on the Element, the Element itself, etc.thiswill refer to the top-level scope and shouldn't be used.
In short, use
eande.targetto access the propagated DOM Event and current Element, respectively. (Similar to React'sref.current.)
Fastest Way To Remove An Index
Supposedly the fastest way to remove an index in an array is as follows:
const todos = []
for (let i = 0; i < todos.length; i++) {
if (todos[i].uuid === uuid) {
// Remove by index
todos.splice(i, 1)
break
}
}
Vs. deep removal:
const test = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, "a", 'b', 'c', 'd', 'e', "f", 'g']
const deepRmv = key => {
const temp = [...test]
let result = []
const BEGIN = new Date()
for (let i = 0; i < temp.length; i++) {
if (temp[i] === key) continue
result.push(temp[i])
}
const END = new Date()
console.log(`deepRmv time: ${END-BEGIN} result: ${result}`)
return result
}
const rmvBySlice = key => {
const temp = [...test]
const BEGIN = new Date()
for (let i = 0; i < temp.length; i++) {
if (temp[i] === key) {
temp.splice(i, 1)
break
}
}
const END = new Date()
console.log(`rmvBySlice time: ${END-BEGIN} result: ${temp}`)
return temp
}
deepRmv("d")
rmvBySlice("d")
"deepRmv time: 0 result: 1,2,3,4,5,6,7,8,9,0,a,b,c,e,f,g"
"rmvBySlice time: 0 result: 1,2,3,4,5,6,7,8,9,0,a,b,c,e,f,g"
Probably best to use for loop with splice since a tad more succinct writing-wise.
https://javascript.plainenglish.io/how-to-remove-a-specific-item-from-an-array-in-javascript-a49b108404c shows that
forloop withspliceis5xfaster thanindexOfwithsplice.
Common Operations
const ARR = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const first = ARR.shift() // shift - remove and return element from index 0
console.log(first) // 1
console.log(ARR) // [2, 3, 4, 5, 6, 7, 8, 9, 10]
ARR.unshift(first) // unshift - add element to index 0
console.log(ARR) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
ARR.splice(4, 5) // splice(i, j) - remove j element(s) from and including i
console.log(ARR) // [1, 2, 3, 4, 10]
ARR.splice(3, 0, 11) // splice(i, j) - remove j element(s) from and including i
console.log(ARR) // [1, 2, 3, 11, 4, 10]
const sliced = ARR.slice(0, 3) // slice(i, j) - return elements between i and j (inclusive-exclusive) without altering ARR
console.log(sliced) // [1, 2, 3]
console.log(ARR) // [1, 2, 3, 11, 4, 10]
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
Type and Instance Checking
const A = 999, B = "I'm a String.", C = [1, 2, 3]
console.log(Number.isNaN(A)) // false
console.log(Number.isNaN(B)) // false
console.log(Number.isNaN(C)) // false
console.log(typeof A) // "number"
console.log(typeof B) // "string"
console.log(typeof C) // "object"
console.log(A instanceof Number) // false
console.log(B instanceof Object) // false
console.log(B instanceof String) // false
console.log(C instanceof Array) // true
console.log(C instanceof Object) // true
Benchmarking
A cool benchmarking site: https://jsbench.me/nyla6xchf4/1
BigInt
Number to BigInt conversion:
const A = 9007199254740991n
const B = BigInt(9007199254740991)
const C = 1n
const D = C / B
Resources and Links
- https://javascript.plainenglish.io/how-to-remove-a-specific-item-from-an-array-in-javascript-a49b108404c
- https://jsbench.me/
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice