DEV Community

Cover image for Taking your JS Skills to the Next Level { with The Most Powerful JS_Tutorial }
bricourse
bricourse

Posted on

Taking your JS Skills to the Next Level { with The Most Powerful JS_Tutorial }

JavaScript (JS) is a dynamic interpreted language that powers the web. It is widely used in browsers (where JS scripts are interpreted by JavaScript engines like Chrome's V8) and increasingly on servers (on a Node.js runtime environment).

JS is a prototype-based scripting language with first-class functions and dynamic typing. Because of its super flexibility, JS supports multiple styles of programming including imperative, object-oriented and functional.

Here's what all those big words above mean:

  • Interpreted Language: a language (eg. JS, Python) in which most of its implementations execute instructions directly, without previously compiling a program into machine-language instructions like compiled languages do (eg. C++)
  • JavaScript Engine: a virtual machine that interprets and executes JS
  • Prototype-Based: unlike classical OOP with classes and objects, in JS, objects are cloned from other objects, and all objects have prototypes (kinda like the template they inherit from)
  • First-Class Functions: JS supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures
  • Dynamically Typed: The "type" of all variables is only interpreted at run-time unlike statically typed languages where all variables have a type at compile-time
  • Imperative Programming: Statement based programming
  • Object-Oriented Programming: Object based programming
  • Functional Programming: Function based programming

Quick Access Links

  1. Basics
    1. Primitives
    2. Operators
  2. More Basic Syntax
    1. Variables
    2. Arrays
    3. Logic and Control Structures
  3. Objects and Functions
    1. Objects
    2. Functions
    3. Bind, Call and Apply
  4. Function Execution, Variable Scope, Closures & Callbacks
    1. Hoisting
    2. Scope Chain
    3. Closures
    4. Callbacks
  5. Object-Oriented JS and Prototypal Inheritance
    1. Constructors
    2. Prototypes
    3. Prototypal Inheritance
    4. Built-In Constructors
  6. Bugs and Error Handling
  7. New ES6 stuff

Get the book: Javascript Challenges


1. Basics

Everything in JS is either an object or a primitive.


// This is a single line comment,
/* and this is a 
multiline comment */

// Semicolons (;) to terminate lines are optional
// However, the JS engine will (usually) automatically insert semicolons upon seeing '\n'
// This can cause some weird behaviour so ALWAYS use semicolons
doStuff();
Enter fullscreen mode Exit fullscreen mode

i. Primitives: Number, String, Boolean (and some special ones)

// JavaScript has one number type (which is a 64-bit IEEE 754 double).
// Doubles have a 52-bit mantissa, which is enough to store integers
//    up to about 9✕10¹⁵ precisely.
3; // = 3
1.5; // = 1.5

// Some basic arithmetic works as you'd expect.
1 + 1; // = 2
0.1 + 0.2; // = 0.30000000000000004 (funky floating point arithmetic--be careful!)
8 - 1; // = 7
10 * 2; // = 20
10 ** 2; // =100 (10 raised to the power 2) same as Math.pow(10, 2)
35 / 5; // = 7

// Including uneven division.
5 / 2; // = 2.5

// Bitwise operations also work; when you perform a bitwise operation your float
// is converted to a signed int *up to* 32 bits.
1 << 2; // = 4

// Precedence is enforced with parentheses.
(1 + 3) * 2; // = 8

// There are special primitive values:
Infinity; // result of e.g. 1/0
-Infinity; // result of e.g. -1/0
NaN; // result of e.g. 0/0
undefined; // never use this yourself. This is the default value for "not assigned"
null; // use this instead. This is the programmer setting a var to "not assigned"

// There's also a boolean type.
true;
false;

// Strings are created with single quotes (') or double quotes (").
'abc';
"Hello, world";

// You can access characters in a string with `charAt`
"This is a string".charAt(0);  // = 'T'

// ...or use `substring` to get larger pieces.
"Hello world".substring(0, 5); // = "Hello"
"Hello world".slice(0, 5); // does the same thing
"Hello world".substr(0, 5); // yet again

// `length` is a property, so don't use ().
"Hello".length; // = 5

// Searching strings
"Mary had a little lamb".search("had"); // returns 5
"Mary had a little lamb".indexOf("zebra"); // returns -1
"Mary had a little lamb".includes("had"); //returns true (ES7). includes() will return true if the parameter provided is in the string, and false otherwise.

// String to a character array
"one two three four".split(" "); // ['one', 'two', 'three', 'four']

// String replace
"happy birthday henry!".replace("h", "H"); // "Happy birthday Henry!"

// ES6 also introduces Symbol as a new primitive type
// But I'll add that on here once I actually figure out what it is
Enter fullscreen mode Exit fullscreen mode

ii. Operators aka weirdly written functions

// Operators have both a precedence (order of importance, like * before +) 
// and an associativity (order of evaluation, like left-to-right)
// A table of operators can be found here 
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence)

// Negation uses the ! symbol
!true; // = false
!false; // = true

// There's shorthand operators for performing math operations on variables:
someVar += 5; // equivalent to someVar = someVar + 5;
someVar *= 10; // someVar = someVar * 10;

// and an even-shorter-hand  operators for adding or subtracting 1
someVar++; 
someVar--; 

// Strings are concatenated with +
"Hello " + "world!"; // = "Hello world!"

// Comparison Operators
1 < 10; // = true
1 > 10; // = false
2 <= 2; // = true
2 >= 2; // = true

// and are compared with < and >
"a" < "b"; // = true

// Strict Equality is ===
// Strict meaning both type AND value are the same
1 === 1; // = true
2 === 1; // = false

// Strict Inequality is !==
1 !== 1; // = false
2 !== 1; // = true

// == allows for type coercion (conversion) and only checks if the values are equal after coercion
"5" == 5; // = true
null == undefined; // = true

// ...which can result in some weird behaviour...so use === whenever possible
13 + !0; // 14
"13" + !0; // '13true'

// false, null, undefined, NaN, 0 and "" are falsy; everything else is truthy.
// Note that 0 is falsy and "0" is truthy, even though 0 == "0".

// We can use this to our advantage when checking for existence
if (x) { //doSomething };

// Or to set default values
x = x || "default" 
// if x exists, do nothing and short-circuit, else set x to a default

// but a problem arises if x = 0. It exists, but will coerce to false
// be wary of this

// figuring out types of literals and vars
typeof "Hello"; // = "string"
typeof 42; // = "number"
typeof undefined // = "undefined"
typeof null // = 'object' THIS IS A JS BUG!

// figuring out if an object is an instance of another object
// checks all the way down the prototype chain
var x = {}
x instanceof Object // = true
x instanceof Function // = false

Enter fullscreen mode Exit fullscreen mode

2. More Basic Syntax

i. Variables

// Variables are declared with the `var` keyword. JavaScript is dynamically
// typed, so you don't need to specify type. Assignment uses a single `=`
// character.
var someVar = 5;

// if you leave the var keyword off, you won't get an error...
someOtherVar = 10;

// ...but your variable will be created in the global scope, not in the scope
// you defined it in.

// Variables declared without being assigned to are set to undefined.
var someThirdVar; // = undefined
Enter fullscreen mode Exit fullscreen mode

ii. Arrays

// Arrays are ordered lists of values, of any type.
var myArray = ["Hello", 45, true];

// Their members can be accessed using the square-brackets subscript syntax.
// Array indices start at zero.
myArray[1]; // = 45

// Arrays are mutable and of variable length (dynamically sized arrays)
myArray.push("World"); // adds to the end
myArray.length; // = 4

// Add/Modify at specific index
myArray[3] = "Hello";

//ES7 includes() can be used with arrays to check for the presence of a value. it is case sensitive
let names = ["Samuel", "Hamilton", "Eric"];
names.includes("Samuel"); //true
names.includes("samuel"); //false
names.includes("John"); //false
Enter fullscreen mode Exit fullscreen mode

iii. Logic and Control Structures

// The `if` structure works as you'd expect.
var count = 1;
if (count === 3){
    // evaluated if count is 3
} else if (count === 4){
    // evaluated if count is 4
} else {
    // evaluated if it's not either 3 or 4
}

// As does `while`.
while (true){
    // An infinite loop!
}

// Do-while loops are like while loops, except they always run at least once.
var input;
do {
    input = getInput();
} while (!isValid(input))

// The `for` loop is the same as C++ and Java:
// initialisation; continue condition; iteration.
for (var i = 0; i < 5; i++){
    // will run 5 times
}

// && is logical AND, || is logical OR
if (house.size === "big" && house.colour === "blue"){
    house.contains = "bear";
}
if (colour === "red" || colour === "blue"){
    // colour is either red or blue
}

// The `switch` statement checks for equality with `===`.
// use 'break' after each case 
// or the cases after the correct one will be executed too. 
grade = 'B';
switch (grade) {
  case 'A':
    console.log("Great job");
    break;
  case 'B':
    console.log("OK job");
    break;
  case 'C':
    console.log("You can do better");
    break;
  default:
    console.log("Oy vey");
    break;
}

Enter fullscreen mode Exit fullscreen mode

3. Objects and Functions

i. Objects

An object is simply an unordered collection of key-value pairs.

// They can be made literally:
var myObj = {key1: "Hello", key2: "World"};
// or using the Object constructor:
var myObj = new Object();

// Keys are strings, but quotes aren't required if they're a valid
// JavaScript identifier. Values can be any type including other objects.
var myObj = {myKey: "myValue", "my other key": 4};

// Objects can even contain functions (called methods)
// When functions attached to an object are called, they can access the object
// they're attached to using the `this` keyword.
var myObj = { 
  name: "Destiny's Child",
  sayMyName: function() {
    console.log(this.name);
  }
}
myObj.sayMyName(); // outputs "Destiny's Child"

// Object attributes can also be accessed using the subscript syntax,
myObj["my other key"]; // = 4

// ... or using the dot syntax, provided the key is a valid identifier.
myObj.myKey; // = "myValue"

// Objects are mutable; values can be changed and new keys added.
myObj.myThirdKey = true;

// If you try to access a value that's not yet set, you'll get undefined.
myObj.myFourthKey; // = undefined

// iterating through objects
for(var property in myObj) { // do something }

// JSON (JavaScript Object Notation) is just a special case of Object literal notation
// where the keys are strings wrapped in quotes
var json_stuff = {
  "firstName": "John",
  "lastName": "Doe",
  "Age": 25
}

// JS Object => JSON
JSON.stringify(myObj);

// JSON => JS Object
JSON.parse(json_stuff);
Enter fullscreen mode Exit fullscreen mode

ii. Functions

Functions are special kinds of objects! Functions can have their own methods and properties just like other objects, but they're rarely used in that way.

Remember, functions in JS are first-class. Meaning they can be assigned and passed just like any other variable can.

Functions are special in that they have an optional name property and a code property (which is the body of the function that actually does stuff). The function's code is executed by the invocation operator ().

// JavaScript functions are declared with the `function` keyword.
// This is a function statement
function myFunction(thing){
    return thing.toUpperCase();
}

// This is a function expression
var makeUpperCase = function() {
    return think.toUpperCase();
}

// Note that the value to be returned must start on the same line as the
// `return` keyword, otherwise you'll always return `undefined` due to
// automatic semicolon insertion. Watch out for this when using Allman style.
function myFunction()
{
    return // <- semicolon automatically inserted here
    {
        thisIsAn: 'object literal'
    }
}
myFunction(); // = undefined

// JavaScript functions are first class objects, so they can be reassigned to
// different variable names and passed to other functions as arguments - for
// example, when supplying an event handler:
function myFunction(){
    // this code will be called in 5 seconds' time
}
setTimeout(myFunction, 5000);
// Note: setTimeout isn't part of the JS language, but is provided by browsers
// and Node.js.
Enter fullscreen mode Exit fullscreen mode

Function objects don't even have to be declared with a name - you can write an anonymous function definition directly into the arguments of another.

setTimeout(function(){
    console.log("It's been 5 seconds!");
    // this code will be called in 5 seconds time
}, 5000);
Enter fullscreen mode Exit fullscreen mode

This has led to a common pattern of "immediately-executing anonymous functions", which prevent temporary variables from leaking into the global scope. The function expression is wrapped in parenthesis and then is invoked using ()

(function(){
    var temporary = 5;
})();
temporary; // raises ReferenceError
permanent; // = 10
Enter fullscreen mode Exit fullscreen mode

An important distinction: primitives Pass by Value while objects Pass by Reference

// Primitives are passed by value
var i = 2;
function double(i){  i*2;  } // another i is created with the same value in a different execution context
double(i);
console.log(i); // still 2 

// Objects (including functions) are passed by reference
var obj = { hero: "Superman" };
function bestSuperhero(obj){
  obj.hero = "Batman";
}
bestSuperhero(obj);
console.log(obj.hero); // = "Batman"
Enter fullscreen mode Exit fullscreen mode

The this keyword within methods, always refers to the object that the method is tied to. However, if the method has an inner function, its this refers to the global object. Some regard this as a bug in JS, so the good practice is to create and use a variable called self.

var c = {
    name: 'The c object',
    log: function() {
        var self = this;

        self.name = 'Updated c object';
        console.log(self);

        var setname = function(newname) {
            self.name = newname;   
        }
        setname('Updated again! The c object');
        console.log(self);
    }
}
c.log(); // outputs "Updated again! The c object"
Enter fullscreen mode Exit fullscreen mode

iii. Bind, Call and Apply

Functions that aren't tied to objects can use this and still be useful. Consider this example.

var cow = { says: "moo" };
var dog = { says: "woof" };
var pig = { says: "oink" };

function speak(times) { 
  for(i = 0; i < times; i++) {
    console.log(this.says);
  }
}
speak(4); // error because this is the global object which doesn't have a 'says' property
Enter fullscreen mode Exit fullscreen mode

To use speak, we need to use the .bind, .call or .apply methods, which are available to all functions. The first parameter of these functions is the object which becomes this within the function.

// bind creates a copy of the function it's being called on
var cowSpeak = speak.bind(cow);
cowSpeak(4); // outputs "moo moo"

// call directly executes the function with the first parameter being 'this'
// and all the other parameters being the function's parameters
speak.call(dog, 3); // outputs "woof woof woof"

// apply directly executes the function with the first parameter being 'this'
// and all the other function parameters being passed in as an array
speak.apply(pig, [1]); // outputs "oink"
Enter fullscreen mode Exit fullscreen mode

The call and apply methods allow us to do something called Function Borrowing.

var darthVader = { 
  son: "Luke",
  saying: function(){
    console.log(this.son + ", I am your father");
  }
};
var luke = { son: "Ben" };

darthVader.saying.call(luke);
// borrowing Darth Vader's saying
// outputs "Ben, I am your father"
Enter fullscreen mode Exit fullscreen mode

The bind method allows us to do Function Currying.

// Creating a copy of a function with preset parameters
function multiply(a,b){ return a*b }

// first parameter can be 'this' or null or anything--doesn't matter since 'this' is not used
// the second parameter (a) is permanently set to 2
var double = multiply.bind(this, 2);
double(16); // outputs 32

Enter fullscreen mode Exit fullscreen mode

4. Function Execution, Variable Scope, Closures & Callbacks

A few important concepts:

  • Global means not inside a function. The global object is 'window' in browsers.
  • The Lexical Environment is where something sits physically in the code
  • 'this' is a reference to the object that the currently running method is tied to (by default it's tied to the global object)
  • The Execution Context consists of the environment (state of variables) of the function currently being evaluated. It also includes 'this' and a reference to the outer environment (aka what object is sitting outside this function lexically)
  • The Execution Stack or Call Stack is the "stack" of execution contexts with the global execution context being the bottommost. When the program flow enters a function, a new execution context is popped onto the call stack, and when the function returns, it is popped off.

i. Hoisting

Before actually executing any of the code, the JS engine first looks at all the variable declarations and function statements and sets aside some memory space for them effectively moving them to the top of the code. This is known as hoisting.

// Variable example

function a(){
  console.log(x);
  var x = 2;
  console.log(x);
}
a(); // outputs 'undefined' then 2

// Function a is essentially equivalent to:
function a(){
  var x; // the var declaration is hoisted up to the top and is set to undefined
  console.log(x); // outputs undefined
  x = 2; // the variable assignment stays. It is NOT hoisted.
  console.log(x); // outputs 2
}

// Function example

a(); // will run properly
b(); // will fail with TypeError because b isn't assigned until line 4
function a() { }
var b = function() { }

The above is equivalent to:
function a() {} // the function statement is hoisted
var b;
a();
b(); // = undefined(); invoking undefined raises an error
b = function() {}
Enter fullscreen mode Exit fullscreen mode

JS is always synchronous (executing code 1 line at a time and in-order) and single-threaded (only 1 command at a time). However, jQuery, event handlers and AJAX calls make use of callbacks which appear to run asynchronously. AJAX calls are delegated off to a different part of the browser (outside the JS engine) which is why they are run asynchronously. When the call returns, or if there's a user click, then these events fill up the Event Queue. The JS Engine only handles the Event Queue when the Execution Stack is empty.

ii. Scope Chain

To find a variable when functions are running, JS looks further than just the variable environment of the currently executing context, it also looks at the outer environment (the environment to which this function is lexically attached). This process continues looking all the way down to the global environment in a process known as the _scope chain.

function b() {
    console.log(myVar);
}

function a() {
    var myVar = 2;
    b();
}

var myVar = 1;
a();

// function b does not have a myVar variable so it looks to its outer environment
// here, b is lexically attached to the global object, and myVar exists in the global environment
// therefore, it logs '1'


// JavaScript has function scope; functions get their own scope but other blocks
// do not.
if (true){
    var i = 5;
}
i; // = 5 - not undefined as you'd expect in a block-scoped language
Enter fullscreen mode Exit fullscreen mode

iii. Closures

One of JS's most powerful features is closures. Whenever a function is nested within another function, the inner function has access to all the outer function's variables even after the outer function exits.
After the outer function exits, it is popped off the Execution Stack, however if any of its variables are referenced in the inner function, then those variables are "closed into" the inner function's Execution Context and are accessible by the inner function.

// Example 1

function sayHelloInFiveSeconds(name){
    var prompt = "Hello, " + name + "!";
    // Inner functions are put in the local scope by default, as if they were
    // declared with `var`.
    function inner(){
        alert(prompt);
    }
    setTimeout(inner, 5000);
    // setTimeout is asynchronous, so the sayHelloInFiveSeconds function will
    // exit immediately, and setTimeout will call inner afterwards. However,
    // because inner is "closed over" sayHelloInFiveSeconds, inner still has
    // access to the `prompt` variable when it is finally called.
}
sayHelloInFiveSeconds("Adam"); // will open a popup with "Hello, Adam!" in 5s


// Example 2

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

var fs = buildFunctions();
fs[0]();
fs[1]();
fs[2]();
// all 3 of these will log '3' because after buildFunctions finishes, i = 3
// when fs[0..2] are invoked, they look for the value of i and they all see 3

// Avoid creating functions in loops. It will lead to bad behaviour.
Enter fullscreen mode Exit fullscreen mode

iv. Callbacks

Callbacks are simply functions passed as arguments to other functions to be run when the other functions are finished.

function alertWhenDone(callback){
  // do some work
  callback(); // invoke the callback right before exiting
}

alertWhenDone(function(){
  alert("I am done!"); 
});

// Callback making use of the JavaScript Timer
setTimeout(function(){
  alert("3 seconds have passed.");
}, 3000);
Enter fullscreen mode Exit fullscreen mode

5. Object-Oriented JS and Prototypal Inheritance

i. Function Constructors

When you call a function with the new keyword, a new object is created in memory, and is made available to the function via the this keyword. Functions designed to be called like that are called constructors.

var MyConstructor = function(){
    // public variables are declared using this
    this.myNumber = 5;
    // private variables are declared using var
    var secretNum = 4;
    // public getter for the private variable
    this.getSecret = function(){ return secretNum };
}
myNewObj = new MyConstructor(); // = {myNumber: 5, secretNum: 4}
myNewObj.myNumber; // = 5
myNewObj.secretNum; // undefined
myNewObj.getSecret(); // = 4

Enter fullscreen mode Exit fullscreen mode

ii. Prototypes

Every JavaScript object has a 'prototype' property, which is simply a reference to another object. When you go to access a property that doesn't exist on the actual object, the interpreter will look at its prototype. If it doesn't exist on the prototype, it'll look at the prototype's prototype. It will keep looking down this prototype chain until it hits the base object Object, which doesn't have a prototype.

// Some JS implementations let you access an object's prototype on the magic
// property `__proto__`. While this is useful for explaining prototypes it's not
// part of the standard; we'll get to standard ways of using prototypes later.
var myObj = {
    myString: "Hello world!"
};
var myPrototype = {
    meaningOfLife: 42,
    myFunc: function(){
        return this.myString.toLowerCase()
    }
};

myObj.__proto__ = myPrototype;
myObj.meaningOfLife; // = 42

// This works for functions, too.
myObj.myFunc(); // = "hello world!"

// Of course, if your property isn't on your prototype, the prototype's
// prototype is searched, and so on.
myPrototype.__proto__ = {
    myBoolean: true
};
myObj.myBoolean; // = true

// There's no copying involved here; each object stores a reference to its
// prototype. This means we can alter the prototype and our changes will be
// reflected everywhere.
myPrototype.meaningOfLife = 43;
myObj.meaningOfLife; // = 43
Enter fullscreen mode Exit fullscreen mode

iii. Prototypal Inheritance aka setting prototypes of new objects

Accessing __proto__ is non-standard, and there are no standard ways to change the prototype of an existing object. However, there are two ways to create a new object with a given prototype.

// The first is Object.create, which is a recent addition to JS, and therefore
// not available in all implementations yet.
var myObj = Object.create(myPrototype);
myObj.meaningOfLife; // = 43
Enter fullscreen mode Exit fullscreen mode

Every JS function also has a property called 'prototype'. When used as a normal function, the 'prototype' property is not used. Only when functions are used as constructors with the new keyword, the 'prototype' sets the prototype of the object being created.

// Constructors have a property called prototype. This is *not* the prototype of
// the constructor function itself; instead, it's the prototype that new objects
// are given when they're created with that constructor and the new keyword.
MyConstructor.prototype = {
    myNumber: 5,
    getMyNumber: function(){
        return this.myNumber;
    }
};
var myNewObj2 = new MyConstructor();
myNewObj2.getMyNumber(); // = 5
myNewObj2.myNumber = 6
myNewObj2.getMyNumber(); // = 6
Enter fullscreen mode Exit fullscreen mode

iv. Built-In Constructors

// Built-in types like strings and numbers also have constructors that create
// equivalent wrapper objects.
var myNumber = 12;
var myNumberObj = new Number(12);
myNumber == myNumberObj; // = true

// Except, they aren't exactly equivalent.
typeof myNumber; // = 'number'
typeof myNumberObj; // = 'object'
myNumber === myNumberObj; // = false
if (0){
    // This code won't execute, because 0 is falsy.
}
if (new Number(0)){
   // This code will execute, because wrapped numbers are objects, and objects
   // are always truthy.
}

// However, the wrapper objects and the regular builtins share a prototype, so
// you can actually add functionality to a string, for instance.
String.prototype.firstCharacter = function(){
    return this.charAt(0);
}
"abc".firstCharacter(); // = "a"
Enter fullscreen mode Exit fullscreen mode

Polyfilling takes advantage of the fact that we can modify the built-in prototypes to implement newer features of JavaScript in an older subset of JavaScript, so that they can be used in older environments such as outdated browsers.

// For instance, Object.create isn't yet available in all
// implementations, but we can still use it with this polyfill:
if (Object.create === undefined){ // don't overwrite it if it exists
    Object.create = function(proto){
        // make a temporary constructor with the right prototype
        var Constructor = function(){};
        Constructor.prototype = proto;
        // then use it to create a new, appropriately-prototyped object
        return new Constructor();
    }
}

Enter fullscreen mode Exit fullscreen mode

6. Bugs and Error Handling

// You can opt in to tell the JS engine to be very strict in its interpretation
// It must go at the top of the file to interpret the whole file in strict mode
// Or at the top of a function, to make just that function strict
"use strict"
Enter fullscreen mode Exit fullscreen mode

7. New ES6 stuff

Arrows

Arrows are a function shorthands for anonymous functions used with the => syntax. They pass the outside lexical scope (ie. this) to the function.

// Expression bodies
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);

// Statement bodies
nums.forEach(v => {
  if (v % 5 === 0)
    fives.push(v);
});

// Lexical this
var bob = {
  _name: "Bob",
  _friends: [],
  printFriends() {
    this._friends.forEach(f =>
      console.log(this._name + " knows " + f));
  }
}
Enter fullscreen mode Exit fullscreen mode

Classes

Object-oriented syntactic sugar for the prototypal inheritance pattern.

class SkinnedMesh extends THREE.Mesh {
  constructor(geometry, materials) {
    super(geometry, materials);

    this.idMatrix = SkinnedMesh.defaultMatrix();
    this.bones = [];
    this.boneMatrices = [];
    //...
  }
  update(camera) {
    //...
    super.update();
  }
  get boneCount() {
    return this.bones.length;
  }
  set matrixType(matrixType) {
    this.idMatrix = SkinnedMesh[matrixType]();
  }
  static defaultMatrix() {
    return new THREE.Matrix4();
  }
}
Enter fullscreen mode Exit fullscreen mode

String Interpolation

var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
Enter fullscreen mode Exit fullscreen mode

let and const

let is like var except it is block-scoped. Variables declared with const can only be assigned once.

if (1 < 2) {
  let i = 4;
  const name = 'Jon Snow'
}
var i = 5; // error, i is already defined
name = 'Samwell Tarly' // error, const can only be defined once
Enter fullscreen mode Exit fullscreen mode

Generator

Functions that can be paused using the yield keyword and restarted from the outside. yield _____ is called a "yield expression" which gets evaluated with whatever value we send in when we restart the generator. yield is making a request for a value.

function* fibonacci() {
  let a = 0, b = 1;

  while(true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

// Enumerates the Fibonacci numbers
for(let value of fibonacci()) {
  console.log(value);
}
Enter fullscreen mode Exit fullscreen mode

Generators are useful because they return (i.e. create) iterators. In turn, an iterator, an object with a next method, actually executes the body of generators. The next method, when repeatedly called, partially executes the corresponding generator, gradually advancing through the body until a yield keyword is hit.

function* argumentsGenerator() {
  for (let i = 0; i < arguments.length; i += 1) {
    yield arguments[i];
  }
}

var argumentsIterator = argumentsGenerator('a', 'b', 'c');

// Prints "a b c"
console.log(
    argumentsIterator.next().value,
    argumentsIterator.next().value,
    argumentsIterator.next().value
);

// ES6 has syntactic sugar for iteration.
// Prints "a", "b", "c"
for(let value of argumentsIterator) {
  console.log(value);
}
Enter fullscreen mode Exit fullscreen mode

The next method of an iterator returns an object with a value property and a done property, as long as the body of the corresponding generator has not returned. The value property refers the value yielded or returned. The done property is false up until the generator body returns, at which point it is true. If the next method is called after done is true, an error is thrown.

Maps, Sets, WeakMap, WeakSet

A Map is an object for which the keys can be any arbitrary object. A Set is a data structure which contains a finite set of elements, each occurring only once. WeakMaps and WeakSets provide leak-free object-key’d side tables. The JavaScript virtual machine periodically frees memory allocated to objects no longer in scope. An object is no longer in scope if there is no chain of references from the current scope leading to it.

// Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;

// Maps
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;

// Weak Maps
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined

// Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });
// Because the added object has no other references, it will not be held in the set
Enter fullscreen mode Exit fullscreen mode

Promises

Promises are a library for asynchronous programming. Promises are a first class representation of a value that may be made available in the future. A Promise is in one of these states:

  • pending: initial state, not fulfilled or rejected.
  • fulfilled: successful operation
  • rejected: failed operation.
  • settled: the Promise is either fulfilled or rejected, but not pending.
var someAsyncThing = function() {
  return new Promise(function(resolve, reject) {
    // this will throw, x does not exist
    resolve(x + 2);
  });
};

someAsyncThing().then(function() {
  console.log('everything is great');
}).catch(function(error) {
  console.log('oh no', error);
});
Enter fullscreen mode Exit fullscreen mode

Modules

// lib/math.js
export function sum(x, y) {
  return x + y;
}
export var pi = 3.141593;
Enter fullscreen mode Exit fullscreen mode
// app.js
import * as math from "lib/math";
alert("2π = " + math.sum(math.pi, math.pi));
Enter fullscreen mode Exit fullscreen mode
// otherApp.js
import {sum, pi} from "lib/math";
alert("2π = " + sum(pi, pi));
Enter fullscreen mode Exit fullscreen mode

Proxy

Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).

// lib/proxy.js
export function create(target) {
  var target = {};
  return new Proxy(target, {});
}
Enter fullscreen mode Exit fullscreen mode
// app.js
import * as proxy from 'lib/proxy';
var target = {};
var origin = proxy.create(target);
origin.a = 37; // operation forwarded to the target
alert('target.a = ' + target.a); // The operation has been properly forwarded
Enter fullscreen mode Exit fullscreen mode

Reference sites: https://github.com/krishnr/JavaScript-cheat-sheet


Additional Resources to learn Javascript:

Javascript Tutorial and Projects Course

Top comments (0)