npm install
npm link babel-cli
See the ECMAScript standard by TC39, ECMA-262
The JSON standard can be found at ECMA-404
JavaScript
was first calledMocha
, thenLiveScript
- First announced in December, 1995
- First released in Netscape Navigator 2.0 in March, 1996
- Delivered to standards body
Ecma International
in November, 1996 - Categorized as
ECMA-262
under working groupTC39
- Release timeline
es1
was adopted in June, 1997es2
was adopted in June, 1998es3
was adopted in December, 1999- The 4th edition was never published
es5
was adopted in December, 2009es5.1
was adopted in June, 2011es6
, officiallyES2015
, was adopted June 2015es7
, orES2016
, is small and will only contain 2 new featuresES2017
draft spec- github/tc39/ecma262 official
- es 5.1 implementations
SpiderMonkey
, used in Firefox with theGecko
layout engineV8
, used in ChromeJavaScriptCore (Nitro)
, used in WebKit, SafariChakra
, used in Microsoft EdgeJScript
, used in IE with theTrident
layout engineRhino
, used in Java
Highlights:
- if a construct is not optimizable, the entire containing function becomes unoptimized
- this is true even if the code is unreachable
- if necessary, isolate such code to a minimal function
- currently not optimizable (in the major vms)
- functions with
try{} catch{}
ortry{} finally{}
- functions containing object literal having
.__proto__
- e.g. as might be done with es6 object literal initializers
- functions containing object literals having
get
orset
properties - functions with
eval()
- functions with
with() {}
- functions containing
debugger
- functions with
- leaking
arguments
arguments
object must not be leaked OR passed() => arguments
[].slice.call(arguments)
- Only use
arguments
with.length
or[i]
wherei
is a valid integer index intoarguments
- i.e. index cannot be out of bounds
fn.apply(y, arguments
is a special case and is OK
for...in
with 'non-local' key- the referenced 'key' must be a 'pure local variable'
- don't do
var key; var f=(obj)=>{for (key in obj)}
- don't do
var f=(obj)=>{for (var key in obj){}; return ()=>key;}
- don't do
- the referenced 'key' must be a 'pure local variable'
- iterating over non
simple enumerable
objects- non
simple enumerable
is one using properties that aren't valid identifiers - this results in
hash table mode
ordictionary mode
, backed by a hash table - this includes objects containing a property that is an
array index
(a.k.a. an element)- never use
for...in
with anArray
function() { var a=[1,2]; for (var i in a) {} }
will not be optimized- if necessary, use
Object.keys(a)
with a regularfor
loop
- never use
- non
hash table mode
- when re-structuring an object
- don't use
delete
- prefer assignment to
undefined
- prefer assignment to
- don't dynamically add properties after initialization
- prefer a fully predefined structure via constructor or object literal assignment
- don't use
- when using properties that aren't
valid identifiers
- e.g.
{'-': 3}
- e.g.
- when an object has
enumerable properties
in itsprototype chain
- e.g.
Object.prototype.fn = function() {}
- e.g.
- when re-structuring an object
- variables are loosely typed, not statically typed
- variables can be assigned and reassigned to values of different types
- variable declarations are hoisted to the top of their function scope
- for function declarations
- the function declaration and definition are hoisted
- for all variable declarations
- the variable declaration is hoisted, but not the assignment
- this includes function expression definitions
- the variable declaration is hoisted, but not the assignment
function outer() {
a(); // -> "a"
b(); // -> TypeError: b is not a function
// for this function declaration,
// both declaration and definition are hoisted
function a() {
console.log("a");
}
// for this variable declaration,
// only the variable declaration is hoisted
var b = function() {
console.log("b");
}
}
- built-in basic data types such as String (as returned from
typeof
)- Note:
instanceof
can be used to check constructor types that are not of the basic types string
,number
,boolean
,null
,undefined
,object
, (function
)string
is a special case- a string literal primitive is not an object
typeof('abc') === 'string' // true
'abc' instanceof Object // false
- a string literal can be used as if it were an object
'abc'.substring(1) // -> 'b
- the js runtime autoboxes the string literal to a String object to perform the operation
'abc'.substring(1)
is logically equivalent tonew String('abc').substring(1) // -> 'bc'
String.substring
returns a string literal, not a new String object
- a string literal primitive is not an object
number
,boolean
,string
literal primitives can be autoboxed(1).toFixed(2) // '1.00'
true.valueOf() // true
'abc'.indexOf('b') // 1
null
is a special case- a global primitive property that has a formal type of
object
but with no properties or methods - it is not itself an instance of any
Object
- it is a value that represents
the absence of an object
- a variable assigned to the value
null
is not a reference to anything typeof (null) === 'object' // true
null instanceof Object // false
null === null // true
Number(null) // 0
- a global primitive property that has a formal type of
undefined
is a special case- a "primitive" value that has the formal type of
undefined
- it is not an object
typeof(undefined) === 'undefined' // true
undefined instanceof Object // false
undefined === undefined // true
Number(undefined) // NaN
- a "primitive" value that has the formal type of
NaN
is an especially special casetypeof (NaN) === 'number' // true
NaN instanceof Number // false
NaN === NaN // false
NaN == NaN // false
isNaN(1) // false
isNaN("a") // true
function
is a special case- a function is a regular object with the additional capability of being
callable
typeof (function(){}) === 'function'
- a function literal of typefunction
(function(){}) instanceof Object // true
(function(){}) instanceof Function // true
- a function is a regular object with the additional capability of being
new function(){}
creates a new object via the function literal's constructor- returns
{}
- a plainobject
typeof (new function(){}) === 'object'
(new function(){}) instanceof Object // true
(new function(){}) instanceof Function // false
- returns
- Note:
- javascript primitive data types
string
,number
,boolean
,null
,undefined
- primitives are discrete types
- simple value types as opposed to complex object types
- javascript
object
data type- an object is a key/value (hash) data structure
- a function is a callable object, so some objects are functions
- objects that are
function
types can be constructors withnew
typeof Object === 'function' // true
typeof Number === 'function' // true
typeof String === 'function' // true
typeof Boolean === 'function' // true
typeof Array === 'function' // true
- objects that are
- note that object prototypal "inheritance" is delegative OLOO (objects linking to other objects)
({}).__proto__ === Object.prototype // true
- built-in objects
String
,Number
,Boolean
- these provide the prototype for "instance wrappers" around the associated primitive types
- String methods
charAt
,indexOf
,concat
,split
,slice
,substring
,toLowerCase
,startsWith
,trim
- regex:
match
,replace
,search
Array
,Function
,Object
,RegExp
- An instance of Array is a regular object having integer key properties that have a relationship to its
length
property- the integer key properties act as a numbered index
Array
type checkingvar arr = []
arr instanceof Array // true
arr.constructor === Array // true
Array.isArray(arr) // true
- An instance of
Function
is a callable object- can be called/invoked as a function (to execute arbitrary work)
- can be used with the
new
operator as a constructor to return a new object of the given typevar Person = function() {}; var me = new Person()
- within the function,
var foo = function bar() {}
, call usingfoo()
,bar()
- do not use
arguments.callee
- not supported in strict mode. Use the function name.
Object
constructornew Object()
returns the empty object{}
new Object(1)
returns a new Number objectnew Object(1) === new Object(1) // false
two new objects that happen to have the same valuevar a = new Number(1); new Object(a) === new Object(a) // true
each returns the objecta
Object.freeze()
var a = {p:1}; Object.freeze(a)
-p
cannot be modified or extended
Object.preventExtensions()
var a = {p:1}; Object.preventExtensions(a)
-p
can be changed butq
can't be added
- Object creation by copying (shallow clone) an object (not prototypal inheritance)
var b = Object.assign({}, a)
copies own properties froma
to target and returns the target
- Object creation with prototypal inheritance
function Shape() {this.x=0, this.y=1}
Shape.prototype.move = function(x,y) {this.x += x; this.y += y;}
function Rectangle() {Shape.call(this)}
Rectangle.prototype = Object.create(Shape.prototype)
Rectangle.prototype.constructor = Rectangle
var r = new Rectangle(); r.move(1,1)
- RegExp literal expression
- start and end with
/
var r = /ab+c/
r instanceof RegExp // true
- start and end with
- An instance of Array is a regular object having integer key properties that have a relationship to its
- in operator
x in obj
checks ifx
is a property ofobj
, regardless of the enumerable flagfor (var x in obj)
gets only enumerable properties (own and inherited)
- comma operator
for (var i = 0, j = 9; i <= j; i++, j--){}
delete
operatorvar obj = {a:1}; delete obj.a;
- See delete on MDN
- NOTE:
delete
has a degenerative v8 optimization penalty
- void operator
void(0)
- spread operator
var a=[1,2,3]; var b=[0, ...a] // b: [0,1,2,3]
var f = (x,y,z) => x + y + z; f(...a); // calls f(1,2,3) --> returns 6
- an object literal is an
Object
var a = {}
is a shortcut for the constructor functionvar a = new Object()
{}.__proto__ === Object.prototype // true
{}.constructor.prototype === Object.prototype // true
- whereas
Object.create(null)
inherites from nothing Object.create(null).__proto__ // -> undefined
- consider properties to be like variables attached to an object
- a property can be an identifier, string literal, or a number
- a property that is not a valid identifier can only be accessed via bracket notation
var a = {a:1, 20:"twenty", "":"empty", "@-^": "works"}
a.a // 1
a[20] // 'twenty'
a["20"] // 'twenty
a[""] // 'empty'
a["@-^"] // 'works'
- add a property using
defineProperty
var a = {}
Object.defineProperty(a, 'p', {configurable:true, enumerable:true, writable:true}) // {p: undefined}
Object.defineProperty(a, 'p', {configurable:true, enumerable:true, value:'val'}) // {p: 'val'}
- by default, the prop will be immutable and not enumerable (excluded from
for...in
loop andObject.keys()
)Object.defineProperty(a, 'p', {}) // immutable
- object property enumeration
for...in
loops over all own enumerable properties and in prototype chain- use
obj.hasOwnProperty(key)
to check for only own properties Object.keys(obj)
array of all own (not in prototype chain) enumerable property namesObject.getOwnPropertyNames(obj)
array of all own property names (both enumerable and not)- note that while
for (var x in obj)
gets only enumerable propertiesx in obj
simply checks ifx
is a property of obj, regardless of the enumerable flag
- get object property descriptors
- to determine
configurable
,enumerable
,writable
,value
Object.getOwnPropertyDescriptor(obj, prop)
- See this jsbin
- to determine
- create an object of a given type without a constructor function
var Phone = {brand:"generic", getBrand: function() {console.log(this.brand)}}
var iphone = Object.create(Phone);
iphone.brand = "iphone"
iphone.getBrand() // iphone
- [jsbin example]{http://jsbin.com/jiruva/1/edit?js,console}
- getters and setters
var a = {a: 1, get propA() {return this.a}, set propA(x) {this.a = x}}
a.a // 1
a.propA // 1
a.propA = 100
a.a // 100
- object literals in ES2015 (es6)
- can declare the target prototype, equivalent to the imperative
Object.create(targetProtoObject)
- can use compact function notation
- can use computed property names
- can declare the target prototype, equivalent to the imperative
var obj = {
// __proto__
__proto__: targetProtoObject,
// Shorthand for ‘handler: handler’
handler,
// Methods
toString() {
// Super calls
return "d " + super.toString();
},
// Computed (dynamic) property names
[ 'prop_' + (() => 42)() ]: 42
};
- An array is of type '
object
' but is printed as '[]' as opposed to '{}'- An Array is an iterable object with index keys and a length property
- es6 array iterators have a
.next()
methodvar a = [10,20,30]
var aIter = a.keys()
aIter.next().value // -> 10
- An iterator
a.keys()
is sparse (doesn't skip holes);Object.keys()
is densevar a = [10,,20,,30,40]
contains two elisions ata[1]
anda[3]
[...a.keys()] // -> [0,1,2,3,4,5]
Object.keys(a) // -> ['0', '2', '4', '5']
Array.forEach()
visits each element in the array, as opposed to each index.var a = [10,,20]
- will visit
a[0]
anda[2]
, as there is no member aa[1]
- Check if an object is an array
Array.isArray(obj)
obj instanceof Array
- An array literal is a kind of object initializer
- Extra commas in array literals
var a = [1,2,,4,5,,,]
- the unspecified elements will be
undefined
a[2] // undefined
Object.keys(a) // [ '0', '1', '3', '4' ]
- the last of any trailing commas is ignored
a.length // -> 7
[10,].length // -> 1
[10,,].length // -> 2
- The
length
property- setting length can resize an array
var a = [1,2]
a.length // 2
a.length = 5 // -> [1, 2, , , ]
a.length = 1 // -> [1]
- The
arguments
object of a regular function is an 'array-like' object that prints{}
.- It can be converted to a proper array with
Array.from(arguments)
(es6) Array.slice(arguments,0)
(es5) also works but is not recommended- (via MDN) "You should not slice on arguments because it prevents optimizations in JavaScript engines (V8 for example)"
- It can be converted to a proper array with
- An array-like object is an iterable object and can be converted to a proper Array
Array.from({'0':10, '1':20, 'length':2} ) // -> [10,20]
Array.from({'0':10, '1':20}) // -> []
- Array prototype methods may alter or create. '
shift
' alters, whereas 'slice
' creates.- some methods that return a new array
.concat()
,.slice()
,.filter()
,.map()
,.reduce()
- note that while
.map()
doesn't alter the original array, it can be modified from the callback functionfn(curVal,i,origArray)
- some methods that alter the array
.shift()
- returns the removed (head) element.unshift()
- returns the new length of the array.splice()
- returns the removed elements (in an array).reverse()
- returnsundefined
.sort()
- returnsundefined
- some methods that return a new array
babel-node code/arrays.js
- Arrow functions inherit
this
from the enclosing context, also known as 'lexical' or 'static' context. The 'this
' reference is not dynamically determined and is not observed when supplied from abind
,call
, orapply
method.- A regular function that isn't already bound can be re-wired to a different object.
- An arrow function is equivalent to
function() {}.bind(this)
that is statically bound and cannot be re-bound
- Arrow functions do not get their own
arguments
object. - Since arrow functions don't get a proper
arguments
, use a 'rest' parameter instead.- Note that arrays and 'rest' objects can be used to 'spread' out as parameters to another function invocation.
- Use parentheses to return an object literal when using concise syntax
let f = () => ({ a: 1});
// without parens, it's just a function block without a return statement and returnsundefined
.
babel-node code/arrow-functions.js
- Function declaration vs function expression
- A function declaration gets added to the execution context's 'variable object'
- In the global context,
function f() {}; "f" in window // -> true
- In the global context,
- A function expression is evaluated inline
(function f() {}); "f" in window // -> false
- A function declaration gets added to the execution context's 'variable object'
- Function currying reduces the 'arity' of a function into an invocation chain of unary functions
- Partial application reduces the 'arity' of a function by binding (applying) some parameter values
- Use default params to change the 'arity' of a function for partial application
- Use
Function.prototype.bind
to define leading args (es5)var f = function (a,b,c) {return a+b+c}
var g = f.bind(null,1,2)
g(3); // returns 6
- or use a fat arrow to create an unary function
var g = c => f(1,2,c);
- Use default trailing parameters (es6)
- Use
- example of currying
var f = function(x,y) {return x+y}
var g = function(x) { return function(y) {return x+y}}
f(2,3) // -> 5
g(2)(3) // -> 5
babel-node code/functions.js
- a traditional class is an abstract definition used to "instantiate" or "realize" a concrete object
- an es6 class is syntactic sugar over the prototype model
- classes are not hoisted (whereas functions are)
// class declaration
class Father {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
// class expression
const Father = class {
...
}
class Son extends Father {
getName() {
return super.getName() + " Jr.";
}
}
var boy = new Son("Jack");
boy.getName(); // -> "Jack Jr."
- a prototype is an existing object that acts as a template to initialize a new object
var P = function() {this.test = 500}
var b = new P()
- creates an object from the constructor function of
P
b
is a new object, initialized to the value{test: 500}
b.__proto__ === P.prototype
now b has a pointer toP
b
has its own object value but also inherits fromP
b.__proto__
can be assigned to another parent,b.__proto__ = Q.prototype
.prototype
changes are dynamic and visible to all childrenvar c = new P()
P.prototype.hi=10
b.hi === 10 && c.hi === 10 // true
- creates an object from the constructor function of
- use
Object.create
var a = {hi: function() {return "hi"}}
var b = Object.create(a)
b.__proto__ === a // true
b.hi() // -> "hi"
- create a type hierarchy
var Animal = function() {this.alive = true}
var Mammal = function() {Animal.call(this); this.warm = true}
var dog = new Mammal()
dog.alive && dog.warm // true
- A variable can be undefined, and a value can be undefined
var a
// a declared, b not declaredtypeof(a) // 'undefined'
typeof(b) // 'undefined'
a == null // true
b == null // in strict mode throws ReferenceError: b is not defined
for...of
(es2015) - likefor...in
but iterates over the property values instead of the keys- strict mode
- place
use strict
at the head of a file - or place
use strict
at the beginning of a function - do not concatenate mixed strict and non-strict files (whichever is first wins)
- place
- template strings
- string literals with embedded expressions
var num = 22
var t = `the number is ${num}`
let
and Temporal Dead Zoneconsole.log(foo)
let foo=1
foo
is hoisted to the top of its scope, but it is unrecognized until the declaration- the above will throw a
ReferenceError
- the region between the hoisted position and the declaration is known as a
TDZ
or Temporal Dead Zone
- the above will throw a
- Promises
var p = new Promise(function (resolve,reject){ if (success) resolve(data) else reject(reason) });
- jsbin XMLHttpRequest example
- MDN reference and examples
- async/await
- a function can be marked
async
- the function can then
await
a Promise - the function can be written in synchronous style
- the function is still async, so can't return the outcome of the Promise
- codepen async/await
- a function can be marked
- Generators, Iterables, Observables
- TBD
- destructuring
- Given:
let arr = [1,2,3];
- Then -
let [el1, el2] = arr;
is equivalent tovar el1=arr[0], el2=arr[1];
- Given:
let obj = {p:1, q:2, r:3};
- Then -
let {p, q} = obj;
is equivalent tovar p=obj.p, q=obj.q;
- Given:
setTimeout
vssetInterval
setTimeout
- will be called after some specified delay
- a blocking operation can further delay the execution
- a nested setTimeout will occur in
delay + block + execution + delay + block
- the calls do not 'stack up'
- there will not be a flurry of calls
setInterval
- will be queued every
n
milliseconds - a blocking operation that spans multiple intervals will enqueue at most 1 call
- the delay between calls is not guaranteed
- if a call is queued, it may execute near the next scheduled call
- it could be more or less than the specified delay
- there will not be a "flurry" of calls
- will be queued every
- http://2ality.com/2015/01/es6-destructuring.html
- Execution context
- an object used to track progress of the associated code
- can be global ec, function ec, eval ec
- maintained on a stack, with the "global ec" at the bottom and the "active ec" at the top
- an execution context contains
- variable object - vars, function decl, args
- scope chain - Variable object + parent scopes
- this value - Context object
- Variable object
- contains variables, function declarations
- in the browser global execution context, the Variable object is the global
window
object - in a function call (activation), an object, known as an Activation object is created
- the Activation object is used as the Variable object for the function context
- it is a Variable object augmented with the function's formal parameters and
arguments
object
- Scope chain
- like a prototype chain, a scope chain is a chain of scope objects
- in an execution context, the Scope object is the Variable object
- resolving a variable starts in the own scope (Variable/Activation object)
- lookup continues up the chain of parent variable objects, ending at the global scope
- free variables (non-local variables) require scope chain lookup
- a scope chain is the set of own and parent Variable objects
- a scope chain consists of linked scope objects
- a variable lookup implies a prototype chain search for each scope link
- a scope object may include dynamically added material (e.g. with objects)
See Below:
- in the
with
block, local scope is that of{}
x
is not found in that local scopex
is sought up the prototype chain and is found atObject.prototype
- the search for
x
does not proceed up the scope chain wherex
has the value100
- a scope search exhausts the local scope object's prototype chain before proceeding along the scope chain
- a scope search does a
__proto__
lookup before a__parent__
lookup
// the 2-dimensional prototype and scope chain lookup
// can lead to unexpected behavior outside of strict mode
Object.prototype.x = 10; // top of prototype chain
var x = 100; // top of scope chain
with ({}) {
console.log(x); // prints 10, not 100
}
console.log(x); // prints 100, not 10
- Closures
- closures are meant to handle the funargs (functional arguments) problem
- there are two flavors of funargs problems, upward, and downward
- upward refers to how free variables are resolved when a function is returned from a function
- the inner function saves the parent scope chain at the time of creation
- this kind of scope is known as static or lexical scope
- downward refers to how a function resolves an ambiguous identifier
- given a local and global variable, the static scope at the time of function creation is used
- this is the same lexical scope behavior as above
- a function, together with its saved static/lexical scope chain is the definition of a closure
- since all functions save lexical scope at creation, all functions are closures
- a function has a static/lexical scope chain known and saved at the time of creation/definition
- this is used to resolve free variable lookups
- the combination of the function and its scope chain form a closure
// when a function is created, the current lexical scope is saved with the closure
var a = 10;
function f() {
return a;
}
function g() {
var a = 100;
return f();
}
g(); // -> returns 10
function x() {
var b = 50;
return function() {
return b;
}
}
var fx = x();
var b = 500;
fx(); // -> returns 50
- this value
this
is another property of an execution context- it is the context object against which an execution context is activated
- the value of
this
is determined when entering the execution context - in the global execution context,
this
is the global object and so equals the Variable object - in a function context, the value of
this
depends on how the function is activated/called - if a function is a property of an owner object and activated via method call, then
this
is the owner object - if a function ref is disassociated from any object and then activated,
this
is the global object- unless the function was previously bound to some context object
- as with Function#bind
- or by the implicit binding of an arrow function
- unless the function was previously bound to some context object
function f() {
let context = {
val: 10,
fn: function() {
return () => this.val;
}
}
return context.fn();
}
let x = {
test: 100,
arrow: f.fn()
}
let theArrow = x.arrow;
theArrow(); // -> 10, because the arrow function is innately bound to and thus activated against 'context'
- the language core information was extracted from Dmitry Soshnikov's javascript blog
- flatten an array using recursive reduce
babel-node code/reduce.js
- find two numbers in an array that sum up to an input value
babel-node code/two-sum.js
- circular references aren't garbage collected in IE, leading to memory leaks
- use polyfills to support older browsers
- use transpilers (e.g. babel) to compile es6 down to vanilla js
- use event delegation, e.g.
$(document).on( "click", "#selector", function() {});
- interesting difference in the treatment of an explicit vs implicit variable on the global object
var a=1;
b=1;
window.c=1;
- for the above
- all three will add a property to the global window object
- but
a
is not configurable - i.e. does not allowdelete
from the global object - See this jsbin
- web events
-
vm turn
- one cycle through the js event loop
-
Functional Programming
- Pure functions - idempotent, free of side effects, avoids shared/mutable data
- First class functions - first class citizens and can be passed around
- Higher order functions - functions that take or return other functions
- Encourages immutability and loose coupling; avoids inflexible class hierarchies
-
Prototypal Inheritance
- Favors delegation/composition over class inheritance
- Instances inherit directly from other objects (OLOO - objects linked to other objects)
- Achieved using function constructors with
new
or viaObject.create(somePrototype)
-
Concatenative (or Selective) inheritance
- Mixin composition from one of more source objects
- See Object.assign()
-
Exemplar Prototypes
- Used for flexible cloning as a means of "concatenative inheritance" instead of rigid class libraries
- Memoization
- An optimization technique that saves calculated results in a lookup table for repeated access
- Example:
code/two-sum.js
- Given an array of integers, find two numbers such that they add up to a specific target number
- Avoid the O(n^2) nested loop
- Use a single pass O(n) loop storing candidates in a map by key
value2 = target - value1
for lookup matching of subsequent entries
- Virtual DOM
- Memory structure for staging DOM changes
- Performs an efficient before/after diff (a.k.a reconciliation in React)
- Minimizes expensive DOM updates and thus reduces browser DOM reflow churn
- Utilizes synthetic events to allow event listeners to run without incurring the cost of browser event propagation
- Conceptually similar to double-buffering in game programming
- es6 features
- es6 equivalents in es5
- VerbalExpressions - Use common language expressions to compose RegEx
- front end developer questions - 20k github stars
- most starred github projects
- javascript framework resources - 50k github stars
- postman commandline
- v8 optimization killers
- 30 seconds of code
- 10 Interview Questions Every Javascript Developer Should Know - by Eric Elliott
- The javascript job market
- The es6 conundrum
- leetcode mockinterview
- Job search technical interviewing
- Intervews: CS/Algos vs engineering - Hacker News
- Interview process at Stripe
- Inside V8 with Lars Bak
- grunt, gulp, webpack
- javascript the core, by Dmitry Soshkinov
- Cognates - words that have the same etymological origin (from Latin
cognatus
, meaningblood relative
)- Example -
memoize
&memorize
- Example -
https://en.wikipedia.org/wiki/Cognate#/media/File:Etymological_Relationships_Tree.png