JavaScript quick-reference
Understanding this in JavaScript
There isn't a single word that describes
this
well, so I just think of it as a special variable that changes depending on the situation. Those different situations are captured below. source
Case 1: the Window object
In a regular function (or if you're not in a function at all), this
points to window
. This is the default case.
function logThis() {
console.log(this);
}
logThis(); // window
// In strict mode, `this` will be `undefined` instead of `window`.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
Case 2: objects & methods
When a function is called as a method, this
points to the object that's on the left side of the dot.
/*
* You can also think of this as the "left of the dot" rule.
* For example, in myObject.myMethod(), `this` will be myObject
* because myObject is to the left of the dot.
*
* Of course, if you're using this syntax myObject['myMethod'](),
* technically it would be the "left of the dot or bracket" rule,
* but that sounds clumsy and generally terrible.
*
* If you have multiple dots, the relevant dot is the one closest
* to the method call. For example, if you have one.two.hi();
* `this` inside of hi will be two.
*/
var myObject = {
myMethod: function() {
console.log(this);
}
};
myObject.myMethod(); // myObject
Case 3: constructors & their instances
In a function that's being called as a constructor, this
points to the object that the constructor is creating.
function Person(name) {
this.name = name;
}
var gordon = new Person('gordon');
console.log(gordon); // {name: 'gordon'}
Case 4: manual binding
When you explicitly set the value of this
manually using bind
, apply
, or call
, it's all up to you.
function logThis() {
console.log(this);
}
var explicitlySetLogThis = logThis.bind({name: 'Gordon'});
explicitlySetLogThis(); // {name: 'Gordon'}
// Note that a function returned from .bind (like `boundOnce` below),
// cannot be bound to a different `this` value ever again.
// In other words, functions can only be bound once.
var boundOnce = logThis.bind({name: 'The first time is forever'});
// These attempts to change `this` are futile.
boundOnce.bind({name: 'why even try?'})();
boundOnce.apply({name: 'why even try?'});
boundOnce.call({name: 'why even try?'});
Case 5: callbacks
In a callback function, apply the above rules methodically.
function outerFunction(callback) {
callback();
}
function logThis() {
console.log(this);
}
/*
* Case 1: The regular old default case.
*/
outerFunction(logThis); // window
/*
* Case 2: Call the callback as a method
* (You'll probably NEVER see this, but I guess it's possible.)
*/
function callAsMethod(callback) {
var weirdObject = {
name: "Don't do this in real life"
};
weirdObject.callback = callback;
weirdObject.callback();
}
callAsMethod(logThis); // `weirdObject` will get logged to the console
/*
* Case 3: Calling the callback as a constructor.
* (You'll also probably never see this. But in case you do...)
*/
function callAsConstructor(callback) {
new callback();
}
callAsConstructor(logThis); // the new object created by logThis will be logged to the console
/*
* Case 4: Explicitly setting `this`.
*/
function callAndBindToGordon(callback) {
var boundCallback = callback.bind({name: 'Gordon'});
boundCallback();
}
callAndBindToGordon(logThis); // {name: 'Gordon'}
// In a twist, we give `callAndBindToGordon` a function that's already been bound.
var boundOnce = logThis.bind({name: 'The first time is forever'});
callAndBindToGordon(boundOnce); // {name: 'The first time is forever'}
Reading, parsing, tracing code
Sometimes reading and tracing JS can be confusing, but I found that reading declarations from right to left can help in better conveying a sense of what's happening in the code.
Arrays
Arrays are zero-based, so in iteration loops you don't need to match the maximum value with a i <= array.length
condition!
Letting the loop run with i < targetList.length
will correctly iterate over all indexes.
var forestCows = [
{name: "Legolas", type: "calf", hadCalf: null},
{name: "Gimli", type: "bull", hadCalf: null},
{name: "Arwen", type: "cow", hadCalf: null},
{name: "Galadriel", type: "cow", hadCalf: null},
{name: "Eowyn", type: "cow", hadCalf: "Legolas"}
];
console.log(forestCows.length);
function listArray (targetList) {
for (var i = 0; i < targetList.length; i++) {
console.log(i + " - " + targetList[i].name);
}
}
Methods
Methods are "a piece of code associated with […] an object to perform a task". In JavaScript, typical methods are functions associated with an object property:
let testObj = {
prop1: "Lorem",
prop2: "Ipsum",
propWithFunction: function() {
return 1 * 999;
}
}
// or, w/ ES6 method initializer:
let testObj = {
prop1: "Lorem",
prop2: "Ipsum",
propWithFunction() {
return 1 * 999;
}
}
Methods can be called from the containing object, and perform a task.
console.log(testObj.propWithFunction()); // outputs 999
While:
console.log(testObj);
/* outputs the entire object:
{ prop1: 'Lorem',
prop2: 'Ipsum',
propWithFunction: [Function: propWithFunction] } */
Callbacks
In computer programming, a callback is any executable code that is passed as an argument to other code
Example:
function logTenNumbers() {
for (var i = 0; i <= 10; i++) {
console.log(i);
}
}
function addHeader(callback) {
console.log("here's 10 numbers!");
callback();
}
// here we call addHeader
addHeader(function logTenNumbers() {
for (var i = 0; i <= 10; i++) {
console.log(i);
}
}); // notice the `;` -- this is a function call with a function as param!
// the higher-order function "enhances" the behavior of the callback
// Output:
// here's 10 numbers!
// 0
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9
// 10
// What if we pass the callback in its short form?
addHeader(logTenNumbers());
// ERROR! We get "TypeError: callback is not a function"
// this is the correct way: we don't need to add `()`
addHeader(logTenNumbers);
Callbacks are used in the implementation of languages such as JavaScript, including support of JavaScript functions as callbacks through js-ctypes[5] and in components such as addEventListener.[6] However, a naive example of a callback can be written without any complex code:
function someAction(x, y, someCallback) {
return someCallback(x, y);
}
function calcProduct(x, y) {
return x * y;
}
function calcSum(x, y) {
return x + y;
}
// alerts 75, the product of 5 and 15
alert(someAction(5, 15, calcProduct));
// alerts 20, the sum of 5 and 15
alert(someAction(5, 15, calcSum));
First a function someAction
is defined with a parameter intended for callback: someCallback
. Then a function that can be used as a callback to someAction
is defined, calcProduct
. Other functions may be used for someCallback
, like calcSum
. In this example, someAction()
is invoked twice, once with calcProduct
as a callback and once with calcSum
. The functions return the product and sum, respectively, and then the alert will display them to the screen.
In this primitive example, the use of a callback is primarily a demonstration of principle. One could simply call the callbacks as regular functions, calcProduct(x, y)
. Callbacks are generally useful when the function needs to perform actions before the callback is executed, or when the function does not (or cannot) have meaningful return values to act on, as is the case for Asynchronous JavaScript (based on timers) or XMLHttpRequest requests. Useful examples can be found in JavaScript libraries such as jQuery where the .each()
method iterates over an array-like object, the first argument being a callback that is performed on each iteration.
Callbacks and asynchronous programming
[…] This is blocking-style code:
a()
b()
And this is in a non-blocking style:
a(b)
In the non-blocking version b
is a callback to a
. In the blocking version a
and b
are both called/invoked (they both have ()
after them which executes the functions immediately). In the non-blocking version you will notice that only a
gets invoked, and b
is simply passed in to a
as an argument.
In the blocking version, there is no explicit relationship between a
and b
. In the non-blocking version it becomes a
's job to do what it needs to do and then call b
when it is done. Using functions in this way is called callbacks because your callback function, in this case b
, gets called later on when a
is all done.
Hopefully you can see now that callbacks are just functions that call other functions after some asynchronous task. Common examples of asynchronous tasks are things like reading a photo, downloading a song, uploading a picture, talking to a database, waiting for a user to hit a key or click on someone, etc. Anything that takes time. JavaScript is really great at handling asynchronous tasks like these as long as you take the time to learn how to use callbacks and keep your JavaScript from being blocked.
Arrow Functions
Arrow functions are a new syntax for creating functions in ES2015. This does not replace the function() {}
syntax that we know and love, but we will be seeing it more and more as the go-to function syntax.
const add = (a, b) => {
return a + b;
};
The core part of the syntax is the lack of the function keyword when defining a new function. Instead we have the =>
or fat arrow. You can call the function just as you would any other.
There are actually a few ways you can define the arrow function. For example, if the function simply returns a value and there is nothing else in the function body, we can remove the {}
and the return keyword.
const add = (a, b) => a + b;
The return here is implicit, meaning that it is implied as opposed to us having to explicitly add return to our block. If the function only had one parameter you can actually leave the ()
off the definition of the function.
const add5 = a => a + 5;
If there are no parameters to be used in the function, empty parenthesis are used as a placeholder.
const eight = () => 3 + 5;