SlideShare a Scribd company logo
Applied Enterprise
Metaprogramming
in JavaScript
Vladyslav Dukhin
Lead Software Engineer at SoftServe
Metaprogramming

Theory
Node.js VM
Practical use cases
Metaprogramming

Theory
Node.js VM
Practical use cases
metaprogramming
metamemory
metaphor
metamorphism
metadata
metarule
metatheory
metalanguage
metamathematics
metaprogramming
metamemory
metaphor
metamorphism
metadata
metarule
metatheory
metalanguage
metamathematics
metaprogramming
metamemory
metaphor
metamorphism
metadata
metarule
metatheory
metalanguage
metamathematics
abstraction
A mechanism of hiding complicated
object details for the sake of simplicity
and ease of manipulation
abstraction layer
A way of hiding the details of a system
leading to interoperability, compatibility
and platform independence of this
system
OSI model
Physical Data Link Netwok Transport Session Presentation Application
Butler Lampson & Kevlin Henney
"All problems in computer science can be
solved by another level of abstraction, except
for the problem of too many layers of
abstraction"
Butler Lampson & Kevlin Henney
"All problems in computer science can be
solved by another level of abstraction, except
for the problem of too many layers of
abstraction"
Butler Lampson & Kevlin Henney
"All problems in computer science can be
solved by another level of indirection, except
for the problem of too many layers of
indirection"
What is the difference between
abstraction and indirection layers?
metaprogramming
Writing a program that can inspect or
modify the behaviour of itself or other
programs
metaprogramming
Treating source code as data that the
program can write, modify or inspect
Metaprogramming
Metaprogramming
Code Generation Code Analysis Code Transformation Self-modification
Metaprogramming
Code Generation Code Analysis Code Transformation Self-modification
Code generation
Runtime Design time Compile time
"Applied Enterprise Metaprogramming in JavaScript", Vladyslav Dukhin
Emmet
https://emmet.io
https://code.visualstudio.com/docs/editor/emmet
https://www.jetbrains.com/help/webstorm/settings-emmet.html
"Applied Enterprise Metaprogramming in JavaScript", Vladyslav Dukhin
Code Snippets
Snippets for VS Code: Jest Snippets, JavaScript code snippets
https://code.visualstudio.com/docs/editor/userdefinedsnippets
https://www.jetbrains.com/webstorm/guide/tips/save-as-live-template
{
"React funtional component template": {
"description": "React funtional component template",
"scope": "javascript,typescript",
"prefix": "fc",
"body": [
"import React from 'react';",
"import PropTypes from 'prop-types';n",
"export function ${1:ComponentName}({ ${2:props} }) {",
"treturn (",
"tt$0",
"t);",
"}n",
"${1:ComponentName}.propTypes = {",
"t${2/([^,s]+),?/$1: PropTypes.any,/g}",
"};"
]
}
}
Own IDE snippets
"Applied Enterprise Metaprogramming in JavaScript", Vladyslav Dukhin
scaffolding
A technique related to either a project
generation or code generation for data
access
Yeoman Generator: https://yeoman.io
npm install yo generator-code && yo code
Create React App: https://create-react-app.dev
npx create-react-app my-react-app
Loopback: https://loopback.io/doc/en/lb4/Getting-started.html
npm install @loopback/cli && npx lb4 app
Angular: https://github.com/angular/angular-cli
npm install @angular/cli && npx ng new angular-project-name
Code generation
Runtime Design time Compile time
transpiling
A translation of the source code from
one programming language to another
How is transpiling different
from the compilation?
How do transpilation and compilation work?

https://github.com/jamiebuilds/the-super-tiny-compiler

https://astexplorer.net
Babel plugin Yeoman generator

https://github.com/babel/generator-babel-plugin
How to create a Babel plugin?

https://github.com/jamiebuilds/babel-handbook
A collection of JavaScript code modifiers

https://github.com/cpojer/js-codemod
A collection of React.js code modifiers

https://github.com/reactjs/react-codemod
jscodeshift

https://github.com/facebook/jscodeshift
Code modifiers to migrate to Jest framework

https://github.com/skovhus/jest-codemods
npx react-codemod pure-component <path>

npx react-codemod update-react-imports <path>
Code generation
Runtime Design time Compile time
const json = '{"a":1,"b":2,"c":true,"d":"string","e":null}';
const evalJSON = eval('(' + json + ')');
const staticCodeExecutionResult = eval(`
const a = 7;
const b = 243;
a * b;
`);
const arr = [5, 7, 2, 3, 1, 4, 0];
const dynamicCodeExecutionResult = eval(`
[Math.min(${arr}), Math.max(${arr})]
`);
console.log(evalJSON);
console.log(staticCodeExecutionResult);
console.log(dynamicCodeExecutionResult);
Eval
const json = '{"a":1,"b":2,"c":true,"d":"string","e":null}';
const parseJSON = new Function('json', 'return JSON.parse(json)');
const parseJSONResult = parseJSON(json);
const multiply = new Function('a', 'b', 'return a * b');
const multiplyResult = multiply(7, 243);
const arr = [5, 7, 2, 3, 1, 4, 0];
const minMax = new Function('arr', 'return [Math.min(...arr), Math.max(...arr)]');
const minMaxResult = minMax(arr);
console.log(parseJSONResult);
console.log(multiplyResult);
console.log(minMaxResult);
new Function
import vm from 'vm';
global.arr = [5, 7, 2, 3, 1, 4, 0];
export const script = new vm.Script(`
[Math.min(...arr), Math.max(...arr)]
`);
const scriptMinMaxResult = script.runInThisContext();
const minMaxFunction = vm.compileFunction(`
return [Math.min(...arr), Math.max(...arr)]
`, ['arr']);
const functionMinMaxResult = minMaxFunction(global.arr);
const contextMinMaxResult = vm.runInThisContext(`
[Math.min(...arr), Math.max(...arr)]
`);
Node.js V8 Virtual Machine
--disallow-code-generation-from-strings
From the Node.js Docs: Make built-in language features like eval and new Function that generate code from strings throw an exception
instead. This does not affect the Node.js vm module. Added in: v9.8.0.
Performance comparison
general function x 15,566,583 ops/sec ±0.59% (84 runs sampled)

VM compileFunction x 15,483,118 ops/sec ±0.83% (86 runs sampled)
VM runInThisContext x 13,527 ops/sec ±50.77% (29 runs sampled)
VM script.runInThisContext x 864,776 ops/sec ±4.48% (85 runs sampled)
eval x 4,722,036 ops/sec ±0.67% (89 runs sampled)
new Function x 15,626,702 ops/sec ±0.78% (83 runs sampled)
Fastest is new Function, VM compileFunction and general function
0
4,000,000
8,000,000
12,000,000
16,000,000
compileFunction runInThisCtx script.runInThisCtx eval new Function general
15,566,583
15,626,702
4,722,036
864,776
13,527
15,483,118
ops/sec
Metaprogramming
Code Generation Code Analysis Code Transformation Self-modification
Metaprogramming
Code Generation Code Analysis Code Transformation Self-modification
introspection
A process of examining types, state and
properties of a code in the runtime
interception
A process of intercepting and redefining
fundamental language operations on an
object
reflection
The ability of a program to modify itself
at runtime by making use of
introspection and interception
Introspection in JavaScript
const symbol = Symbol('for object');
const objToIntrospect = {
keyOne: 1,
keyTwo: 10n,
keyThree: true,
keyFour: 'string',
[symbol]: null,
};
objToIntrospect.constructor; // => link to [Function: Object]
(100).constructor; // => link to [Function: Number]
(true).constructor; // => link to [Function: Boolean]
(() => {}).constructor; // => link to [Function: Function]
Introspection in JavaScript
objToIntrospect.prototype; // => undefined
Object.getPrototypeOf(objToIntrospect);
// => [Object: null prototype] {}
Reflect.getPrototypeOf(objToIntrospect);
// => [Object: null prototype] {}
Object.keys(objToIntrospect);
// => [ 'keyOne', 'keyTwo', 'keyThree', 'keyFour' ]
Object.getOwnPropertyNames(objToIntrospect);
// => [ 'keyOne', 'keyTwo', 'keyThree', 'keyFour' ]
Object.getOwnPropertySymbols(objToIntrospect);
// => [ Symbol(for object) ]
Reflect.ownKeys(objToIntrospect);
// => [ 'keyOne', 'keyTwo', 'keyThree', 'keyFour', Symbol(for object) ]
Introspection in JavaScript
Object.values(objToIntrospect);
// => [ 1, 10n, true, 'string' ]
Object.entries(objToIntrospect);
// => [['keyOne', 1], ['keyTwo', 10n], ['keyThree', true], ['keyFour', 'string']]
Object.prototype.hasOwnProperty.call(objToIntrospect, 'keyOne');
// => true
Object.hasOwn(objToIntrospect, 'keyOne'); // => true
Reflect.has(objToIntrospect, 'keyOne'); // => true
Introspection in JavaScript
Object.isExtensible(objToIntrospect); // => true
Reflect.isExtensible(objToIntrospect); // => true
Object.isFrozen(objToIntrospect); // => false
Object.isSealed(objToIntrospect); // => false
Object.getOwnPropertyDescriptor(objToIntrospect, symbol);
// => { value: null, writable: true, enumerable: true, configurable: true }
Reflect.getOwnPropertyDescriptor(objToIntrospect, symbol);
// => { value: null, writable: true, enumerable: true, configurable: true }
Object.getOwnPropertyDescriptors(objToIntrospect);
Introspection in JavaScript
const functionToIntrospect = (a, b, c = 1) => {
// returns a sum of arguments
return a + b + c;
};
functionToIntrospect.length; // => 2 (one argument is optional)
functionToIntrospect.name; // => functionToIntrospect
functionToIntrospect.toString();
/* =>
(a, b, c = 1) => {
// returns a sum of arguments
return a + b + c;
}
*/
function parseCustomHookName(functionName: void | string): string {
if (!functionName) {
return '';
}
let startIndex = functionName.lastIndexOf('.');
if (startIndex === -1) {
startIndex = 0;
}
if (functionName.substr(startIndex, 3) === 'use') {
startIndex += 3;
}
return functionName.substr(startIndex);
}
function isHookName(s) {
return /^use[A-Z0-9].*$/.test(s);
}
Source: https://github.com/facebook/react
From React 17.0.2 documentation:
A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks.
Layer.prototype.handle_request = function handle(req, res, next) {
var fn = this.handle;
if (fn.length > 3) {
// not a standard request handler
return next();
}
try {
fn(req, res, next);
} catch (err) {
next(err);
}
};
Source: https://github.com/expressjs/express
Error handling middleware in Express.js
Find out more at http://expressjs.com/en/guide/error-handling.html
Introspection in JavaScript
class ClassToIntrospect {
// constructor stores provided arguments
constructor(a, b, c = 1) {
this.a = a; this.b = b; this.c = c;
}
}
ClassToIntrospect.name; // => ClassToIntrospect
ClassToIntrospect.length;
// => 2 (constructor length, one argument is optional)
ClassToIntrospect.toString();
/* =>
class ClassToIntrospect {
// constructor stores provided arguments
constructor(a, b, c = 1) {
this.a = a;
this.b = b;
this.c = c;
}
}
*/
typeof variable;
variable instanceof Object;
Array.isArray(variable);
ArrayBuffer.isView(variable);
Should we treat RTTI
as introspection?
Introspection in Node.js
CommonJS Modules
__dirname; // => /path/to/the
__filename; // => /path/to/the/nodejs.js
require.main; // metadata of the entrypoint module
require.main === module; // is an entrypoint module
module.filename; // => /path/to/the/nodejs.js
module.path; // => /path/to/the
ECMAScript Modules
import.meta;
// => [Object: null prototype] {
// url: 'file:///path/to/the/nodejs.mjs'
// }
Introspection in V8
const exampleFunction = (...args) => Math.max(...args);
%FunctionGetScriptSourcePosition(exampleFunction); // => 97
%GetOptimizationStatus(exampleFunction);
Find more information at https://github.com/v8/v8/blob/master/src/runtime/runtime.h
Quine program
s="s=%j;console.log(s,s)";console.log(s,s)
console.log(%FunctionGetScriptSource(() => {}));
Let’s introspect
the JavaScript array
const arr = [1, 2, 3];
arr.toString(); // => '1, 2, 3'
Reflect.ownKeys(arr); // => ['0', '1', '2', 'length']
typeof arr; // => 'object'
Is array an object?
Yes!
Reflect.ownKeys(Array.prototype);
/* =>
[
'length',
'constructor',
'concat',
'copyWithin',
'fill',
'find',
'findIndex',
'lastIndexOf',
'pop',
'push',
'reverse',
'shift',
'unshift',
…
Symbol(Symbol.iterator),
Symbol(Symbol.unscopables)
]
*/
arr[Symbol.iterator].toString();
// => function values() { [native code] }
arr[Symbol.unscopables];
/* =>
[Object: null prototype] {
copyWithin: true,
entries: true,
fill: true,
find: true,
findIndex: true,
flat: true,
flatMap: true,
includes: true,
keys: true,
values: true,
at: true
}
*/
{
__type(name: "Character") {
name
kind
}
}
{
"data": {
"__type": {
"name": "Character",
"kind": "INTERFACE"
}
}
}
QraphQL Introspection
Find more at https://graphql.org/learn/introspection/
Symbol.iterator – used by for…of loop
Symbol.asyncIterator – used by for await...of loop
Symbol.hasInstance – used by instanceof operator
Symbol.isConcatSpreadable – used by Array.prototype.concat() method
Symbol.match – used by String.prototype.match() method
Symbol.matchAll – used by String.prototype.matchAll() method
Reflection in JavaScript
Reflection in JavaScript
Symbol.replace – used by String.prototype.replace() method
Symbol.search – used by String.prototype.search() method
Symbol.split – used by String.prototype.split() method
Symbol.toStringTag – used by Object.prototype.toString() method
Symbol.unscopables – specifies an object value containing keys that are
excluded from the "with" statement environment bindings
Symbol.toPrimitive – specifies a function valued property that is called to
convert an object to a corresponding primitive value
Symbol.species – specifies a function-valued property that the constructor
function uses to create derived objects
const range = (start, end, step = 1) => ({
*[Symbol.iterator]() {
for (let i = start; i <= end; i += step) {
yield i;
}
}
});
for (const n of range(10, 20)) {
console.log(n); // => 10...20
}
class User {
constructor(name, age, password) {
this.name = name;
this.age = age;
this.password = password;
}
[Symbol.toPrimitive](hint) {
if (hint === 'number') {
return this.age;
}
if (hint === 'string') {
return `${this.name} (${this.age} years old)`;
}
return this;
}
}
const user = new User('Charles Gibson', 54, 'SeCreT-PassW0rd');

console.log(+user); // => 54
console.log(`${user}`); // => Charles Gibson (54 years old)

console.log(user);
// => User { name: 'Charles Gibson', age: 54, password: 'SeCreT-PassW0rd' }
const inspect = Symbol.for('nodejs.util.inspect.custom');
class User {
static [Symbol.hasInstance](instance) {
if (instance === this) return true;
if (instance.password === '*****') return true;
return false;
}
[inspect]() {
const userFakePassword = {
...this,
password: '*****',
};
Object.defineProperty(userFakePassword, 'constructor', {
value: User,
enumerable: false,
});
return userFakePassword;
}
}
const user = new User('Charles Gibson', 54, 'SeCreT-PassW0rd');
console.log(user);
// => { name: 'Charles Gibson', age: 54, password: '*****' }
Reflection in JavaScript
const exampleObject = { a: 1, b: 2 };
Object.assign({ a: 0 }, { b: 1 }); // => { a: 0, b: 1 }
Object.assign({}, 'abc'); // => { '0': 'a', '1': 'b', '2': 'c' }
Object.create({});
Object.create(null);
Object.fromEntries([['a', 1], ['b', 2]]); // => { a: 1, b: 2 }
Object.setPrototypeOf(exampleObject, {});
Reflect.setPrototypeOf(exampleObject, {});
Object.defineProperty(exampleObject, 'c', { value: 4 });
Reflect.defineProperty(exampleObject, 'c', { value: 4 });
Object.defineProperties(exampleObject, { d: { value: 5 } });
Reflection in JavaScript
class Example {
a = 1;
b = 2;
}
const example = new Example();
example; // => Example { a: 1, b: 2 }
example.constructor; // => [class Example]
example instanceof Example; // => true
Object.assign(example, { c: 10 });
example; // => Example { a: 1, b: 2, c: 10 }
example.constructor; // => [class Example]
example instanceof Example; // => true
Reflection in JavaScript
Object.freeze(exampleObject);
Object.preventExtensions(exampleObject);
Reflect.preventExtensions(exampleObject);
Object.seal(exampleObject);
Reflect.construct(Set, [[1, 2, 3]]); // = new Set([1, 2, 3])
Reflect.deleteProperty(exampleObject, 'a'); // = delete exampleObject.a;
Reflect.get(exampleObject, 'b'); // = exampleObject.b
Reflect.set(exampleObject, 'a', 1); // equals exampleObject.a = 1
Interception in JavaScript
new Proxy(target, handler)
handler.apply()
handler.construct()
handler.defineProperty()
handler.deleteProperty()
handler.get()
handler.getOwnPropertyDescriptor()
handler.getPrototypeOf()
handler.has()
handler.isExtensible()
handler.ownKeys()
handler.preventExtensions()
handler.set()
handler.setPrototypeOf()
Performance comparison
obj.get x 931,245,919 ops/sec ±1.49% (85 runs sampled)
Proxy.get x 23,572,175 ops/sec ±0.64% (87 runs sampled)
obj.get is faster by 40x
0
250,000,000
500,000,000
750,000,000
1,000,000,000
obj.get Proxy.get
23,572,175
931,245,919
ops/sec
Interception in JavaScript
console.log(15 in range(10, 20));
for (const n of range(10, 20)) {
console.log(n);
}
Interception in JavaScript
const iterator = {
*[Symbol.iterator]() {
for (let i = start; i <= end; i += step) {
yield i;
}
}
};
const handlers = {
has(target, key) {
if (typeof key === 'symbol') {
return Reflect.has(target, key);
}
let n = Number(key); // cast type to number
if (n >= start && n <= end) {
return true;
}
return false;
}
};
const range = (start, end, step = 1) => {
return new Proxy(iterator, handlers);
};
Node.js Module Loaders
import css from './sample.css';
console.dir(css);
.hello {
content: 'Hello, World!';
color: cornflowerblue;
background-color: rgba(25, 25, 125, 1);
text-align: center;
}
.wow {
animation: linear 0.1s linear;
}
import { URL, pathToFileURL } from 'url';
import { cwd } from 'process';
import cssToJS from 'transform-css-to-js';
const baseURL = pathToFileURL(`${cwd()}/`).href;
export function resolve(specifier, context, defaultResolve) {
const { parentURL = baseURL } = context;
if (specifier.endsWith('.css')) {
return { url: new URL(specifier, parentURL).href };
}
return defaultResolve(specifier, context, defaultResolve);
}
export async function load(url, context, defaultLoad) {
if (url.endsWith('.css')) {
const { source: rawSource } = await defaultLoad(url, { format: 'module' });
return {
format: 'module',
source: 'export default ' + cssToJS(rawSource.toString(), true),
};
}
return defaultLoad(url, context, defaultLoad);
}
function addNumbers(a: number, b: number) {
return a + b;
}
var sum = addNumbers(10, 15)
console.log('Sum of the two numbers is: ' + sum);
node --experimental-loader=ts-node/esm example.ts
Metaprogramming examples
function timerGame(callback) {
console.log('Ready....go!');
setTimeout(() => {
console.log("Time's up -- stop!");
callback && callback();
}, 1000);
}
Mocks and stubs during testing
// test
jest.useFakeTimers();
jest.spyOn(global, 'setTimeout');
test('waits 1 second before ending the game', () => {
const timerGame = require('../timerGame');
timerGame();
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
});
import * as readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'process';
import express from 'express';
const app = express();
const cli = readline.createInterface({ input, output });
function displayCliMessage(...messages) {
output.clearLine(-1);
output.cursorTo(0);
console.log(...messages);
cli.prompt();
}
app.locals.featureFlags = {
shouldDisplayGreeting: true,
};
app.get('/', (req, res, next) => {
const { shouldDisplayGreeting } = res.app.locals.featureFlags;
displayCliMessage('shouldDisplayGreeting', shouldDisplayGreeting);
res.setHeader('Content-Type', 'text/plain');
if (shouldDisplayGreeting) {
const userName = req.query?.name ?? 'Anonymous';
return res.end(`Greetings, ${userName}! It's hello from server!`);
}
return res.end('Hello from server!');
});
app.listen(3000, () => {
displayCliMessage('Server is listening on port 3000');
});
cli.on('line', (command) => {
if (command === 'toggle') {
const { shouldDisplayGreeting } = app.locals.featureFlags;
app.locals.featureFlags.shouldDisplayGreeting = !shouldDisplayGreeting;
displayCliMessage('"shouldDisplayGreeting" flag is toggled!');
}
});
Feature flags
Function arguments validation
const validatedFunction = (userName /* string */, userAge /* number */, callback /* function */) => {
// here we are sure that arguments are of a valid type
};
Function arguments validation
const validatedFunction = validate((userName /* string */, userAge /* number */, callback /* function */) => {
// here we are sure that arguments are of a valid type
});
const validate = (fn) => {
const functionSourceCode = fn.toString();
const tokens = tokenize(functionSourceCode);
const annotations = tokens.filter(token => token.type === 'annotation');
return (...args) => {
if (args.some((arg, i) => typeof arg !== annotations[i].token)) {
throw new Error('invalid arguments');
}
return fn(...args);
};
}
const tokenize = (functionSourceCode) => {
const buffer = Buffer.from(functionSourceCode);
const bufferLength = buffer.length;
let parserMode = PARSER_MODES.ARGUMENT;
let handler = PARSER_MODE_HANDLERS[parserMode];
const ctx = {
charCode: 0,
tokens: [],
index: buffer.indexOf(40) + 1, // '('
isToken: false,
tokenStart: 0,
buffer,
};
let parenthesisBalanceCount = ctx.index > 0 ? 1 : 0;
while (parenthesisBalanceCount > 0 && ctx.index < bufferLength) {
ctx.charCode = ctx.buffer[ctx.index];
handler = PARSER_MODE_HANDLERS[parserMode];
parserMode = handler(ctx);
if (ctx.charCode === 40 /* ( */) {
++parenthesisBalanceCount;
} else if (ctx.charCode === 41 /* ) */ && parserMode === PARSER_MODES.ARGUMENT) {
--parenthesisBalanceCount;
}
if (parenthesisBalanceCount === 0) break;
++ctx.index;
}
return ctx.tokens;
};
PARSER_MODE_HANDLERS[PARSER_MODES.ARGUMENT] = (ctx) => {
if (ctx.charCode === 47) { // '/'
const nextCharCode = ctx.buffer[ctx.index + 1];
if (nextCharCode === 42) { // '*'
return PARSER_MODES.MULTILINE_ANNOTATION;
}
if (nextCharCode === 47) { // '/'
return PARSER_MODES.LINE_ANNOTATION;
}
}
if (!ctx.isToken && isAlphaNumeric(ctx.charCode)) {
ctx.tokenStart = ctx.index;
ctx.isToken = true;
} else if (ctx.isToken && !isAlphaNumeric(ctx.charCode)) {
const token = ctx.buffer.toString(undefined, ctx.tokenStart, ctx.index);
ctx.tokens.push({ type: 'argument', token });
ctx.isToken = false;
}
return PARSER_MODES.ARGUMENT;
};
Find out more on my GitHub
github.com/primeare
linkedin.com/in/vladyslav-dukhin
instagram.com/vladyslav.dev
Thank you for
your time

More Related Content

What's hot (20)

PDF
Супер быстрая автоматизация тестирования на iOS
SQALab
 
PDF
Using JHipster for generating Angular/Spring Boot apps
Yakov Fain
 
PDF
GlassFish Embedded API
Eduardo Pelegri-Llopart
 
PDF
How do I write Testable Javascript - Presented at dev.Objective() June 16, 2016
Gavin Pickin
 
PDF
"Technical Challenges behind Visual IDE for React Components" Tetiana Mandziuk
Fwdays
 
PDF
Backday Xebia : Découvrez Spring Boot sur un cas pratique
Publicis Sapient Engineering
 
PDF
Роман Лютиков "Web Apps Performance & JavaScript Compilers"
Fwdays
 
PDF
Introducing spring
Ernesto Hernández Rodríguez
 
PDF
Philip Shurpik "Architecting React Native app"
Fwdays
 
PDF
Spring IO '15 - Developing microservices, Spring Boot or Grails?
Fátima Casaú Pérez
 
PDF
jQuery plugin & testing with Jasmine
Miguel Parramón Teixidó
 
PDF
Workshop React.js
Commit University
 
PPTX
Web Performance & Latest in React
Talentica Software
 
PDF
Alexander Mostovenko "'Devide at impera' with GraphQL and SSR"
Fwdays
 
PPTX
High Performance NodeJS
Dicoding
 
PDF
When Web meet Native App
Yu-Wei Chuang
 
PDF
Gearman work queue in php
Bo-Yi Wu
 
PDF
JavaScript + Jenkins = Winning!
Eric Wendelin
 
PPTX
Node js Introduction
sanskriti agarwal
 
PDF
Node, express & sails
Brian Shannon
 
Супер быстрая автоматизация тестирования на iOS
SQALab
 
Using JHipster for generating Angular/Spring Boot apps
Yakov Fain
 
GlassFish Embedded API
Eduardo Pelegri-Llopart
 
How do I write Testable Javascript - Presented at dev.Objective() June 16, 2016
Gavin Pickin
 
"Technical Challenges behind Visual IDE for React Components" Tetiana Mandziuk
Fwdays
 
Backday Xebia : Découvrez Spring Boot sur un cas pratique
Publicis Sapient Engineering
 
Роман Лютиков "Web Apps Performance & JavaScript Compilers"
Fwdays
 
Introducing spring
Ernesto Hernández Rodríguez
 
Philip Shurpik "Architecting React Native app"
Fwdays
 
Spring IO '15 - Developing microservices, Spring Boot or Grails?
Fátima Casaú Pérez
 
jQuery plugin & testing with Jasmine
Miguel Parramón Teixidó
 
Workshop React.js
Commit University
 
Web Performance & Latest in React
Talentica Software
 
Alexander Mostovenko "'Devide at impera' with GraphQL and SSR"
Fwdays
 
High Performance NodeJS
Dicoding
 
When Web meet Native App
Yu-Wei Chuang
 
Gearman work queue in php
Bo-Yi Wu
 
JavaScript + Jenkins = Winning!
Eric Wendelin
 
Node js Introduction
sanskriti agarwal
 
Node, express & sails
Brian Shannon
 

Similar to "Applied Enterprise Metaprogramming in JavaScript", Vladyslav Dukhin (20)

KEY
JavaScript Growing Up
David Padbury
 
PPTX
Metaprogramming in ES6
Héctor Pablos López
 
PPTX
Metaprogramming with JavaScript
Timur Shemsedinov
 
PPTX
Metaprogramming, Metaobject Protocols, Gradual Type Checks: Optimizing the "U...
Stefan Marr
 
PPTX
JavaScript (without DOM)
Piyush Katariya
 
PPT
Intermediate JavaScript
☆ Milan Adamovsky ☆
 
PPTX
Awesomeness of JavaScript…almost
Quinton Sheppard
 
PDF
Rediscovering JavaScript: The Language Behind The Libraries
Simon Willison
 
PPTX
Java script
Adrian Caetano
 
PDF
Intro to React
Troy Miles
 
PDF
Practical JavaScript Programming - Session 8/8
Wilson Su
 
PPTX
Don't Be Afraid of Abstract Syntax Trees
Jamund Ferguson
 
PPTX
Academy PRO: ES2015
Binary Studio
 
PDF
JavaScript in 2015
Igor Laborie
 
PDF
Introduction to web programming for java and c# programmers by @drpicox
David Rodenas
 
PDF
ES6 - Next Generation Javascript
RameshNair6
 
PPTX
Javascript Basics
msemenistyi
 
PPTX
ES6: Features + Rails
Santosh Wadghule
 
PDF
React Native One Day
Troy Miles
 
JavaScript Growing Up
David Padbury
 
Metaprogramming in ES6
Héctor Pablos López
 
Metaprogramming with JavaScript
Timur Shemsedinov
 
Metaprogramming, Metaobject Protocols, Gradual Type Checks: Optimizing the "U...
Stefan Marr
 
JavaScript (without DOM)
Piyush Katariya
 
Intermediate JavaScript
☆ Milan Adamovsky ☆
 
Awesomeness of JavaScript…almost
Quinton Sheppard
 
Rediscovering JavaScript: The Language Behind The Libraries
Simon Willison
 
Java script
Adrian Caetano
 
Intro to React
Troy Miles
 
Practical JavaScript Programming - Session 8/8
Wilson Su
 
Don't Be Afraid of Abstract Syntax Trees
Jamund Ferguson
 
Academy PRO: ES2015
Binary Studio
 
JavaScript in 2015
Igor Laborie
 
Introduction to web programming for java and c# programmers by @drpicox
David Rodenas
 
ES6 - Next Generation Javascript
RameshNair6
 
Javascript Basics
msemenistyi
 
ES6: Features + Rails
Santosh Wadghule
 
React Native One Day
Troy Miles
 
Ad

More from Fwdays (20)

PPTX
"Як ми переписали Сільпо на Angular", Євген Русаков
Fwdays
 
PDF
"AI Transformation: Directions and Challenges", Pavlo Shaternik
Fwdays
 
PDF
"Validation and Observability of AI Agents", Oleksandr Denisyuk
Fwdays
 
PPTX
"Autonomy of LLM Agents: Current State and Future Prospects", Oles` Petriv
Fwdays
 
PDF
"Beyond English: Navigating the Challenges of Building a Ukrainian-language R...
Fwdays
 
PPTX
"Co-Authoring with a Machine: What I Learned from Writing a Book on Generativ...
Fwdays
 
PPTX
"Human-AI Collaboration Models for Better Decisions, Faster Workflows, and Cr...
Fwdays
 
PDF
"AI is already here. What will happen to your team (and your role) tomorrow?"...
Fwdays
 
PPTX
"Is it worth investing in AI in 2025?", Alexander Sharko
Fwdays
 
PDF
''Taming Explosive Growth: Building Resilience in a Hyper-Scaled Financial Pl...
Fwdays
 
PDF
"Scaling in space and time with Temporal", Andriy Lupa.pdf
Fwdays
 
PDF
"Database isolation: how we deal with hundreds of direct connections to the d...
Fwdays
 
PDF
"Scaling in space and time with Temporal", Andriy Lupa .pdf
Fwdays
 
PPTX
"Provisioning via DOT-Chain: from catering to drone marketplaces", Volodymyr ...
Fwdays
 
PPTX
" Observability with Elasticsearch: Best Practices for High-Load Platform", A...
Fwdays
 
PPTX
"How to survive Black Friday: preparing e-commerce for a peak season", Yurii ...
Fwdays
 
PPTX
"Istio Ambient Mesh in production: our way from Sidecar to Sidecar-less",Hlib...
Fwdays
 
PPTX
" How to survive with 1 billion vectors and not sell a kidney: our low-cost c...
Fwdays
 
PPTX
"Confidential AI: zero trust concept", Hennadiy Karpov
Fwdays
 
PPTX
"Choosing Tensor Accelerators for Specific Tasks: Compute vs Memory Bound Mod...
Fwdays
 
"Як ми переписали Сільпо на Angular", Євген Русаков
Fwdays
 
"AI Transformation: Directions and Challenges", Pavlo Shaternik
Fwdays
 
"Validation and Observability of AI Agents", Oleksandr Denisyuk
Fwdays
 
"Autonomy of LLM Agents: Current State and Future Prospects", Oles` Petriv
Fwdays
 
"Beyond English: Navigating the Challenges of Building a Ukrainian-language R...
Fwdays
 
"Co-Authoring with a Machine: What I Learned from Writing a Book on Generativ...
Fwdays
 
"Human-AI Collaboration Models for Better Decisions, Faster Workflows, and Cr...
Fwdays
 
"AI is already here. What will happen to your team (and your role) tomorrow?"...
Fwdays
 
"Is it worth investing in AI in 2025?", Alexander Sharko
Fwdays
 
''Taming Explosive Growth: Building Resilience in a Hyper-Scaled Financial Pl...
Fwdays
 
"Scaling in space and time with Temporal", Andriy Lupa.pdf
Fwdays
 
"Database isolation: how we deal with hundreds of direct connections to the d...
Fwdays
 
"Scaling in space and time with Temporal", Andriy Lupa .pdf
Fwdays
 
"Provisioning via DOT-Chain: from catering to drone marketplaces", Volodymyr ...
Fwdays
 
" Observability with Elasticsearch: Best Practices for High-Load Platform", A...
Fwdays
 
"How to survive Black Friday: preparing e-commerce for a peak season", Yurii ...
Fwdays
 
"Istio Ambient Mesh in production: our way from Sidecar to Sidecar-less",Hlib...
Fwdays
 
" How to survive with 1 billion vectors and not sell a kidney: our low-cost c...
Fwdays
 
"Confidential AI: zero trust concept", Hennadiy Karpov
Fwdays
 
"Choosing Tensor Accelerators for Specific Tasks: Compute vs Memory Bound Mod...
Fwdays
 
Ad

Recently uploaded (20)

PDF
SIZING YOUR AIR CONDITIONER---A PRACTICAL GUIDE.pdf
Muhammad Rizwan Akram
 
PDF
UPDF - AI PDF Editor & Converter Key Features
DealFuel
 
DOCX
Python coding for beginners !! Start now!#
Rajni Bhardwaj Grover
 
PDF
“Squinting Vision Pipelines: Detecting and Correcting Errors in Vision Models...
Edge AI and Vision Alliance
 
PDF
“Computer Vision at Sea: Automated Fish Tracking for Sustainable Fishing,” a ...
Edge AI and Vision Alliance
 
PDF
“Voice Interfaces on a Budget: Building Real-time Speech Recognition on Low-c...
Edge AI and Vision Alliance
 
PDF
Automating Feature Enrichment and Station Creation in Natural Gas Utility Net...
Safe Software
 
PDF
NASA A Researcher’s Guide to International Space Station : Physical Sciences ...
Dr. PANKAJ DHUSSA
 
PPTX
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
PPTX
Digital Circuits, important subject in CS
contactparinay1
 
PDF
NLJUG Speaker academy 2025 - first session
Bert Jan Schrijver
 
PDF
Staying Human in a Machine- Accelerated World
Catalin Jora
 
PPTX
Seamless Tech Experiences Showcasing Cross-Platform App Design.pptx
presentifyai
 
PDF
What’s my job again? Slides from Mark Simos talk at 2025 Tampa BSides
Mark Simos
 
PDF
Kit-Works Team Study_20250627_한달만에만든사내서비스키링(양다윗).pdf
Wonjun Hwang
 
PPTX
Future Tech Innovations 2025 – A TechLists Insight
TechLists
 
DOCX
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
PDF
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
PPTX
Agentforce World Tour Toronto '25 - Supercharge MuleSoft Development with Mod...
Alexandra N. Martinez
 
PDF
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 
SIZING YOUR AIR CONDITIONER---A PRACTICAL GUIDE.pdf
Muhammad Rizwan Akram
 
UPDF - AI PDF Editor & Converter Key Features
DealFuel
 
Python coding for beginners !! Start now!#
Rajni Bhardwaj Grover
 
“Squinting Vision Pipelines: Detecting and Correcting Errors in Vision Models...
Edge AI and Vision Alliance
 
“Computer Vision at Sea: Automated Fish Tracking for Sustainable Fishing,” a ...
Edge AI and Vision Alliance
 
“Voice Interfaces on a Budget: Building Real-time Speech Recognition on Low-c...
Edge AI and Vision Alliance
 
Automating Feature Enrichment and Station Creation in Natural Gas Utility Net...
Safe Software
 
NASA A Researcher’s Guide to International Space Station : Physical Sciences ...
Dr. PANKAJ DHUSSA
 
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
Digital Circuits, important subject in CS
contactparinay1
 
NLJUG Speaker academy 2025 - first session
Bert Jan Schrijver
 
Staying Human in a Machine- Accelerated World
Catalin Jora
 
Seamless Tech Experiences Showcasing Cross-Platform App Design.pptx
presentifyai
 
What’s my job again? Slides from Mark Simos talk at 2025 Tampa BSides
Mark Simos
 
Kit-Works Team Study_20250627_한달만에만든사내서비스키링(양다윗).pdf
Wonjun Hwang
 
Future Tech Innovations 2025 – A TechLists Insight
TechLists
 
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
Agentforce World Tour Toronto '25 - Supercharge MuleSoft Development with Mod...
Alexandra N. Martinez
 
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 

"Applied Enterprise Metaprogramming in JavaScript", Vladyslav Dukhin

  • 1. Applied Enterprise Metaprogramming in JavaScript Vladyslav Dukhin Lead Software Engineer at SoftServe
  • 7. abstraction A mechanism of hiding complicated object details for the sake of simplicity and ease of manipulation
  • 8. abstraction layer A way of hiding the details of a system leading to interoperability, compatibility and platform independence of this system
  • 9. OSI model Physical Data Link Netwok Transport Session Presentation Application
  • 10. Butler Lampson & Kevlin Henney "All problems in computer science can be solved by another level of abstraction, except for the problem of too many layers of abstraction"
  • 11. Butler Lampson & Kevlin Henney "All problems in computer science can be solved by another level of abstraction, except for the problem of too many layers of abstraction"
  • 12. Butler Lampson & Kevlin Henney "All problems in computer science can be solved by another level of indirection, except for the problem of too many layers of indirection"
  • 13. What is the difference between abstraction and indirection layers?
  • 14. metaprogramming Writing a program that can inspect or modify the behaviour of itself or other programs
  • 15. metaprogramming Treating source code as data that the program can write, modify or inspect
  • 17. Metaprogramming Code Generation Code Analysis Code Transformation Self-modification
  • 18. Metaprogramming Code Generation Code Analysis Code Transformation Self-modification
  • 19. Code generation Runtime Design time Compile time
  • 23. Code Snippets Snippets for VS Code: Jest Snippets, JavaScript code snippets https://code.visualstudio.com/docs/editor/userdefinedsnippets https://www.jetbrains.com/webstorm/guide/tips/save-as-live-template
  • 24. { "React funtional component template": { "description": "React funtional component template", "scope": "javascript,typescript", "prefix": "fc", "body": [ "import React from 'react';", "import PropTypes from 'prop-types';n", "export function ${1:ComponentName}({ ${2:props} }) {", "treturn (", "tt$0", "t);", "}n", "${1:ComponentName}.propTypes = {", "t${2/([^,s]+),?/$1: PropTypes.any,/g}", "};" ] } } Own IDE snippets
  • 26. scaffolding A technique related to either a project generation or code generation for data access
  • 27. Yeoman Generator: https://yeoman.io npm install yo generator-code && yo code Create React App: https://create-react-app.dev npx create-react-app my-react-app Loopback: https://loopback.io/doc/en/lb4/Getting-started.html npm install @loopback/cli && npx lb4 app Angular: https://github.com/angular/angular-cli npm install @angular/cli && npx ng new angular-project-name
  • 28. Code generation Runtime Design time Compile time
  • 29. transpiling A translation of the source code from one programming language to another
  • 30. How is transpiling different from the compilation?
  • 31. How do transpilation and compilation work?
 https://github.com/jamiebuilds/the-super-tiny-compiler
 https://astexplorer.net Babel plugin Yeoman generator
 https://github.com/babel/generator-babel-plugin How to create a Babel plugin?
 https://github.com/jamiebuilds/babel-handbook
  • 32. A collection of JavaScript code modifiers
 https://github.com/cpojer/js-codemod A collection of React.js code modifiers
 https://github.com/reactjs/react-codemod jscodeshift
 https://github.com/facebook/jscodeshift Code modifiers to migrate to Jest framework
 https://github.com/skovhus/jest-codemods npx react-codemod pure-component <path>
 npx react-codemod update-react-imports <path>
  • 33. Code generation Runtime Design time Compile time
  • 34. const json = '{"a":1,"b":2,"c":true,"d":"string","e":null}'; const evalJSON = eval('(' + json + ')'); const staticCodeExecutionResult = eval(` const a = 7; const b = 243; a * b; `); const arr = [5, 7, 2, 3, 1, 4, 0]; const dynamicCodeExecutionResult = eval(` [Math.min(${arr}), Math.max(${arr})] `); console.log(evalJSON); console.log(staticCodeExecutionResult); console.log(dynamicCodeExecutionResult); Eval
  • 35. const json = '{"a":1,"b":2,"c":true,"d":"string","e":null}'; const parseJSON = new Function('json', 'return JSON.parse(json)'); const parseJSONResult = parseJSON(json); const multiply = new Function('a', 'b', 'return a * b'); const multiplyResult = multiply(7, 243); const arr = [5, 7, 2, 3, 1, 4, 0]; const minMax = new Function('arr', 'return [Math.min(...arr), Math.max(...arr)]'); const minMaxResult = minMax(arr); console.log(parseJSONResult); console.log(multiplyResult); console.log(minMaxResult); new Function
  • 36. import vm from 'vm'; global.arr = [5, 7, 2, 3, 1, 4, 0]; export const script = new vm.Script(` [Math.min(...arr), Math.max(...arr)] `); const scriptMinMaxResult = script.runInThisContext(); const minMaxFunction = vm.compileFunction(` return [Math.min(...arr), Math.max(...arr)] `, ['arr']); const functionMinMaxResult = minMaxFunction(global.arr); const contextMinMaxResult = vm.runInThisContext(` [Math.min(...arr), Math.max(...arr)] `); Node.js V8 Virtual Machine
  • 37. --disallow-code-generation-from-strings From the Node.js Docs: Make built-in language features like eval and new Function that generate code from strings throw an exception instead. This does not affect the Node.js vm module. Added in: v9.8.0.
  • 38. Performance comparison general function x 15,566,583 ops/sec ±0.59% (84 runs sampled)
 VM compileFunction x 15,483,118 ops/sec ±0.83% (86 runs sampled) VM runInThisContext x 13,527 ops/sec ±50.77% (29 runs sampled) VM script.runInThisContext x 864,776 ops/sec ±4.48% (85 runs sampled) eval x 4,722,036 ops/sec ±0.67% (89 runs sampled) new Function x 15,626,702 ops/sec ±0.78% (83 runs sampled) Fastest is new Function, VM compileFunction and general function 0 4,000,000 8,000,000 12,000,000 16,000,000 compileFunction runInThisCtx script.runInThisCtx eval new Function general 15,566,583 15,626,702 4,722,036 864,776 13,527 15,483,118 ops/sec
  • 39. Metaprogramming Code Generation Code Analysis Code Transformation Self-modification
  • 40. Metaprogramming Code Generation Code Analysis Code Transformation Self-modification
  • 41. introspection A process of examining types, state and properties of a code in the runtime
  • 42. interception A process of intercepting and redefining fundamental language operations on an object
  • 43. reflection The ability of a program to modify itself at runtime by making use of introspection and interception
  • 44. Introspection in JavaScript const symbol = Symbol('for object'); const objToIntrospect = { keyOne: 1, keyTwo: 10n, keyThree: true, keyFour: 'string', [symbol]: null, }; objToIntrospect.constructor; // => link to [Function: Object] (100).constructor; // => link to [Function: Number] (true).constructor; // => link to [Function: Boolean] (() => {}).constructor; // => link to [Function: Function]
  • 45. Introspection in JavaScript objToIntrospect.prototype; // => undefined Object.getPrototypeOf(objToIntrospect); // => [Object: null prototype] {} Reflect.getPrototypeOf(objToIntrospect); // => [Object: null prototype] {} Object.keys(objToIntrospect); // => [ 'keyOne', 'keyTwo', 'keyThree', 'keyFour' ] Object.getOwnPropertyNames(objToIntrospect); // => [ 'keyOne', 'keyTwo', 'keyThree', 'keyFour' ] Object.getOwnPropertySymbols(objToIntrospect); // => [ Symbol(for object) ] Reflect.ownKeys(objToIntrospect); // => [ 'keyOne', 'keyTwo', 'keyThree', 'keyFour', Symbol(for object) ]
  • 46. Introspection in JavaScript Object.values(objToIntrospect); // => [ 1, 10n, true, 'string' ] Object.entries(objToIntrospect); // => [['keyOne', 1], ['keyTwo', 10n], ['keyThree', true], ['keyFour', 'string']] Object.prototype.hasOwnProperty.call(objToIntrospect, 'keyOne'); // => true Object.hasOwn(objToIntrospect, 'keyOne'); // => true Reflect.has(objToIntrospect, 'keyOne'); // => true
  • 47. Introspection in JavaScript Object.isExtensible(objToIntrospect); // => true Reflect.isExtensible(objToIntrospect); // => true Object.isFrozen(objToIntrospect); // => false Object.isSealed(objToIntrospect); // => false Object.getOwnPropertyDescriptor(objToIntrospect, symbol); // => { value: null, writable: true, enumerable: true, configurable: true } Reflect.getOwnPropertyDescriptor(objToIntrospect, symbol); // => { value: null, writable: true, enumerable: true, configurable: true } Object.getOwnPropertyDescriptors(objToIntrospect);
  • 48. Introspection in JavaScript const functionToIntrospect = (a, b, c = 1) => { // returns a sum of arguments return a + b + c; }; functionToIntrospect.length; // => 2 (one argument is optional) functionToIntrospect.name; // => functionToIntrospect functionToIntrospect.toString(); /* => (a, b, c = 1) => { // returns a sum of arguments return a + b + c; } */
  • 49. function parseCustomHookName(functionName: void | string): string { if (!functionName) { return ''; } let startIndex = functionName.lastIndexOf('.'); if (startIndex === -1) { startIndex = 0; } if (functionName.substr(startIndex, 3) === 'use') { startIndex += 3; } return functionName.substr(startIndex); } function isHookName(s) { return /^use[A-Z0-9].*$/.test(s); } Source: https://github.com/facebook/react From React 17.0.2 documentation: A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks.
  • 50. Layer.prototype.handle_request = function handle(req, res, next) { var fn = this.handle; if (fn.length > 3) { // not a standard request handler return next(); } try { fn(req, res, next); } catch (err) { next(err); } }; Source: https://github.com/expressjs/express Error handling middleware in Express.js Find out more at http://expressjs.com/en/guide/error-handling.html
  • 51. Introspection in JavaScript class ClassToIntrospect { // constructor stores provided arguments constructor(a, b, c = 1) { this.a = a; this.b = b; this.c = c; } } ClassToIntrospect.name; // => ClassToIntrospect ClassToIntrospect.length; // => 2 (constructor length, one argument is optional) ClassToIntrospect.toString(); /* => class ClassToIntrospect { // constructor stores provided arguments constructor(a, b, c = 1) { this.a = a; this.b = b; this.c = c; } } */
  • 52. typeof variable; variable instanceof Object; Array.isArray(variable); ArrayBuffer.isView(variable); Should we treat RTTI as introspection?
  • 53. Introspection in Node.js CommonJS Modules __dirname; // => /path/to/the __filename; // => /path/to/the/nodejs.js require.main; // metadata of the entrypoint module require.main === module; // is an entrypoint module module.filename; // => /path/to/the/nodejs.js module.path; // => /path/to/the ECMAScript Modules import.meta; // => [Object: null prototype] { // url: 'file:///path/to/the/nodejs.mjs' // }
  • 54. Introspection in V8 const exampleFunction = (...args) => Math.max(...args); %FunctionGetScriptSourcePosition(exampleFunction); // => 97 %GetOptimizationStatus(exampleFunction); Find more information at https://github.com/v8/v8/blob/master/src/runtime/runtime.h
  • 57. const arr = [1, 2, 3]; arr.toString(); // => '1, 2, 3' Reflect.ownKeys(arr); // => ['0', '1', '2', 'length'] typeof arr; // => 'object' Is array an object? Yes!
  • 58. Reflect.ownKeys(Array.prototype); /* => [ 'length', 'constructor', 'concat', 'copyWithin', 'fill', 'find', 'findIndex', 'lastIndexOf', 'pop', 'push', 'reverse', 'shift', 'unshift', … Symbol(Symbol.iterator), Symbol(Symbol.unscopables) ] */ arr[Symbol.iterator].toString(); // => function values() { [native code] } arr[Symbol.unscopables]; /* => [Object: null prototype] { copyWithin: true, entries: true, fill: true, find: true, findIndex: true, flat: true, flatMap: true, includes: true, keys: true, values: true, at: true } */
  • 59. { __type(name: "Character") { name kind } } { "data": { "__type": { "name": "Character", "kind": "INTERFACE" } } } QraphQL Introspection Find more at https://graphql.org/learn/introspection/
  • 60. Symbol.iterator – used by for…of loop Symbol.asyncIterator – used by for await...of loop Symbol.hasInstance – used by instanceof operator Symbol.isConcatSpreadable – used by Array.prototype.concat() method Symbol.match – used by String.prototype.match() method Symbol.matchAll – used by String.prototype.matchAll() method Reflection in JavaScript
  • 61. Reflection in JavaScript Symbol.replace – used by String.prototype.replace() method Symbol.search – used by String.prototype.search() method Symbol.split – used by String.prototype.split() method Symbol.toStringTag – used by Object.prototype.toString() method Symbol.unscopables – specifies an object value containing keys that are excluded from the "with" statement environment bindings Symbol.toPrimitive – specifies a function valued property that is called to convert an object to a corresponding primitive value Symbol.species – specifies a function-valued property that the constructor function uses to create derived objects
  • 62. const range = (start, end, step = 1) => ({ *[Symbol.iterator]() { for (let i = start; i <= end; i += step) { yield i; } } }); for (const n of range(10, 20)) { console.log(n); // => 10...20 }
  • 63. class User { constructor(name, age, password) { this.name = name; this.age = age; this.password = password; } [Symbol.toPrimitive](hint) { if (hint === 'number') { return this.age; } if (hint === 'string') { return `${this.name} (${this.age} years old)`; } return this; } } const user = new User('Charles Gibson', 54, 'SeCreT-PassW0rd');
 console.log(+user); // => 54 console.log(`${user}`); // => Charles Gibson (54 years old)
 console.log(user); // => User { name: 'Charles Gibson', age: 54, password: 'SeCreT-PassW0rd' }
  • 64. const inspect = Symbol.for('nodejs.util.inspect.custom'); class User { static [Symbol.hasInstance](instance) { if (instance === this) return true; if (instance.password === '*****') return true; return false; } [inspect]() { const userFakePassword = { ...this, password: '*****', }; Object.defineProperty(userFakePassword, 'constructor', { value: User, enumerable: false, }); return userFakePassword; } } const user = new User('Charles Gibson', 54, 'SeCreT-PassW0rd'); console.log(user); // => { name: 'Charles Gibson', age: 54, password: '*****' }
  • 65. Reflection in JavaScript const exampleObject = { a: 1, b: 2 }; Object.assign({ a: 0 }, { b: 1 }); // => { a: 0, b: 1 } Object.assign({}, 'abc'); // => { '0': 'a', '1': 'b', '2': 'c' } Object.create({}); Object.create(null); Object.fromEntries([['a', 1], ['b', 2]]); // => { a: 1, b: 2 } Object.setPrototypeOf(exampleObject, {}); Reflect.setPrototypeOf(exampleObject, {}); Object.defineProperty(exampleObject, 'c', { value: 4 }); Reflect.defineProperty(exampleObject, 'c', { value: 4 }); Object.defineProperties(exampleObject, { d: { value: 5 } });
  • 66. Reflection in JavaScript class Example { a = 1; b = 2; } const example = new Example(); example; // => Example { a: 1, b: 2 } example.constructor; // => [class Example] example instanceof Example; // => true Object.assign(example, { c: 10 }); example; // => Example { a: 1, b: 2, c: 10 } example.constructor; // => [class Example] example instanceof Example; // => true
  • 67. Reflection in JavaScript Object.freeze(exampleObject); Object.preventExtensions(exampleObject); Reflect.preventExtensions(exampleObject); Object.seal(exampleObject); Reflect.construct(Set, [[1, 2, 3]]); // = new Set([1, 2, 3]) Reflect.deleteProperty(exampleObject, 'a'); // = delete exampleObject.a; Reflect.get(exampleObject, 'b'); // = exampleObject.b Reflect.set(exampleObject, 'a', 1); // equals exampleObject.a = 1
  • 68. Interception in JavaScript new Proxy(target, handler) handler.apply() handler.construct() handler.defineProperty() handler.deleteProperty() handler.get() handler.getOwnPropertyDescriptor() handler.getPrototypeOf() handler.has() handler.isExtensible() handler.ownKeys() handler.preventExtensions() handler.set() handler.setPrototypeOf()
  • 69. Performance comparison obj.get x 931,245,919 ops/sec ±1.49% (85 runs sampled) Proxy.get x 23,572,175 ops/sec ±0.64% (87 runs sampled) obj.get is faster by 40x 0 250,000,000 500,000,000 750,000,000 1,000,000,000 obj.get Proxy.get 23,572,175 931,245,919 ops/sec
  • 70. Interception in JavaScript console.log(15 in range(10, 20)); for (const n of range(10, 20)) { console.log(n); }
  • 71. Interception in JavaScript const iterator = { *[Symbol.iterator]() { for (let i = start; i <= end; i += step) { yield i; } } }; const handlers = { has(target, key) { if (typeof key === 'symbol') { return Reflect.has(target, key); } let n = Number(key); // cast type to number if (n >= start && n <= end) { return true; } return false; } }; const range = (start, end, step = 1) => { return new Proxy(iterator, handlers); };
  • 73. import css from './sample.css'; console.dir(css); .hello { content: 'Hello, World!'; color: cornflowerblue; background-color: rgba(25, 25, 125, 1); text-align: center; } .wow { animation: linear 0.1s linear; } import { URL, pathToFileURL } from 'url'; import { cwd } from 'process'; import cssToJS from 'transform-css-to-js'; const baseURL = pathToFileURL(`${cwd()}/`).href; export function resolve(specifier, context, defaultResolve) { const { parentURL = baseURL } = context; if (specifier.endsWith('.css')) { return { url: new URL(specifier, parentURL).href }; } return defaultResolve(specifier, context, defaultResolve); } export async function load(url, context, defaultLoad) { if (url.endsWith('.css')) { const { source: rawSource } = await defaultLoad(url, { format: 'module' }); return { format: 'module', source: 'export default ' + cssToJS(rawSource.toString(), true), }; } return defaultLoad(url, context, defaultLoad); }
  • 74. function addNumbers(a: number, b: number) { return a + b; } var sum = addNumbers(10, 15) console.log('Sum of the two numbers is: ' + sum); node --experimental-loader=ts-node/esm example.ts
  • 76. function timerGame(callback) { console.log('Ready....go!'); setTimeout(() => { console.log("Time's up -- stop!"); callback && callback(); }, 1000); } Mocks and stubs during testing // test jest.useFakeTimers(); jest.spyOn(global, 'setTimeout'); test('waits 1 second before ending the game', () => { const timerGame = require('../timerGame'); timerGame(); expect(setTimeout).toHaveBeenCalledTimes(1); expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000); });
  • 77. import * as readline from 'node:readline/promises'; import { stdin as input, stdout as output } from 'process'; import express from 'express'; const app = express(); const cli = readline.createInterface({ input, output }); function displayCliMessage(...messages) { output.clearLine(-1); output.cursorTo(0); console.log(...messages); cli.prompt(); } app.locals.featureFlags = { shouldDisplayGreeting: true, }; app.get('/', (req, res, next) => { const { shouldDisplayGreeting } = res.app.locals.featureFlags; displayCliMessage('shouldDisplayGreeting', shouldDisplayGreeting); res.setHeader('Content-Type', 'text/plain'); if (shouldDisplayGreeting) { const userName = req.query?.name ?? 'Anonymous'; return res.end(`Greetings, ${userName}! It's hello from server!`); } return res.end('Hello from server!'); }); app.listen(3000, () => { displayCliMessage('Server is listening on port 3000'); }); cli.on('line', (command) => { if (command === 'toggle') { const { shouldDisplayGreeting } = app.locals.featureFlags; app.locals.featureFlags.shouldDisplayGreeting = !shouldDisplayGreeting; displayCliMessage('"shouldDisplayGreeting" flag is toggled!'); } }); Feature flags
  • 78. Function arguments validation const validatedFunction = (userName /* string */, userAge /* number */, callback /* function */) => { // here we are sure that arguments are of a valid type };
  • 79. Function arguments validation const validatedFunction = validate((userName /* string */, userAge /* number */, callback /* function */) => { // here we are sure that arguments are of a valid type });
  • 80. const validate = (fn) => { const functionSourceCode = fn.toString(); const tokens = tokenize(functionSourceCode); const annotations = tokens.filter(token => token.type === 'annotation'); return (...args) => { if (args.some((arg, i) => typeof arg !== annotations[i].token)) { throw new Error('invalid arguments'); } return fn(...args); }; }
  • 81. const tokenize = (functionSourceCode) => { const buffer = Buffer.from(functionSourceCode); const bufferLength = buffer.length; let parserMode = PARSER_MODES.ARGUMENT; let handler = PARSER_MODE_HANDLERS[parserMode]; const ctx = { charCode: 0, tokens: [], index: buffer.indexOf(40) + 1, // '(' isToken: false, tokenStart: 0, buffer, }; let parenthesisBalanceCount = ctx.index > 0 ? 1 : 0; while (parenthesisBalanceCount > 0 && ctx.index < bufferLength) { ctx.charCode = ctx.buffer[ctx.index]; handler = PARSER_MODE_HANDLERS[parserMode]; parserMode = handler(ctx); if (ctx.charCode === 40 /* ( */) { ++parenthesisBalanceCount; } else if (ctx.charCode === 41 /* ) */ && parserMode === PARSER_MODES.ARGUMENT) { --parenthesisBalanceCount; } if (parenthesisBalanceCount === 0) break; ++ctx.index; } return ctx.tokens; };
  • 82. PARSER_MODE_HANDLERS[PARSER_MODES.ARGUMENT] = (ctx) => { if (ctx.charCode === 47) { // '/' const nextCharCode = ctx.buffer[ctx.index + 1]; if (nextCharCode === 42) { // '*' return PARSER_MODES.MULTILINE_ANNOTATION; } if (nextCharCode === 47) { // '/' return PARSER_MODES.LINE_ANNOTATION; } } if (!ctx.isToken && isAlphaNumeric(ctx.charCode)) { ctx.tokenStart = ctx.index; ctx.isToken = true; } else if (ctx.isToken && !isAlphaNumeric(ctx.charCode)) { const token = ctx.buffer.toString(undefined, ctx.tokenStart, ctx.index); ctx.tokens.push({ type: 'argument', token }); ctx.isToken = false; } return PARSER_MODES.ARGUMENT; };
  • 83. Find out more on my GitHub