Prototype, this, and lambda functions in JavaScript

Explains this, prototype, functions and how named lambda functions are used in JavaScript.

Events in JavaScript are always asynchronous which means they most likely expect a callback function. For example:

setTimeout( whatHappensNext );

Note that there are no parentheses next to the whatHappensNext function name!

Look at the following code to grok the difference between parentheses and no parentheses when it comes to functions in JavaScript:

function add(a, b) {
	console.log("called add");
	return a + b; // the return object (a plus b)
}
add; // Does nothing
add(); // Calls add
var four = add; // points function add to variable four

var five = add(2,3); // calls function add 
                     // and points the return object to variable five

console.log(four); // [Function: add]
console.log(five); // 5

So remember, the function name without parentheses is just a pointer to the function object.
If you for example point variable A at a function object, writing A will do nothing, while writing A() with parenthesis will call the function object.
If you then point another variable B at variable A without parenthesis, variable B will point at the same function object as variable A.
But if you point variable B at variable A() with parenthesis, variable B will point to the object returned by the function object.

this

What "this" points to in JavaScript depends on if the function was called with the new keyword ...
If it was called with the "new" keyword, "this" points to the class/function object. While if no "new" keyword was used "this" points to the global scope (or the global variable in NodeJS).

function Cat(name) {
	var cat = this;
	cat.name = name;
	console.log(cat);
}

Cat("Garfield"); // console log's the global scope because no "new" keyword

new Cat("Garfield"); // console log's: Cat { name: 'Garfield' }

So the "this" variable is only used/useful when creating custom objects by using the "new" keyword.
Note that I used capital C in Cat to give a hint that Cat is a custom object and not a function!

Prototype

In JavaScript, objects can share properties and methods (functions) with other objects! This is called the object's prototype.

Lets say you have two arrays, they can both use the push method:

var arrA = [1,2,3];
var arrB = [4,5,6];

arrA.push(4); // Calls Array.prototype.push, where "this" points to arrA's object
arrB.push(7); // Calls Array.prototype.push, where "this" points to arrB's object

When to use the new keyword

Use the new keyword to create new custom objects, and not when calling functions.

The built in custom objects String, Number, Array, Boolean, RegExp and Object doesn't need the new keyword:

var string = "abc"; // new String 
var number = 42; // new Number
var array = []; // new Array
var boolean = true; // new Boolean
var regexp = /str/g; // new RegExp
var object = {}; // new Object

Also note that the native objects are immutable, meaning you can not change them, only create new ones.

var luckyNr = 8; // new Number
luckyNr = 7; // new Number

Remember the difference between custom objects and functions!

function CustomObject() {};  // use with new
function customFunction() {};

There is an unwritten rule in the JavaScript community that the first letter in custom objects should be named with Big/Uppercase Captial letter.
You can also ask yourself: Do I want to create a new object, or call a function ?

Prototype vs "this"

Your custom objects can also have prototype properties and methods.
But remember that prototype functions are just normal functions!

function Cat(name) {
	var cat = this;
	cat.name = name;
}
Cat.prototype.meow = function meow() {
	var cat = this;
	console.log(cat.name + " says meow");
}

console.log(Cat("Garfield")); // console log's "undefined" because nothing was returned
console.log(new Cat("Garfield")); // console log's "Cat { name: 'Garfield' }"

var garfield = Cat("Garfield"); // "this" will point to the global scope
                                // because the "new" keyword was omitted.

var newGarfield = new Cat("Garfield"); // "this" will point to
                                       // the custom Cat object

Cat.prototype.meow(); // console log's "undefined says meow"
                      // because "this" points to the global scope,
                      // because the function is called directly

newGarfield.meow(); // console log's "Garfield says meow",
                    // because the function is called via the Cat object

garfield.meow(); // TypeError: Cannot read property 'meow' of undefined,
                 // because calling Cat() didn't return an object
	

Variable this in (prototype) functions points either to the global scope or the (custom) object depending on if it's called directly or via (custom) object.

Events vs "this"

Prototype functions are just normal functions ...

function Worker(name) {
	var worker = this;
	worker.name = name;
}
Worker.prototype.next = function next() {
	var worker = this;
	console.log(worker.name + " doing work!");
}

var worker = new Worker("Robot A");

setTimeout( worker.next , 3000); // Points to the actual function,
                                 // so when it's called in the future "this"
                                 // points to the global scope

setTimeout( worker.next(), 3000); // Called right away,
                                  // passes the return object to
                                  // the setTimout function
	

Remember the difference between calling the function using parentheses and pointing to the function (without parentheses) ...
In the first setTimeout, we pass the pointer (variable name) to the worker.next prototype function. When it's later called by setTimeout "this" in Worker.prototype.next will point to the global scope.

Because "this" inside Worker.prototype.next will point to the global scope, console.log prints:
"undefined doing work!", because name is not defined in the global scope.

For it to work, you have to run worker.next() inside a function ...

Lambda function

Where JavaScript expect a callback function, like in setTimout, window.onload, button.addEventListener, etc it's always safe the give a named lambda function that in turn does what you want, for example calling a prototype method.

Optimization note: Functions add a lot over overhead, although if it's run many times, most JavaScript engines will inline them, so it doesn't matter.

setTimeout( function runWorkerNext() {
	worker.next();
});


// EcmaScript 6: (warning: function will be anonymous)

setTimeout( () => {
	worker.next();
});

setTimeout( () => worker.next() );
	

Only use the arrow function when you need a quick (as in fast to type) throwaway function

Why should I name lambda functions ?

By naming the functions it gets easier to debug and lift them out:

setTimeout( runWorkerNext );

// Lifted out
function runWorkerNext() {
	worker.next();
}

Lifting functions out can be good for readability and prevents the Christmas tree from hell or Pyramid of doom when calling many asynchronous functions after each other.

And stack traces with named functions are more helpful then stack traces with anonymous functions.

this in subfunctions

function Cat(name) {
	this.name = name;
}
Cat.prototype.meow = function meow() {
	console.log(say("meow"));
	
	function say(sound) {
		// "this" in here points to the global scope
		return this.name + " says " + sound;
	}
}
Cat.prototype.meow2 = function meow() {
	// Only works in EcmaScript 6+
	// Arrow functions binds "this" to the lexical (function) scope
	say = (sound) => this.name + " says " + sound;
	
	console.log(say("meow"));
}
Cat.prototype.meow3 = function meow() {
	var cat = this; // Point to the right "this"
	
	console.log(say("meow"));
	
	function say(sound) {
		return cat.name + " says " + sound;
	}
}

var garfield = new Cat("Garfield");

garfield.meow(); // undefined says meow
garfield.meow2(); // Garfield says meow
garfield.meow3(); // Garfield says meow
	

Always start custom objects and it's prototype functions by saving "this" in a variable!

Factory functions

If you think "this" and "new" is too confusing. You can use factory functions:

Optimization note: Factory functions are much slower the prototype's and take up more memory!

function worker(name) {
	return {
		next: function next() {
			console.log(name + " doing work!");
		}
	}
}

var robot = worker("Robot A"); // "new" can be omitted if you don't want
                               // to inherit the prototype or use "this"
                               // worker returns a new object that has a next function

setTimeout( robot.next , 3000); // Called in the future
setTimeout( robot.next(), 3000); // Called right away
	

Written by Nov 25, 2016.


Follow me via RSS:   RSS https://zäta.com/rss_en.xml (copy to feed-reader)
or Github:   Github https://github.com/Z3TA