Unit testing with jspm, karma, mocha and react

Javascript biggest downside is all the dependencies you need to bring in and configuration needed to get something similar as unit testing going. Imagine your psyched for this idea of a code project you just had and just wan’t to get started coding. But after a few nights of your precious spare time you almost give up because you even haven’t started coding yet. All this packages and configuration is killing your motivation. Javascript fatigue… Enough ranting…

I’ve been struggling for some days now to get unit testing with react in a project using jspm. I’m documenting this to hopefully save future me and someone else the hours and frustration.

Jspm

Jspm is a package manager for Javascript that aims to solve the problem of different module formats in JavaScript(ES6, AMD, CommonJS, and globals). Jspm uses the SystemJS module loader which handles all this cases and also can handle registries other than npm like github.

When developing your Javascript application jspm uses a transpiler of your choice to transpile your next generation Javascript code, TypeScript or whatever needs transpiling directly in the browser. When going to production you can produce a bundle.

Karma

Karma is a test runner which runs your tests in a browser or even headless browsers like PhantomJS.  It spawns a web server that executes the test against each browser which then displays the results in the command line. It’s primary usage is for unit testing and not end to end testing.

Karma can watch specified files and run the tests whenever something changes. It also has the functionality to load preprocessors which means that your javascript files can be processed before getting served to the browser. One example could be running the files through babel or webpack.

It’s testing framework agnostic and there’s support for every popular testing framework out there like Mocha, QUnit and Jasmine and so on. If it doesn’t you can build your own adapter.

Mocha

My previous post was about unit testing with mocha. Read it here.

React

React is Javascript library for building user interfaces. Enough of that. Let’s get started.

Installation

I’ve already created jspm-react-boilerplace. I will use that as a starting point. So start by cloning the github repo and install the packages.

$ git clone https://github.com/Jontem/jspm-react-boilerplate.git
$ npm install
$ jspm install

After the 1000 dependencies have finished installing we need to install karma and mocha along with some required karma plugins.

$ npm install --save-dev karma karma-chrome-launcher karma-jspm karma-mocha mocha

After another 1000 packages is downloaded we can run.

$ ./node_modules/.bin/karma init

Just choose mocha as the testing framework and Chrome for browser then choose the defaults. We will edit the config file directly. If you don’t want to specify the path to karma you can install it globally. But after we add an npm script we don’t need to worry about that.

Open karma.conf.js and find the frameworks property and add jspm.

    frameworks: [
      'jspm',
      'mocha'
    ]

After frameworks add a jspm configuration section

jspm: {
      loadFiles: [
        'test/**/*.js',
        'src/**/*.js'
      ]
    }

This will make jspm run a transpiler on the files specified before handing them over to mocha.

The last thing we need to add to the karma cofig is the proxies property and this is because karma loads our specified files under the base url /base/. We can tell the karma webserver that som paths should automatically be proxied to /base/

proxies: {
      '/test/': '/base/test/',
      '/src/': '/base/src/',
      '/jspm_packages/': '/base/jspm_packages/'
    }

Let’s install two more packages… Chai which is an assertion library and react-addons-test-utils

$ jspm install --dev npm:chai npm:react-addons-test-utils

Now edit package.json so we can run npm test. The script property should have these entries.

"scripts": {
    "start": "npm run webserver & npm run watch",
    "webserver": "node server.js",
    "watch": "chokidar-socket-emitter",
    "test": "karma start"
  }

Running npm start launches a browser that will execute the tests. But it will fail because it won’t find any tests. So lets add one. We start by creating the test/ and src/ directories.

Create a file hello_world_component.test.js under the test/ directory.

import {assert} from "chai";
import TestUtils from "react-addons-test-utils";
import * as React from "react";
import DOM from "react-dom";
import {HelloWorld} from "../src/hello_world_component";

describe("Hello world component", function () {

    it("should should display the text hello world", function () {
        var renderer = TestUtils.createRenderer();
        const result = renderer.render(<HelloWorld />);

        assert.equal(result.type, "div");
        assert.deepEqual(result.props.children, [
            <h1>Hello world</h1>,
            <span>test</span>
        ]);
    });

});

Next we create the file which defines the component hello_world_component.js under src/

import * as React from "react";

export const HelloWorld = React.createClass({
    render() {
        return (
            <div>
                <h1>Hello world</h1>
                <span>test</span>
            </div>
        );
    }
});

Now run npm test and you should have one green test!

The benefit of using karma is that we’re running our tests in a browser environment which means we have access to DOM which some of React test utils needs for simulating DOM events or rendering deeper hierarchies of react components. In our tests above we make use of shallow rendering which only renders react components one level deep and doesn’t need access to a DOM. Read more about React test utils on reacts website.

If you think this post was long you don’t have any idea of the struggle to get this working :). Oh one last thing… If you want to use the renderIntoDocument method be aware that stateless components cannot be tested. It will only return null, I don’t know why… But it was hidden somewhere in the documentation.

Getting started with Mocha and unit testing

Mocha

Mocha is a JavaScript test framework running on NodeJS and in the browser. It’s feature rich and has great support for asynchronous testing. It also gives you the possibility to use the assertion library of choice.

A selection of features

  • Async support
  • Test coverage reporting
  • Javascript API for running tests
  • file watcher support
  • Highlights slow tests
  • Transpiler support

Run our first test

Getting started with mocha is easy.

npm install --global mocha
npm install --save-dev mocha
mkdir test

Lets add our first test
test/first_test.js

var assert = require('assert');
describe('Array', function() {
  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function() {
      assert.equal(-1, [1,2,3].indexOf(4));
    });
  });
});

Now we just run mocha

mocha

You should be getting an output like this.
mocha test results

Well that wasn’t too complicated. So what happened.

First we used a built in node module called assert. Basically it just evaluates an expression and throws and exception if the result is false. Most times you would use a more advanced assert library like ChaiJS.

Then we used the describe function which takes a string and a callback. It’s used for grouping tests and as seen above you can nest describe functions.

The It function takes a string and callback which will make one test case. We then assert the result which mocha displays in it’s test reporter.

Specifying tests to run

When we run mocha it looks for test with this glob pattern test/*.js.

If we want to look for tests recursively

mocha --recursive

Or if you want to specify another directory for your tests

mocha -- path/to/tests/*.js

And if you don’t wan’t to re run your tests you can tell mocha to watch your test files for changes.

mocha --watch

To see all parameters just run mocha --help. In my osx terminal i had this strange issue that the help output was cut off so i only got a third of the output. Upgrading node from 6.2.0 to 6.5.0 solved that problem.

Using the “fat arrow”(arrow functions)

You can use arrow functions with Mocha  to specify callbacks to describe and It. But because of the lexical binding of this it’s discourage because you wont be able to access Mocha context. With the mocha context you can specify how the tests should behave for example this.skip() and this.slow(1000). The skip function explains itself and the slow function defines a threshold for mocha to mark this test as slow which the test reporter will notify you about.

Testing async code

Lets add a new test.
test/async_test.js

var assert = require('assert');
describe('Async', function () {

  describe('callbacks', function () {
    it('should return 123 after 3 seconds', function (done) {
      this.timeout(4000);
      asyncWithCallback(function (data) {
        assert.equal(data, 123);
        done();
      })
    });
  });

  describe('promises', function () {
    it('should return 567 after 1 second', function () {
      return asyncWithPromise().then((data) =&gt; {
        assert.equal(data, 567);
      });
    });
  });

});

function asyncWithCallback(callback) {
  setTimeout(() =&gt; {
    callback(123);
  }, 3000);
}

function asyncWithPromise() {
  return new Promise((resolve) =&gt; {
    setTimeout(() =&gt; {
      resolve(567);
    }, 1000);
  });
}

With mocha we can make use of the done callback to specify when asynchronous tests are done or we can return a promise which mocha will wait for being resolved. Mocha has a default timeout of 2000ms so if a test is taking longer you need to use the mocha context to set a higher timeout.

Conclusion

It’s really simple and fast to get mocha up and running and write simple unit tests. Head over to the Mocha website fore more detailed information.

Getting started with TypeScript and Visual Studio Code

What’s TypeScript

Typescript is a typed superset of JavaScript that compiles down to plain JavaScript that works in any browser, host or operating system. TypeScript is open source and maintained by Microsoft.

TypeScript introduces features like:

  • Static typing
  • Interfaces
  • Generics

But also uses features from ES6:

  • Classes
  • Modules
  • The arrow syntax
  • Optional and default parameters

Larger JavaScript projects tends to get a bit messy without following design patterns. TypeScript tries to solve this problem by making it easier to write modular code and by introducing types in JavaScript.

Setting up the environment

I assume that you have Visual Studio Code and npm already installed. We start by installing the TypeScript compiler(tsc).

npm install -g typescript

Now lets create a file named app.ts. TypeScript files have the .ts extension and its’s in these files we will write our TypeScript code.

// app.ts
function sayHelloWorld(name) {
    console.log("Hello world " + name);
}

sayHelloWorld("Jonathan")

As you can see this is just plain old JavaScript but it proves that you can mix TypeScript and JavaScript code. Now lets create tsconfig.json file. The tsconfig file is placed in the root directory of the TypeScript project. It specifies the files and the compiler options to compile the project. It is not mandatory to have this file but it’s simpler than specifying a whole bunch of command line arguments to tsc.

// tsconfig.js
{
    "compilerOptions": {
        "target": "es5",
        "removeComments": true,
        "sourceMap": true
    },
    "exclude": [
        "node_modules",
        "typings"
    ]
}

To create a task in Visual Studio Code that will compile our TypeScript file press CTRL+SHIFT+P to access the command palette. Type “configure task runner” and press enter. You will now have a task.json file created under the folder .vscode in your root folder. It probably looks like this:

{
	// tasks.json
	"version": "0.1.0",
	"command": "tsc",
	"isShellCommand": true,
	"args": ["-w", "-p", "."],
	"showOutput": "silent",
	"isWatching": true,
	"problemMatcher": "$tsc-watch"
}

If we look at the args array we have the -w parameter which tells the compiler to look for change files in our project so we don’t have to run our task manually. The -p parameter specifies which folder is the root of our project. The dot specifies the current directory.

Now press CTRL+SHIFT+B to start the task. It should have created a app.js file in the same directory. Create this simple html document and verify in the JavaScript console that it works.

<!doctype html>
<html>

<head>
    <script src="app.js"></script>

<body>
</body>

</html>

Types

Lets look at types:

let any1: any = "asd";
let any2: any = 123;

let str1: string = "asd";
let str2 = "asd"; // Type inference

let number1: number = 123;

let bool1: Boolean = true;

let myFunction1: (message: string) => string = function (message: string) {
    return "hej";
}

let myFunction2 = function (message: string) { // Type inference
    return "hej";
}

let obj = { // Type inference
    property1: 123,
};

I’m not going in to detail about every row but there’s two notable things. First we have the type any which can be any type of value. Second we have the object literal at the bottom. When an object is declared you cannot add properties later.

interface ISayHelloCallback {
    (message: string): void;
}

interface ISayHelloOptions {
    names: string[];
    alert?: boolean;
    callback: ISayHelloCallback;
}

function sayHello(options: ISayHelloOptions) {
    var msg = "Hello " + options.names.join(", ") + "!";
    
    if(options.alert) {
        alert(msg);
    } else {
        console.log(msg);
    }
    
    options.callback("Said hello!");
}

sayHello({
   names: [
       "Kalle",
       "Nisse"
   ],
   callback: (message: string) => {
       console.log(message);
   }
   
});

You can set an interface as type parameter to a function so when you calling that function you need to specify an object with those properties. You can also set optional properties. Above i specified the alert property as an optional parameter. Interfaces can also be used for specifying function signatures.

Type assertions

window.onload = () => {
    let input = <HTMLInputElement>document.getElementById("input1");
    input.value = "Tjena";
}

Type assertions works like type casting in other languages. Sometimes you know more about the underlying type than TypeScript does. The return type of document.getElementById is HTMLElement. So we cast it to HTMLInputElement to get access to all the properties of an input element.

Classes and inheritance

class BaseClass {
    public publicBaseProperty1: number;
    publicBaseProperty2: number; // default public
    protected protectedBaseProperty: number;
    private baseProperty3: number;

    constructor(someNumber: number) {
        this.baseProperty3 = someNumber
    }
    
    baseMethod() {
        console.log("hej");
    }
}

class ChildClass extends BaseClass {
    static childProperty1: number = 123;
    private privateChildProperty: number;
    
    constructor() {
        super(1);
        this.publicBaseProperty1 = 123;
        this.publicBaseProperty2 = 1234;
    }
    
    childMethod() {
        this.protectedBaseProperty = 555;
        super.baseMethod()
    }
    
    get childProperty1(): number {
        return this.privateChildProperty;
    }
    
    set childProperty1(value: number) {
        this.privateChildProperty = value;
    }
}

Classes can inherit other classes and implement interfaces. Above we can se that TypeScript supports the common functionality of other object oriented languages.

Loading external libraries

To get intellisense for external libraries like jQuery you need to get a TypeScript definition file. Go to http://definitelytyped.org/  and download the .d.ts file for jQuery. And then reference it in the top of your .ts file

/// <reference path="typings/browser.d.ts" />

$(document).ready(function () {
    alert(1);
});

Don’t forget to load the jQuery library either with a module loader or just include it on the web page.

Moving on

I hope you’ve got something out of this and if you wan’t to learn more about TypeScript there’s a lot more documentation at the website https://www.typescriptlang.org/.

JavaScript series: Lexical scope, closures, function scope and block scope

Lexical scope

Lexical scope which is also sometimes called static scope defines how variable names are resolved in nested functions.

function myOuterFunction() {
    var outerVariable = 10;

    console.log(globalVariable); // 5

    function myInnerFunction() {
        console.log(globalVariable); // 5
        console.log(outerVariable); // 10
    }

    myInnerFunction();
}

var globalVariable = 5;

myOuterFunction();

With lexical scope we can access variables defined in parent scopes. Above outerVariable is declared in the scope of myOuterFunction but is still accessible inside the scope of myInnerFunction. The globalVariable is declared in the global scope and will be accessible everywhere.

function myFunction() {
    var myVariable = 10;

    console.log(myVariable); // 10
}

var myVariable = 5;

myFunction();

console.log(myVariable) // 5

Variables declared in an outside scope can be overriden in a child scope. We’ve declared myVariable in the global scope and then declared it again in the scope of myFunction. We can see that myVariable has different values depending on which scope you’re in.

function myFunction() {
    var myVariable = 10;

    console.log(myVariable); // 10
}

myFunction();

console.log(myVariable) // ReferenceError

Variables declared in an inner scope is not accessible by an outer scope.

Closure

Closure is when a function remember its lexical scope even when it’s referenced and executed elsewhere and even after the outer function has returned.

function functionBuilder() {
    var count = 0;
    return function () {
        count++;
        console.log("Number of times this function has been called: " + count);
    }
}

var myFunction = functionBuilder();

myFunction(); // 1
myFunction(); // 2
myFunction(); // 3

With the help of closures we can have private variables. Here we’ve created a function named functionBuilder which declares a variable named count and then returns a function which just increment count by one and logs to the console how many times it’s been called. The count variable is now inaccessible in the global scope. But our returned function still has access to it. This is what closure is all about.

var myModule = (function () {
    var count = 5;

    function printValue() {
        console.log("Value: " + count);
    }
    
    return {
        increment: function () {
            count++;
            printValue();
        },
        decrement: function () {
            count--;
            printValue();
        }
    };
} ());

myModule.increment(); // 6
myModule.decrement(); // 5
myModule.increment(); // 6

myModule.printValue(); // Won't work
myModule.count; // Won't work

To use a modular design pattern we can make use of Immediately-invoked function expression(IIFE) which is a function that is executed immediately. Here we returned an object with public methods on it. The public methods then have access to our private state.

Function scope

Before a function starts executing an executiion context is created and pushed to the stack. The execution context contains information about its lexical environment and which functions and variables that belongs to the scope. That means that every variable and function declaration is known before the function starts executing. Before ECMAScript 6 JavaScript only has function scope and not block scope like any C based language.

function functionScope() {
    if(true) {
        var a = 1;
    }
    
    console.log(a);
}

functionScope();

This is valid code in JavaScript and it is called hoisting, meaning that every variable declared with the var statement will be lifted to the top of the function and available everywhere in the function even if it’s declared inside a block.

console.log(a); // undefined
var a = 1;
console.log(a); // 1

console.log(b); // ReferenceError

It’s important to understand the difference between declaring and initializing a variable. Above we can see that a is accessible before the line declaring it but it’s not initialized which means it’s value is undefined. The last line shows us that forgetting to declare a variable gives us a ReferenceError. Because of hoisting it’s good practice to declare variables at the top of a function so that it’s clear that they are function scoped.

var functions = [];
for (var i=0;i<3;i++) {
    functions[i] = function() {
        console.log(i);
    }
}

functions[0](); // 3
functions[1](); // 3
functions[2](); // 3

Coming from a block scoped language can sometimes trick you to believe that the above code would print 1 2 3, but that is not correct because of function scope and closures. Closure is when a function remembers its lexical scope even after it’s referenced elsewhere and because of the variable called i is function scoped it’s value for each function in the array will be 3.

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

functions[0](); // 0
functions[1](); // 1
functions[2](); // 2

But we can solve this by creating a wrapping function which gives us a new function scope. Here we created an IIFE which takes i as a parameter and then returns our function. The reason that it works is beacuse it’s creating a new scope for our returned function in each iteration.

Block scope

As of ECMAScript 6 you have the possibility to have block scope in JavaScript with the new let and const keyword. Because of how JavaScript declares variables let and const is also hoisted internally. But you will get a ReferenceError using it before the declare statement.

function test() {
    if (1 === 1) {
        let a = 1;
    }

    console.log(a); // ReferenceError
}

test();

As we see above the variable called a declared with let is local to the if statement.

function test() {

    let a = 2;
    //let a = 3; // Throws an error. Already declared

    console.log(a); // 2
}

let a = 1;
test();
console.log(a); // 1

The lexical aspect works the same as with var but you can’t redeclare a variable in the same block scope with let.

var functions = [];
for (let i=0;i<3;i++) {
    functions[i] = function() {
        console.log(i);
    }
}

functions[0](); // 0
functions[1](); // 1
functions[2](); // 2

With the let keyword we don’t have to create a wrapping function to have a unique value of variable i in each iteration. This is because every iteration itself is a new block scope.

Conclusion(tl;dr)

Lexical scope defines how variable names are resolved in nested functions.

Closure is the mechanism of a function remembering it’s lexical scope even when the functions is referenced elsewhere and even after the parent function has returned.

Before ECMAScript 6 JavaScript only has function scope meaning that every variable created with the var keyword is accessible in the entire function. It’s declared even before the row of the declaration but it’s value isn’t initialized. This is called hoisting.

In ECMAScript 6 we’ve got the let and const keywords that lets us declare variables block scoped.

JavaScript series: The ThisBinding

The this keyword in Javascript can be confusing even for experienced developers. What this is a reference to depends on how the function was called. It’s a runtime binding that has nothing to do with where the function was declared. The ThisBinding makes it possible to use a function in multiple contexts rather than creating multiple versions of it.

To find out what the this keyword will be a reference to you need to find the call-site of the function. In other words where the function was called either by looking at your code or through a debugger. Then you’ll have to inspect the call-site to determine which of the four rules explained below that applies.

The new binding

The first rule with the highest precedence is the new binding.

function Person(name) {
	this.name = name;
}

var personObj = new Person("Jonathan");
console.log(personObj.name); // Jonathan

When the new operator is put in front of a function call it creates a new object and assigns it’s reference to the this keyword.  The newly created object also gets it’s prototype to reference Person.prototype which in this case is an empty object. To learn more about the prototype read the post Prototype-based inheritance of my Javascript series.

If the function does not explicitly return an object the object this is referencing will be returned. Should the function return anything else that is not an object that return value will be ignored and the object referenced by this will be returned.

Explicit binding

The explicit binding forces this to reference a specified object.

function sayHi() {
	console.log("Hi " + this.name );
}

var person1 = {
	name: "Jonathan"
};

var person2 = {
	name: "Kalle"
};

sayHi.call(person1); // Hi Jonathan
sayHi.apply(person2); // Hi Kalle

With .call and .apply we can set this to reference the object we want on any function call(except functions that is using hard binding).

Function.prototype.apply(thisArg, [argsArray])
Function.prototype.call(thisArg[, arg1[, arg2[, …]]])

The call and apply function is inherited from Function.prototype. They achieve the same goal but with different signatures. The apply function takes an array as it’s second parameter which will be the arguments for the function invoked. The call function requires the arguments to be listed explicitly as parameters.

Hard binding

Sometimes we don’t want the caller to change the thisBinding. We can then use hard binding.

function sayHello() {
	console.log(this.name);
}

var person1 = {
	name: "Jonathan"
};

var person2 = {
	name: "Kalle"
};

var origSayHello = sayHello;
sayHello = function() {
	origSayHello.call(person2);
};

sayHello.call(person1); // Kalle

By saving a reference to sayHello and then creating a wrapper function which always runs the original function with .call and our specific object we’re throwing away whatever the sayHello.call specifies. This way we can always be sure that the this keyword is referencing what we want.

In ECMAScript 5 we have the built in function bind that is specified on Function.prototype to help us with hard binding.

function sayHello() {
	console.log(this.name);
}

var person1 = {
	name: "Jonathan"
};

var name = "Kalle";

setTimeout(sayHello, 1000); // undefined
setTimeout(sayHello.bind(person1), 1000); // Jonathan

The setTimeout function will have it’s thisBinding pointing at the global/window object which is the last of the four rules(Default binding, explained below). But with hard binding we force the thisBinding to point the object we want.

Implicit binding

The third rule is the implicit binding. The rules states that the containing object is referenced by the this keyword when a function is called.

function sayHello() {
	console.log(this.name);
}

var person1 = {
	name: "Jonathan",
	sayHi: sayHello
};

var person2 = {
	name: "Kalle",
	sayHi: sayHello
};

person1.sayHi(); // Jonathan
person2.sayHi(); // Kalle

As we can see above it doesn’t matter were the function was declared only how it was called. So when sayHi was called with person1.sayHi the this keyword is referencing person1 and on the line under it’s referencing person2.

var personUtilities = {
	sayHello: function() {
		console.log(this.name)
	}
};

var person1 = Object.create(personUtilities);
person1.name = "Jonathan";


var person2 = Object.create(personUtilities);
person2.name = "Kalle";

person1.sayHello(); // Jonathan
person2.sayHello(); // Kalle

The same is true for functions in the prototype chain no matter how high up they’re defined.

The implicit binding is all about finding the nearest object and then you’ll know what this is a reference to.

Default binding

The last catch-all rule is the default binding.

function sayHello() {
	console.log(this.name);
	console.log(this === window)
}

var name = "Jonathan";

sayHello(); // Jonathan, true

If none of the other three rules above matches the default binding kicks in. The example above shows us that this is referencing the global/window object in the sayHello function.

function sayHello() {
	"use strict";
	console.log(this === undefined)
}

var name = "Jonathan";

sayHello(); // undefined

If strict mode is specified inside the function the global/window object is not eligible for the default binding. If the call-site is running in strict mode but not the executed function this will reference the global/window object.

var person = {
	name: "Jonathan",
	sayHello: function() {
		console.log(this.name);
	}
};

var sayHi = person.sayHello;


sayHi(); // undefined

Remember that it’s all about the call-site. In the above code we created a variable sayHi and referenced person.sayHello. Then we called sayHi without an object in front of it so the default binding rule applies and in this case the global/window object doesn’t have variable called name and therefore we get an undefined error.

Arrow functions

Until arrow functions was introduced in ECMAScript 6 every function defined it’s own this reference. Arrow functions lexically binds this. Meaning that this will be the same as in it’s enclosing context.

function Person(name) {
	this.name = name;

	setTimeout(function() {
		console.log(this.name); // undefined
		console.log(this === window); // true
	}, 1000);
}

var person = new Person("Jonathan");

Without a normal callback function the default binding will apply and this will be the global/window object.

function Person(name) {
	this.name = name;

	setTimeout(() => {
		console.log(this.name); // Jonathan
	}, 1000);
}

var person = new Person("Jonathan");

But with a arrow function this will be the same as in the enclosing context.

setTimeout(() => {
	"use strict";
	console.log(this === window); // true
}, 1000);	

In the above code this is referencing the window object even though were using strict mode because this is lexically bound.

Conclusion(tl;dr)

The this keyword in JavaScript can be confusing but when you learn the four binding rules it’s quite straightforward. It’s all about how and where the function was called.

We have the new binding which happens when the new keyword is put in front of any function. It creates an empty object and binds this to it inside that function.

The explicit binding with .call and .apply which makes it possible to call a function with a specified object as this.

The implicit binding that states that it doesn’t matter where a function was declared only where it was called. Find the call-site and on which object the function was called. If obj.sayHello() was called, then inside sayHello this would point to obj.

The default binding which is the catch-all rule binds this to the global/window object(in strict mode it will be undefined) when none of the other three rules apply.

Arrow functions introduced in ECMAScript 6 is the exception to these rules above. It uses a lexical this which means that this will be the same as this in the enclosing context.