Node.js 16
and Modern JavaScript
JaxNode May 2021
About Me
fek.io/blog
youtube.com/c/polyglotengineer
github.com/davidfekke
@jaxnode @polyglotengine1
Node 16
- Node 14 LTS
- Can use Node 16 for R&D
- Use Node LTS for Production
Node 16 Features
- Apple ARM64 support
- New V8
- New Regex functions
- Stable Timer Promises API
- Web Crypto API
- New Node-API C methods
- AbortController API
- atob and btoa support
- Source Maps v3
Apple Silicon M1
- Build group has installers for Node 16
- .pkg installer installs Universal Binary
- Previous versions could use Rosetta 2
- NVM installs arch specific
V8 9.0
- New Regex Indices /d
- Faster Js-to-Wasm calls
- Use experimental --turbo-inline-js-wasm-calls
Stable Timer Promises
- Exists under 'timer/promises'
- Use this for using timers within Async/Await
import { setTimeout } from 'timers/promises';
async function doSomething() {
console.log('doSomething started!');
await setTimeout(2000);
console.log('Delayed Timers!');
}
doSomething();
Web Crypto API
- Newer version of the Crypto API
- All new features under `subtle` interface
- Many versions of `Crypto`
- Standardizing and a single Standard
Web Crypto Example
import { webcrypto } from 'crypto';
const { subtle } = webcrypto;
(async function() {
const key = await subtle.generateKey({
name: 'HMAC',
hash: 'SHA-256',
length: 256
}, true, ['sign', 'verify']);
const digest = await subtle.sign({
name: 'HMAC'
}, key, 'I love node.js');
console.log(digest);
})();
N-API now Node-API
- The way you extend Node with C/C++
- napi_add_async_cleanup_hook
- napi_object_freeze
- napi_object_seal
- napi_type_tag_object
- napi_check_object_type_tag
- napi_type_tag
AbortController
- Actually part of Node 15
- Use { once: true } option in Event Listeners
const abortC = new AbortController();
abortC.signal.addEventListener('abort', () => {
console.log('Just cancelled')
}, { once: true });
abortC.abort();
console.log(abortC.signal.aborted);
atob and btoa Added
- atob and btoa added to Node for JS compatibility
- Used for converting Base64
- DO NOT USE THESE NEW FUNCTIONS
- There is a better way
const str = 'Hello JavaScript Developer!';
const strBuf = Buffer.from(str, 'utf8');
console.log(strBuf);
// <Buffer 1d e9 65 a0 96 af 69 27 2b 8a 9b 43 7a f7 a5 a2 97 ab>
const base64Buf = Buffer.from(strBuf, 'base64');
const base64Str = base64Buf.toString('base64');
console.log(base64Str);
// SGVsbG8gSmF2YVNjcmlwdCBEZXZlbG9wZXIh
const bufFromBase64Str = Buffer.from(base64Str, 'base64');
const decodedStr = bufFromBase64Str.toString('utf-8');
console.log(decodedStr);
// Hello JavaScript Developer!
console.log(str === decodedStr);
// true
Other Features
- npm v7.10.0
- Source Maps v3
- process.binding() has been deprecated
Modern JavaScript
JavaScript Has Changed
- Original version released in 1995
- Changed name to ECMAScript
- V3 added Regex
- V4 was scraped and became ActionScript
- V5 Added JSON libs, Array functions, 'strict mode' and getters and setters in 2009
- V6 became ES2015, ES2016
- ES2017
- ES2018
- ES2020 ...
Features since 2015
- let, const keywords
- for-of loops
- arrow functions
- Promises and async/await
- import/export syntax
- ... Spread Operator
- De-structuring
- Default Parameters
- new.target
- nullish coalescing and optional chaining
num = 5;
console.log(num);
var num; // 5
num = 5;
console.log(num);
let num; // error
var catsname = 'Bebe';
function hoistName() {
othercatsname = 'Zedadiah';
console.log(othercatsname); // Zedadiah
var othercatsname;
}
hoistName(); // Zedadiah
console.log(othercatsname);
// Uncaught ReferenceError: othercatsname is not defined
let and const
- 'var' will hoist to top of scope
- 'let' and 'const' will not let you hoist
- 'const' supposed to be write once, readonly
Object Construction
- function prototypes
- Use the 'class' keyword
- class is controversial
- factory functions
- Object Literals
- Object.create, .assign, .freeze
function Thing(name) {
this.thingName = name;
return this;
}
Thing.prototype.getName = function() {
return this.name;
}
const myThing = new Thing('My Thing');
const currentName = myThing.getName();
// returns 'My Thing';
Prototype
class Thing {
construtor(name) {
this.thingName = name;
}
getName() {
return this.thingName;
}
}
const myThing = new Thing('My Thing');
// returns 'My Thing';
Class
function createThing(name) {
function getName() {
return name;
}
return Object.freeze({
name,
getName
});
}
const myThing = createThing('My Thing');
// returns 'My Thing';
Crockford Style
// Sample from Wikipedia https://en.wikipedia.org/wiki/ECMAScript
let object = {a: 1, b: 2}
let objectClone = Object.assign({}, object) // before ES9
let objectClone = {...object} // ES9 syntax
let otherObject = {c: 3, ...object}
console.log(otherObject) // -> {c: 3, a: 1, b: 2}
// https://fek.io/blog/8-1-2-ways-to-loop-through-an-array-in-java-script/
const arr = ['First', 2, 3.3, 'Foo', 'Bar'];
//for-in
for (let index in arr) {
console.log(arr[index]);
}
//for-of
for (let item of arr) {
console.log(item);
}
For-of
Arrow functions
const prn2 = txt => console.log(txt);
const prn3 = (txt) => {
console.log(txt);
};
prn1('Hello JaxNode1!'); // Hello JaxNode1!
prn2('Hello JaxNode2!'); // Hello JaxNode2!
prn3('Hello JaxNode3!'); // Hello JaxNode3!
function prn1(txt) {
console.log(txt);
}
Promises async/await
- Promises and async and await are interchangeable
- Error-first functions can easily be converted into promises and used with 'async' and 'await'
var fs = require('fs'),
rootPath = 'C:\\Users\\dfekke\\Documents\\',
ext = 'tiff',
newExt = 'TIF';
fs.readdir(rootPath,
function(err, files) {
if (err) {
console.error("unable to read directory");
} else {
var re = new RegExp("^.*\\." + ext + "$");
for (var f of files) {
if (re.test(f)) {
console.log(f);
var oldPath = rootPath + f;
var newPath = rootPath
+ f.substring(0, f.length - ext.length)
+ newExt;
console.log(newPath);
fs.rename(oldPath,
newPath,
function (err) {
console.error("unable to rename file");
});
}
}
}
});
Error first callbacks
Convert to Promise
- Some older APIs do not have Promise support
- Util.Promisify will convert error-first callbacks
- let fsP = util.promisfy(fs.readdir);
- let folders = await fsP(path);
- import { readdir } from 'fs/promises';
- let files = await readdir(path);
import * as pkg from 'node-fetch';
const fetch = pkg.default;
const getsomedata = async () => {
const resp = await fetch('https://reqres.in/api/users?page=2');
const jsondata = await resp.json();
const names = jsondata.data.map(item => `${item.first_name} ${item.last_name}`);
return names;
};
const results = await getsomedata();
console.log(results);
Fetch
import axios from 'axios';
const getsomedata = async () => {
const resp = await axios.get('https://reqres.in/api/users?page=1');
const respdata = resp.data;
const names = respdata.data.map(item => `${item.first_name} ${item.last_name}`);
return names;
};
const results = await getsomedata();
console.log(results);
Axios
import/export syntax
- Replaces require()
- Can use .mjs and .cjs extensions
- set the 'type' in package.json file to either 'commonjs' or 'module'
- If you set it to 'module' you module will use 'import' and 'export'
- Also possible to use `require` and `import` in same file.
const fsPromise = require('fs/promises');
const readdir = fsPromise.readdir;
// Can do the same thing on one line with import syntax
import { readdir } from 'fs/promises';
// Can use both at the same time
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const talib = require('talib');
console.log("TALib Version: " + talib.version);
// dataservice.js
import axios from 'axios';
async function getVehicles() {
const results = await axios.get('https://swapi.dev/api/vehicles/');
return results.data.results;
}
async function getPlanets() {
const results = await axios.get('https://swapi.dev/api/planets/');
return results.data.results;
}
async function getStarships() {
const results = await axios.get('https://swapi.dev/api/starships/');
return results.data.results;
}
export { getVehicles, getPlanets, getStarships };
Export
const arr1 = [2, 5, 7, 9];
const arr2 = [1, 3, ...arr1, 8];
console.log(arr2); // [ 1, 3, 2, 5, 7, 9, 8 ]
const [first, second, third] = arr2;
console.log(second); // 3
const obj1 = { f: first, s: second, t: third };
const { f, s, t } = obj1;
console.log(t); // 2
...Spread
function doSomething(greet, time = 3000) {
setTimeout(() => {
console.log(`Hello ${greet}`);
}, time);
}
doSomething('Jaxnode!');
doSomething('Orlando!', 1000);
// 'Hello Orlando!' in 1 second
// 'Hello Jaxnode!' in 3 seconds
Default parameters
function Foo() {
if (!new.target)
throw 'Foo() must be called with new';
console.log('Foo instantiated with new');
}
new Foo(); // logs "Foo instantiated with new"
Foo(); // throws "Foo() must be called with new"
new.target
undefined ?? "string" // -> "string"
null ?? "string" // "string"
false ?? "string" // -> false
NaN ?? "string" // -> NaN
Nullish coalescing ??
const person = {
address: {
streetnumber: 9080,
streetname: 'Simpson St.',
city: 'Burlington',
state: 'VT'
}
};
const zipcode = person?.address?.zipcode;
// Sets to empty value instead of throwing error
Optionals ?.
TypeScript
- From Anders Hejlsberg, man behind Turbo Pascal, C#
- Super set of JavaScript
- Requires Transpiler like TSC or Deno
- Allows Data Types for JavaScript
- Designed for large projects
- Used with Angular
References
- https://fek.io/blog/node-16-now-available/
- https://fek.io/blog/crockford-objects-in-java-script/
- https://fek.io/blog/use-dependency-injection-with-express/
- https://fek.io/blog/how-to-upgrade-an-express-rest-app-to-fastify/
- https://fek.io/blog/why-you-should-not-use-classes-in-java-script/
- https://fek.io/blog/how-to-import-native-modules-using-the-new-es-6-module-syntax/
Questions
Node 16 and Modern JavaScript
By David Fekke
Node 16 and Modern JavaScript
These slides cover the changes to in Node.js 16
- 737