Skip to content

Instantly share code, notes, and snippets.

@macouella
Last active September 12, 2017 08:54
Show Gist options
  • Save macouella/b3f2aa269f95e134c8cc9f24ab493048 to your computer and use it in GitHub Desktop.
Save macouella/b3f2aa269f95e134c8cc9f24ab493048 to your computer and use it in GitHub Desktop.
JS Notes

ES6

ECMAScript table

https://kangax.github.io/compat-table/es6/

Block Scoping

A variable can be either of global or local scope. A global variable is a variable declared in the main body of the source code, outside all functions, while a local variable is one declared within the body of a function or a block. Modern versions allow nested lexical scoping.

Let vs var

The difference is scoping. var is scoped to the nearest function block and let is scoped to the nearest enclosing block, which can be smaller than a function block. Both are global if outside any block.

Also, variables declared with let are not accessible before they are declared in their enclosing block. As seen in the demo, this will throw a ReferenceError exception.

Const

A signal that the identifier won’t be reassigned. Holds a pointer to a space in memory. Pointers can't be changed, but the values of the pointed object can be changed.

const person = {
  name: 'Igi'
}
person.name = 'Igis' // updates the name property of the person object, but not the reference!

Hoisting

Let, const and class declarations are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable’s Lexical Binding is evaluated. In short - you can only access them after the declarations have been evaluated.

Fat arrow functions

ES6 introduces a new way to write functions. When keeping the this reference to the outer environment in ES5, you can use workarounds like the .bind method or create a closure using var self = this. Arrow functions allow you to retain the scope of the caller inside the function, you don’t need to create self = this; closures or use bind.

var fn = function(){};
fn = () => {};
// E.g.
fn = (a,b) => a + b;
fn = (a,b) => {
  return a + b;
}

Binding events with fat arrows and keeping callback references

I encountered an issue when binding an event in a class using ES6 syntax and trying to remove the event. To properly unregister an event, there needs to be a reference to the function being registered. This is achieved by reassigning the function and binding (this) inside ES6 classes.

export default class Button{
  constructor(){
    // The alert box 
    this.button = document.getElementById('button');
    // The reference to this needs to be preserved via bind.
    this.alertBox = this.alertBox().bind(this);
    this.element.addEventListener('click', this.alertBox)
    this.element.removeEventListener('click', this.alertBox)
  }
  alertBox(){
    alert('you clicked!')
  }
}

Default parameters

In ES6 - we can assign default values to arguments

someFuncion(someVariable = 'Default Value', somethingSimilar = someVariable) => console.log(someVariable)
someFunction() // logs Default Value

Object literal extensions

ES6 provides extensions for object literals.

let name = "Mike";
let age = 25;
let skill = "writing";
let skillField = skill;
let obj = {
  name,
  age,
  [skillField]: 'good', // Dynamic field useful for dynamic computed property access
  someFunction(){
    console.log(`${this.name}`);
  }
}
obj.someFunction();
obj.age;
obj['writing'];
obj[skill];

Rest operator

The rest parameter syntax allows us to represent an indefinite number of arguments as an array.

sum(...numbers) => {
  let result = 0;
  for(let i = 0; i < numbers.length; i++){
    result += numbers[i];
  }
  return result;
}
sum(1,2,3,4); // returns 10;

Spread operator

Spread syntax allows an iterable such as an array expression to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected.

let numbers = [1,2,3,4,5];
console.log(...numbers) // splits up the array to a list of values
Math.max(...numbers);

For of loop

testResult = [1,2,3];
for(let testResult of testResults){
  console.log(testResult);
}

Template literals

Allows writing of multi-line strings and interpolation.

`${name} Hello.`

\ can be used to escape the $ sign.

Destructuring arrays

The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables. A copy is made so the original object is left untouched.

let stuff = [1, 'toby', 'girly', 'sam-smith', 'gary-x']
let [a,b,,x, ...d] = stuff;
// a = 1
// b = 'toby'
// girly doesn't get destructured
// d = ['sam-smith', ['gary-x']]
// defualt values can be assigned as well, e.g. let [a,b=2]

Quick variable swap

let a = 5;
let b = 10;
[b,a] = [a,b] // b = 5, a = 10

Destructuring objects

let ob = {
  age: 21;
  greet: ()=>{
    console.log(this.age);
  }
  another: ()=>{
  }
}
let {age, greet, another: greetF} = ob;
// greet() should work
// greetF() is an alias for another()
// age gets destructured as a variable, i.e. age

Modules

There are two important Rules, which you need to understand if you're working with ES6 Modules:

Modules are always in Strict Mode (no need to define "use strict") Modules don't have a shared, global Scope. Instead each Module has its own execution context / scope.

Import & export

The export statement is used when creating JavaScript modules to export functions, objects, or primitive values from the module so they can be used by other programs with the import statement.

// There are two different types of export. Each type corresponds to one of the above syntax:

// Named exports:
// exports a function declared earlier
export { myFunction }; 

// exports a constant
export const foo = Math.sqrt(2);

// Default exports (function):
export default function() {}

// Default exports (class):
export default class {}
// Importing
import { cube, foo as bar } from 'my-module';
import defaultClass from 'my-module';
// or better yet
import defaultClass, { cube, foo as bar } from 'my-module'
// Importing everything
import * as something from 'my-module'

Classes

JavaScript classes, introduced in ECMAScript 2015, are primarily syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax is not introducing a new object-oriented inheritance model to JavaScript. JavaScript classes provide a much simpler and clearer syntax to create objects and deal with inheritance.

class Person(name){
  constructor(name){
    this.name = name;
  }
  greet(){
    alert(`Hello ${this.name}`
  }
}
let person = new Person('Igi');
instanceof person // Person
person.__proto__ === Person.prototype // returns true

Inheritance

When inheriting, we use the extends keyword. Prototypes of the child and the inherited class won't be the same, but properties and methods will be extended / carried over.

// Person...
class Staff extends Person {
  constructor(name, age){
    super(name);
    this.age = age;
  }
  
  greet(){
    super.greet(); // calls the inherited greet function
    console.log('Hello');
  }
}
let igi = new Staff('Igi', 39);
igi.greet(); // will log twice.

Static methods

The static keyword defines a static method for a class.

class Helper {
  static logSomething(word){
    console.log(`${word} something`)  ;
  }
}
Helper.logSomething('hey'); // should work

Encapsulation

Encapsulation includes the idea that the data of an object should not be directly exposed. Instead, callers that want to achieve a given result are coaxed into proper usage by invoking methods (rather than accessing the data directly).

Getters and setters

class Person {
  constructor(name){
    this._name = name;
  }
  
  get name(){
    return this._name.toUpperCase();
  }
  set name(value){
    if(value.length > 2){
      this._name = value;
      return;
    }
    console.log("nah");
  }
}

let person = new Person('Igi');
person._name; // returns the name
person.name; // returns the name in upper case
person.name = "Hello world"; // sets the name
person._name = "Hello world"; // also sets the name

Subclassing built-in objects

Built-in objects can be extended to introduce new functions or properties.

class ConvArray extends Array{
  convert(){
    // perhaps loop the array via this.forEach
  }
}
console.log((new ConvArray()).convert());

Metaprogramming

Meta programming is programming where the operation targets the behavior of the program itself. In other words, it means that you're able to change (parts of) the behavior of the underyling language. The goal of meta programming is to leverage the language’s own intrinsic capabilities to make the rest of your code more descriptive, expressive, and/or flexible.

Symbol()

The data type "symbol" is a primitive data type having the quality that values of this type can be used to make object properties that are anonymous. This data type is used as the key for an object property when the property is intended to be private, for the internal use of a class or an object type.

A value having the data type "symbol" can be referred to as a "symbol value." In the JavaScript run-time environment, a symbol value is created by invoking the function Symbol(), which dynamically produces an anonymous, unique value. The only sensible usage is to store the symbol and then use the stored value to create an object property. A symbol value may be used as an identifier for object properties; this is the data type's only purpose.

Symbol.for('age') == Symbol.for('age');
Symbol('age') != Symbol('age');

let symb = Symbol.fod('age');
let person = {
  name: 'Max',
  age: 30
}
person[smyb] = 27;
console.log(person.age); points to the public object key
console.log(person[symb]); // points to the symbol

Well-known symbols

There are some built-in symbols you may utilize to overwrite default behaviors of JavaScript. Visit Well known symbols to learn more.

class Person{}
let person = new Person();
console.log(person); // logs [object Object]
Person.prototype[Symbol.toStringTag] = 'Person';
console.log(person); // logs [object Person]

Iterators & Generators

Processing each of the items in a collection is a very common operation. JavaScript provides a number of ways of iterating over a collection, from simple for loops to map() and filter(). Iterators and Generators bring the concept of iteration directly into the core language and provide a mechanism for customizing the behavior of for...of loops.

Iterators

In computer programming, an iterator is an object that enables a programmer to traverse a container, particularly lists. Iterators probably sound more complex than they are. Basically an iterator has a function – next() – which allows you to output values step by step. Calling next prints the current state (done => false or true, depending on the amount of values left) and the current value.

let array = 123;
typeof array[Symbol.iterator] // The iterator is a function, accessible via a symbol
let it = array[Symbol.iterator]();
console.log(it.next()); // returns { done: false, value: 1 }
// console.log 3 more times to change done to true

Creating a custom, iterable object

Objects can implement iterators.

let person = {
  name: 'Igi',
  hobbies: ['Watching', 'Learning', 'Playing']
  [Symbol.iterator]: function() {
    let i = 0;
    let hobbies = this.hobbies;
    return {
      next: function(){
        let value = hobbies[i];
        i++;
        return {
          done: i > hobbies.length ? true : false,
          value: value
        }
      }
    }
  }
}
for(let hobby of person){
  console.log(hobby);
} // logs 'Watching', 'Learning', 'Playing'

Generators

A function which doesn't necessarily run to the end when executed. Allows us to create logic contained in a function to yield multiple times of which the iterator can iterate over. A generator is created by adding an asterisk in front of the function name.

function *select(){
  yield 'hello world';
  yield 'hello me';
}
let it = select();
console.log(it.next()); // logs { done: false, value: 'hello world' }
// run next two more times to reach the end, with done being set to true.

Throwing and returning iterators

function *gen(end){
  for(let i = 0; i < end; i++){
    try {
      yield i;
    } catch (e){
      console.log(e);
    }
  }
}
it.next(); // returns 0
// { done: false, value: 0}
it.throw('An error occured'); // Throws an exception and gets caught. Error is logged in the console.
it.return('Something else'); // Overrides returned argument
// { done: false, value: 'Something else'}

Promises

A Promise is an object representing the eventual completion or failure of an asynchronous operation. A promise may be created using its constructor. However, most people are consumers of already-created promises returned from functions. Essentially, a promise is a returned object to which you attach callbacks, instead of passing callbacks into a function.

let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Done!') // a function that accepts an argument that gets resolved, i.e. returned
    reject('Failed!'); // rejects the promise and returns the passed argument
  }, 1500);
});

promise.then((response) => {
  console.log(response); // logs Done!
}, (error) => {
  console.log(error); // if we get here, it logs Failed! This only occurs for the first then execution 
}).then(response){
  // do something else via chaining
}.catch((error) => {
  // we can also catch errors via a chain golf, this will always run when an error happens
  console.log(error);
});

Multiple promises

Promise.all([promise1, promise2]).then((response) => {
  // When all promises are resolved, we get here
  // Response will be an array of the resolved values, e.g. ['tpby', 'smith']
  console.log(response);
}).catch((error) => {
  console.log(error);
});
Promise.race([promise1, promise2]).then((response) => {}); // only returns the first one!

Merging objects and changing prototypes

class Ob1 {
  constructor(){
    this.a = 1;
  }
}
class Ob2 {
  constructor(){
    this.a = 1;
  }
}
let ob1 = new Ob1();
let ob2 = new Ob2();
Object.assign(ob1, ob2); // ob1 will be an instance of Ob1

let ob3 = Object.assign({}, ob1, ob2); // returns a new object instance

Object.setPrototypeOf(ob1, ob2); // sets the prototype of ob1 to ob2

Useful Built-in Objects

Math, String, Number

Array tricks

Array(5) // returns an array with 5 undefined spaces
Array.of(5,3,2) // returns [5,3,2]
array.from(array, val => val * 2) // multiples all values of array by 2 and returns a new array
array.fill(100) // replaces all values of the array with 100, multiple values will map to array values
array.find(val => val >= 12) // returns all values that are greater or equal to 12
array.copyWithin(1, 2) // copies a value in an array. Copies the specified value to the specified index
array.entries() // returns an iterator (use with .next, or for let statements)

Maps

The Map object holds key-value pairs. Any value (both objects and primitive values) may be used as either a key or a value. Instead of using objects for key-value pairs, maps are available in ES6 to provide helpful functions such as set, delete, get and clear.

let cardOne = {
  name: 'test 1'
}

let cardTwo = {
  name: 'test 2'
}

deck.set('ab', cardOne);
deck.set('bc', cardTwo);
deck.get('ab'); // returns the cardOne object
deck.delete('ab');
deck.clear();

WeakMap

Holds weak references to entries in the map. Optimised for garbage collection. Not enumerable.

Sets

The Set object lets you store unique values of any type, whether primitive values or object references.

let set = new Set([1,1,1])

for (element of set){
  console.log(element);
} // only returns 1!

WeakSet

The WeakSet object lets you store weakly held objects in a collection.

Reflect API

The Reflect API could be described as a collection or “central place” which houses all kind of object and function related functions (for creation, property management etc.). Some of the functionalities added on the Reflect object where available before on the Object object. But the goal for the future is, to have on central place to store all those methods – the Reflect Object/ API. Therefore, the Reflect API provides useful methods to create, manipulate and query objects and functions in your JavaScript project.

Proxy

The Proxy API allows you to wrap objects, functions, whatever and trap / handle incoming property accessing, function calls etc. You may think of Proxies as filter or barrier which has to be passed and which may interrupt access on the wrapped element. For example you might wrap a Proxy around an object and set up a trap (that’s what these functions are called) to be triggered whenever something (the source code) tries to access a property of the wrapped object. The Proxy can then interrupt this access and maybe deny it, return another value, run some calculation – whatever you want.

class Person {
 constructor(name, age) {
 this.name = name;
 this.age = age;
 }
}
let person = new Person('Max', 27);
let proxy = new Proxy(person, {
 // Setup traps here
 get: function(target, property, receiver) {
 return 'Something else';
 }
});

function log(message) {
 console.log('Log entry created: ' + message);
}
let proxy = new Proxy(log, {
 apply: function(target, thisArg, argumentsList) {
 if (argumentsList[0].length < 20) {
 return Reflect.apply(target, thisArg, argumentsList);
 }
 return false;
 }
});
proxy('Hello!');
proxy('Hello, this is a very long message!');
`` In this example, the second function call would fail since the message is too long.

Most of the notes are derived from DoFactory's design patterns guides

Simple / modular design patterns

In software engineering, the module pattern is a design pattern used to implement the concept of software modules, defined by modular programming, in a programming language with incomplete direct support for the concept.

Object Literal Pattern

An object literal is perhaps the simplest way to encapsulate related code. It doesn't offer any privacy for properties or methods, but it's useful for eliminating anonymous functions from your code, centralizing configuration options, and easing the path to reuse and refactoring.

// Using an object literal for a jQuery feature
var myFeature = {
    init: function( settings ) {
        myFeature.config = {
            items: $( "#myFeature li" ),
            container: $( "<div class='container'></div>" ),
            urlBase: "/foo.php?item="
        };
 
        // Allow overriding the default config
        $.extend( myFeature.config, settings );
 
        myFeature.setup();
    },
 
    setup: function() {
        myFeature.config.items
            .each( myFeature.createContainer )
            .click( myFeature.showItem );
    },
 
    createContainer: function() {
        var item = $( this );
        var container = myFeature.config.container
            .clone()
            .appendTo( item );
        item.data( "container", container );
    },
 
    buildUrl: function() {
        return myFeature.config.urlBase + myFeature.currentItem.attr( "id" );
    },
 
    showItem: function() {
        myFeature.currentItem = $( this );
        myFeature.getContent( myFeature.showContent );
    },
 
    getContent: function( callback ) {
        var url = myFeature.buildUrl();
        myFeature.currentItem.data( "container" ).load( url, callback );
    },
 
    showContent: function() {
        myFeature.currentItem.data( "container" ).show();
        myFeature.hideContent();
    },
 
    hideContent: function() {
        myFeature.currentItem.siblings().each(function() {
            $( this ).data( "container" ).hide();
        });
    }
};
 
$( document ).ready( myFeature.init );

Module Pattern

The module pattern overcomes some of the limitations of the object literal, offering privacy for variables and functions while exposing a public API if desired.

// Using the module pattern for a jQuery feature
$( document ).ready(function() {
    var feature = (function() {
        var items = $( "#myFeature li" );
        var container = $( "<div class='container'></div>" );
        var currentItem = null;
        var urlBase = "/foo.php?item=";
 
        var createContainer = function() {
            var item = $( this );
            var _container = container.clone().appendTo( item );
            item.data( "container", _container );
        };
 
        var buildUrl = function() {
            return urlBase + currentItem.attr( "id" );
        };
 
        var showItem = function() {
            currentItem = $( this );
            getContent( showContent );
        };
 
        var showItemByIndex = function( idx ) {
            $.proxy( showItem, items.get( idx ) );
        };
 
        var getContent = function( callback ) {
            currentItem.data( "container" ).load( buildUrl(), callback );
        };
 
        var showContent = function() {
            currentItem.data( "container" ).show();
            hideContent();
        };
 
        var hideContent = function() {
            currentItem.siblings().each(function() {
                $( this ).data( "container" ).hide();
            });
        };
 
        items.each( createContainer ).click( showItem );
 
        return {
            showItemByIndex: showItemByIndex
        };
    })();
 
    feature.showItemByIndex( 0 );
});

Controlling the global scope

(function(window, document, $){
  // Code to set up feature module
  window.feature = window.feature || feature; // expose the module to the global scope
}(window, document, jQuery))

Creational Design Patterns

In software engineering, creational design patterns are design patterns that deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. The basic form of object creation could result in design problems or in added complexity to the design.

A list of common creational Patterns

  • Abstract Factory - Creates an instance of several families of classes
  • Builder - Separates object construction from its representation
  • Factory Method - Creates an instance of several derived classes
  • Prototype - A fully initialized instance to be copied or cloned
  • Singleton - A class of which only a single instance can exist

Singleton design pattern

The Singleton Pattern limits the number of instances of a particular object to just one. This single instance is called the singleton.

Singletons are useful in situations where system-wide actions need to be coordinated from a single central place. An example is a database connection pool. The pool manages the creation, destruction, and lifetime of all database connections for the entire application ensuring that no connections are 'lost'.

Singletons reduce the need for global variables which is particularly important in JavaScript because it limits namespace pollution and associated risk of name collisions. The Module pattern (see our JavaScript + jQuery Design Pattern Framework) is JavaScript's manifestation of the Singleton pattern.

var Singleton = (function () {
  var instance;
 
  function createInstance() {
    var object = new Object("I am the instance");
    return object;
  }
 
  return {
    getInstance: function () {
      if (!instance) {
        instance = createInstance();
      }
      return instance;
    }
  };
})();
 
function run() {
  var instance1 = Singleton.getInstance();
  var instance2 = Singleton.getInstance();
  alert("Same instance? " + (instance1 === instance2));  
}

Factory design pattern

A Factory Method creates new objects as instructed by the client. One way to create objects in JavaScript is by invoking a constructor function with the new operator. There are situations however, where the client does not, or should not, know which one of several candidate objects to instantiate. The Factory Method allows the client to delegate object creation while still retaining control over which type to instantiate.

The key objective of the Factory Method is extensibility. Factory Methods are frequently used in applications that manage, maintain, or manipulate collections of objects that are different but at the same time have many characteristics (i.e. methods and properties) in common. An example would be a collection of documents with a mix of Xml documents, Pdf documents, and Rtf documents.

function Factory() {
  this.createEmployee = function (type) {
    var employee;
 
    if (type === "fulltime") {
      employee = new FullTime();
    } else if (type === "parttime") {
      employee = new PartTime();
    } else if (type === "temporary") {
      employee = new Temporary();
    } else if (type === "contractor") {
      employee = new Contractor();
    }
 
    employee.type = type;
 
    employee.say = function () {
      log.add(this.type + ": rate " + this.hourly + "/hour");
    }
 
    return employee;
  }
}
 
var FullTime = function () {
  this.hourly = "$12";
};
 
var PartTime = function () {
  this.hourly = "$11";
};
 
var Temporary = function () {
  this.hourly = "$10";
};
 
var Contractor = function () {
  this.hourly = "$15";
};
 
// log helper
var log = (function () {
  var log = "";
 
  return {
    add: function (msg) { log += msg + "\n"; },
    show: function () { alert(log); log = ""; }
  }
})();
 
function run() {
  var employees = [];
  var factory = new Factory();
 
  employees.push(factory.createEmployee("fulltime"));
  employees.push(factory.createEmployee("parttime"));
  employees.push(factory.createEmployee("temporary"));
  employees.push(factory.createEmployee("contractor"));
  
  for (var i = 0, len = employees.length; i < len; i++) {
    employees[i].say();
  }
 
  log.show();
}

Abstract Factory design pattern

An Abstract Factory creates objects that are related by a common theme. In object-oriented programming a Factory is an object that creates other objects. An Abstract Factory has abstracted out a theme which is shared by the newly created objects.

Suppose we have two Abstract Factories whose task it is to create page controls, such as, buttons, textboxes, radio buttons, and listboxes. One is the Light Factory which creates controls that are white and the other the Dark Factory which creates controls that are black. Both Factories creates the same types of controls, but they differ in color, which is their common theme. This is an implementation of the Abstract Factory pattern.

Over time the Abstract Factory and Factory Method patterns have merged into a more general pattern called Factory. A Factory is simply an object that creates other objects.

You may be wondering why you would want to leave the responsibility of the construction of objects to others rather than simply calling a constructor function with the new keyword directly. The reason is that that constructor functions are limited in their control over the overall creation process and sometimes you will need to hand over control to a factory that has broader knowledge.

This includes scenarios in which the creation process involves object caching, sharing or re-using of objects, complex logic, or applications that maintain object and type counts, and objects that interact with different resources or devices. If your application needs more control over the object creation process, consider using a Factory.

Builder design pattern

Separate the construction of a complex object from its representation so that the same construction process can create different representations. An example of a library using the builder design pattern is jQuery - for $.

Prototype design pattern

The Prototype Pattern creates new objects, but rather than creating non-initialized objects it returns objects that are initialized with values it copied from a prototype - or sample - object. The Prototype pattern is also referred to as the Properties pattern.

An example of where the Prototype pattern is useful is the initialization of business objects with values that match the default values in the database. The prototype object holds the default values that are copied over into a newly created business object.

Classical languages rarely use the Prototype pattern, but JavaScript being a prototypal language uses this pattern in the construction of new objects and their prototypes.

Syntax Parser

A program that reads your code and determines what it does and if its grammar is valid. Intermediat program that translates your code into something that the computer can understand. Checks for valid syntax!

Lexical Environment

Where something sits physically in the code you write.

Execution Context

A wrapper to help manage the code that is running. For each function being run, there is a new execution context.

Name/value pairs

A name which maps to a unique value.

Object

Collection of name value pairs. Anything that has a memory reference... Even functions can be objects.

Object literals

A JavaScript object literal is a comma-separated list of name-value pairs wrapped in curly braces.

Global Execution Context

A Global Object is setup for a run-time: this. (window = this)

  1. Creation Phase - Sets up memory space for functions and variables via hoisting. References are assigned to undefined
  2. Execution Phase - Runs your code, line by line.

Hoisting

To move to the top. Never rely on hoisting, call after hoisted vars and functions have been setup! Always be explicit. Named functions are hoisted function something(){} so you can call it anywhere. Variables are hoisted and no values are set.

Undefined

A special javascript value to denote that something hasn't been set yet! Not to be mistaken with null. When coerced as a boolean, returns false.

Null

Represents a lack of existence. Set a variable equal to nothing. When coerced as a boolean, returns false.

Empty strings ''

When coerced as a boolean, returns false.

Synchronous

Ordered execution, one line at a time. JavaScript is single-threaded, the browser can be multi-threaded (other parts of the browser can run simultaneously).

Asynchronous

More than one at a time.

Invocation

Running a function. In JS using a parenthesis ().

Variable enviroment

Where the variables live and how they relate to each other.

Scope

Where a variable is avaialbe in your code? Execution contexts can reference outer environments. For every function being called inside a function, a new new stack is added on top of the scope chain.

Scope Chaining

If a variable or function can't be found in the current execution context, it will look at the outer environment (move down the scope chain).

Let

Allows the JS engine to use block scoping by declaring a variable to be lexically available inside the block. Not hoisted. Var is scoped to the nearest function block and let is scoped to the nearest enclosing block, which can be smaller than a function block. Both are global if outside any block.

Event queue

Sequenece of events

Setting up events

Events only get executed after all execution contexts have executed.

Dynamic typing

No intended type of variable to be stored. The JS Engine figures out the types of data being used during execution.

Static typing

Specifying a variable's value type.

Primitive types

A type of data that represents a single value. Somethign that isn't an object string, number (floating value, something with decimals), boolean, null, undefined, symbol (used in es6)

Symbols

Symbols are all about Reflection within implementation - you sprinkle them on your existing classes and objects to change the behaviour. When used as object keys, symbols provide a hidden layer to Objects - not iterable over, not fetched using the already existing Reflection tools and guaranteed not to conflict with other properties in the object. They are not private however!

Operators

A special function that is syntactically written differently. Takes two params and returns one result. a + b = c. Written using infix notation.

function +(a,b){
  return add a and b
}
so... +(3,4) is better read in its infix form: 3 + 4

Infix notation

Operator in the middle. in

Associativity

Order of operator functions. LTR or RTL. a + (b + c) - b + c first then add to a. Refer to the Operator Precedence chart

Coercion

Converting or forcing a value from one type to another. It happens in JS because it is dynamically typed.

3 < 2 < 1 // returns true
(3 < 2) < 1
(false) < 1
0 < 1 // false is coerced

NaN

Something that can't be converted to number.

Number(undefined) // returns NaN
Number(null) // null is coerced to 0, returns 0

Equality (==)

Coerces and compares

String Inequality (===)

Compare values and types

Default Values

For the logical OR operator ||, return the first value that when coerced returns true!

true || false // returns true
'girl' || false // returns girl
window.libraryName = window.libraryName || {}

Dot operator vs computed property access []

Use [] for string based object property lookups/setting.

Namespace

A container for variables and functions

First class functions

Your language is treating functions as first class objects. It means that functions are objects, with a type and a behaviour. They can be dynamically built, passed around as any other object, and the fact that they can be called is part of their interface. It means that function actually inherits from Object.

function(function(){}); // passing functions to functions!

Functions

Properties and methods can be attached to functions. Function object has some properties: name and code (block). When an execution context is setup, there are a few things that are setup as well:

  1. Variable Environment (the current block)
  2. this reference
  3. Outer environment
  4. arguments

Invocable

Run a function object's code. ()

Anonymous Function

A function that doesn't have the name property specified. (){};

Expression

A unit of code that results in a value. Doesn't have to save a variable.

var a = b; // returns b
sometFunction(); // invoked functions return something, even if it's null
var anonymousGreet = function(){
} // this is an expression because anonymousGreet returns a function!
// anonymousGreet gets hoisted, so calling it before the variable assignment will cause the code to break!

Statement

Statements only do work... don't return values.

if(){
}  // the if part is a statement because it can only execute and not return!

Function statement vs function expression

function greet(){
   // some code
}  // function statements are hoisted. This is a statement because it doesn't return anything until it gets executed.

var varFunction = function(){} // only the var is hoisted
varFunction(); // calls the function

By value

Become the same by copying the value to two separate spots in memory. a = b

By reference

Usually applies to objects. One spot in memory is being referenced by different variables.

a = {
  something: 'somethign'
}

b = a;
b.hello = 'test' // mutate
console.log(a.hello) // outputs test
b = { new: 'someting' } // Equals operator sets up new memory space!

Mutate

To change something. Objects can be mutated and all references to it will see the same changes!

Immutable

Something that can't be changed.

ES5 this

This always refers to the current execution context / scope. When used in objects, this refers to the object!

var d = {
  name: '',
  log: function(){
    self = this;
    this.name = 'updated!'; // works
    console.log(this) // logs d
    function innerfunction(name){
      // calling this here will refer to the global object!!!
      self.name = name;
    }
    innerfunction('hello') // works!
  }
}

ES5 arguments

The old way to pass a variable amount of arguments.

function someFunctionWithArguments(name, age, location){
  name = name || 'default name aye';
  console.log(args[1]); // logs age
}

ES6 Spread parameter

This is the preferred way to pass a variable amount of arguments.

function someFunction(...other){
  return other[3]; // returns the second argument...
}

Function overloading

Having a function of the same name, with different argument counts. There are other patterns in javascript. Just don't!

Automatic semi-colon insertion

return {

} // always put the opening bracket next to the return statement. else, the syntax parser will think that it's an empty return!

Immediately invoked function expressions (IIFE)

Can be used to create a safe scope for scripts!

(
  // function expression
  function(global, name){ 
    global.someGlobalObject = {}; // we pass window so we can use it!
    console.log (name)
  }(window, 'some name') // immediately invoke!
);
// immediately logs some name

Closures

A closure is the combination of a function and the lexical environment within which that function was declared. An execution context that has closed in its variables.

function greet(som){
  return function(name){
    console.log(som, name); // when we get here, som will be available from the lexical context of things.
  }
}

var greetHi = greet('hi'); // a returned function gets stored in memory, along with its lexically scoped variables.
greetHi('Igi');

function buildIt(){
  var arr = [];
  for (var i = 0; i < 3; i++){
    arr.push((function(j){
      return function(){
        console.log(j)
      }
    }(i)))
  }
}

var n = buildIt();
n[0](); // logs 1
n[1](); // logs 2

Function Factories

Create functions from other functions. Used a lot in frameworks. Eg. greetEnglish('Igi') calls a more complex function, e.g.

function greet(language){
  return function(name){
    if(language=='en'){
      return `Hello ${name}. Nice to meet you!`
    } else {
      return `Hola ${name}.`
    }
  };
}
var greetEnglish = greet('en');
greetEnglish('Igi');

Garbage Collection

Clearing up unused memory allocations.

Call

  var person = {
    name: 'Igi',
    getCuteName: function(){
      this.name + 'chan';
    }
  }
  var testMe = function(greeting){
    alert(`${greeting} ${this.name}`);
  }
  var person2 = {
    name: 'Cindy'
  }
  
  console.log(person.getCuteName.apply(person2)); // logs Cindychan
  
  testMe.call(person, 'Hello') // set the execution context to person so that when this is called in the function, it points to the person.
  // Alerts Hello Igi

Apply

The same as call, but extra vars are passed as an array. Useful for mathemtical uses that depends on indexes/arrays.

testMe.apply(person, ['Hello']);

Bind / Function currying

Creating a copy of a function with the option of providing preset parameters. Does not execute like call or apply. Useful in mathematical situations or when copying functions.

var multiply = function(a, b){
  return a * b;
}
var multiplyByTwo = multiply.bind(this, 2);
multiplyByTwo(4); // b gets set as 4. 2 * 4 = 8

Functional programming

A paradigm. Using first class functions to segment code in cleaner and tighter ways. Treats computation as the evaluation of mathematical functions and avoids mutating data. Popular (and a bit old) examples are lodash and underscore.js.

// underscore
_.map([1,2,3,4,5], multiply(20))
_.map([1,2,3,4,5], multiply(5))
_.map([1,2,3,4,5], multiply(8))

function multiply(multiplier){
  return function(item, multiplier){
    return item * multiplier;
  }.bind(this, multiplier);
}

Inheritance

One object gets access to the props and methods of another object.

Classical vs prototypal inheritance

JavaScript is a class-free, object-oriented language, and as such, it uses prototypal inheritance instead of classical inheritance. This can be puzzling to programmers trained in conventional object-oriented languages like C++ and Java.

Class Inheritance

A class is like a blueprint — a description of the object to be created. Classes inherit from classes and create subclass relationships: hierarchical class taxonomies.

Prototypal inheritance

Prototypal Inheritance: A prototype is a working object instance. Objects inherit directly from other objects.

Prototype

All objects, including functions have a property called proto. Prototypes have their own props too.

// Don't do this ever! This is bad.
igi.__proto__ = { someParam: 'someValue' }

Prototype chain

A sequence of objects connected via the proto property. Objects that inherit from other objects.

Reflection

Reflection means examining the structure of a program and its data. When examining the properties of an object, JavaScript’s default is to include inherited properties. Thus, you have to make sure that a method does what you want and that will usually be to ignore inherited properties. One clue is to look for the presence or absence of “own” in the method name. For example, ES5’s Object.getOwnPropertyNames() returns all direct (non-inherited) property descriptors of an object.

Function constructors

The Function constructor creates a new Function object. In JavaScript every function is actually a Function object. Ths this variable points a new empty object, and that object is returned from the function automatically. New is required to create the prototype of the function object.

var Vehicle = function Vehicle(params) {
  this.make = params.make || 'Honda';
  this.someFunction = function(){} // this is bad, each var assignment will create a memory reference!
}

// This is better for adding extra props and methods to the prototype
Vehicle.prototype.tow = function(){
  return this.make + ' was towed';
}

var vehicle = new Vehicle();
vehicle.tow(); // logs Honda was towed

Side tip: Don't add functional extra methods or props to built-in function constructors unless you really have to. E.g.

String.prototype.someFunction // might as well create a separte class

Safely iterating objects

Don't use for var in syntax. Use the index for instead. for var x = 0; x < s.length; ++

Pure prototypal inheritance

Browsers have Object.create available to create objects with prototypes. Most developers prefer it over functional constructors as it doesn't mimic other languages.

var person = { name: '', someFunction: function(){} }
var igi = Object.create(person);
igi.name = 'Igi';
igi.anExclusiveFunction = function(){}
igi.someFunction();

Polyfill

Code that adds a feature which an engine may lack.

Instanceof

The instanceof operator returns true if an object is created by a given constructor. To do this, it checks if constructor is in a prototypes chain of object.

e instanceof Person

Typeof

The typeof operator checks if value belong to one of six basic types: "number", "string", "boolean", "object", "function" or "undefined". Where the string "object" belong all objects (except functions, which are objects, but have its own value in typeof operator), and also "null" value and arrays (for "null" it's a bug, but this bug is so old, so it's become a standard). It doesn't rely on constructors and can be used even if value is undefined. But it's doesn't give any details about objects. So if you needed it, go to instanceof.

Side tip: Use Object.prototype.toString.call([]) to check if something is an array. Using typeof [] returns an object.

ECMAScript 5's strict mode is a way to opt in to a restricted variant of JavaScript. Strict mode isn't just a subset: it intentionally has different semantics from normal code. Browsers not supporting strict mode will run strict mode code with different behavior from browsers that do, so don't rely on strict mode without feature-testing for support for the relevant aspects of strict mode. Strict mode code and non-strict mode code can coexist, so scripts can opt into strict mode incrementally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment