Skip to content

Instantly share code, notes, and snippets.

@yaditya
Last active June 12, 2020 03:18
Show Gist options
  • Save yaditya/b16535ebea3e2d65b04c to your computer and use it in GitHub Desktop.
Save yaditya/b16535ebea3e2d65b04c to your computer and use it in GitHub Desktop.
Common ES6 methods
Source: https://leanpub.com/understandinges6/read/
includes(), startsWith(), endsWith()
---------------------------------------
var msg = "Hello world!";
console.log(msg.startsWith("Hello")); // true
console.log(msg.endsWith("!")); // true
console.log(msg.includes("o")); // true
console.log(msg.startsWith("o")); // false
console.log(msg.endsWith("world!")); // true
console.log(msg.includes("x")); // false
console.log(msg.startsWith("o", 4)); // true
console.log(msg.endsWith("o", 8)); // true
console.log(msg.includes("o", 8)); // false
====================================================
repeat()
---------
"x".repeat(3); // "xxx"
"hello".repeat(2); // "hellohello"
"abc".repeat(4); // "abcabcabcabc"
==========================================
Object.is()
-----------
console.log(+0 == -0); // true
console.log(+0 === -0); // true
console.log(Object.is(+0, -0)); // false
console.log(NaN == NaN); // false
console.log(NaN === NaN); // false
console.log(Object.is(NaN, NaN)); // true
console.log(5 == 5); // true
console.log(5 == "5"); // true
console.log(5 === 5); // true
console.log(5 === "5"); // false
console.log(Object.is(5, 5)); // true
console.log(Object.is(5, "5")); // false
================================================
let()
-----
for (var i=0; i < items.length; i++) {
process(items[i]);
}
// i is still accessible here and is equal to items.length
for (let i=0; i < items.length; i++) {
process(items[i]);
}
// i is not accessible here
// using var, variable i is shared across each iteration of the loop, meaning the closures created inside the loop all hold a reference to the same variable. The variable i has a value of 10 once the loop completes, and so that’s the value each function outputs. To fix the issue, we need to use IIFE.
var funcs = [];
for (var i=0; i < 10; i++) {
funcs.push(function() { console.log(i); });
}
funcs.forEach(function(func) {
func(); // outputs the number "10" ten times
});
// using let, we don't need IIFE. Each iteration through the loop results in a new variable being created and initialized to the value of the variable with the same name from the previous iteration.
var funcs = [];
for (let i=0; i < 10; i++) {
funcs.push(function() { console.log(i); });
}
funcs.forEach(function(func) {
func(); // outputs 0, then 1, then 2, up to 9
});
===============================================================================
Object Destructuring
---------------------
var options = {
repeat: true,
save: false,
rules: {
custom: 10,
}
};
// later
var { repeat, save, rules: { custom }} = options;
console.log(repeat); // true
console.log(save); // false
console.log(custom); // 10
==========================================================
Array Destructuring
---------------------
var colors = [ "red", [ "green", "lightgreen" ], "blue" ];
// later
var [ firstColor, [ secondColor ] ] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
==============================================================
Mixed Destructuring
----------------------
Mixed destructuring is very useful for pulling values out of JSON configuration structures without navigating the entire structure.
var options = {
repeat: true,
save: false,
colors: [ "red", "green", "blue" ]
};
var { repeat, save, colors } = options;
console.log(repeat); // true
console.log(save); // false
console.log(colors); // "red,green,blue"
console.log(colors === options.colors); // true
==================================================================================
isFinite() and isNaN()
--------------------------
These two new methods are aimed at eliminating certain types of errors that can be caused when non-number values are used with isFinite() and isNaN() without dramatically changing the language.
console.log(isFinite(25)); // true
console.log(isFinite("25")); // true
console.log(Number.isFinite(25)); // true
console.log(Number.isFinite("25")); // false
console.log(isNaN(NaN)); // true
console.log(isNaN("NaN")); // true
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN("NaN")); // false
========================================================
parseInt() and parseFloat()
---------------------------
They are used as Number.parseInt() and Number.parseFloat() now
=================================================================
isInteger
----------
In this code, Number.isInteger() returns true for both 25 and 25.0 even though the latter looks like a float. Simply adding a decimal point to a number doesn’t automatically make it a float in JavaScript. Since 25.0 is really just 25, it is stored as an integer. The number 25.1, however, is stored as a float because there is a fraction value.
console.log(Number.isInteger(25)); // true
console.log(Number.isInteger(25.0)); // true
console.log(Number.isInteger(25.1)); // false
==============================================================
isSafeInteger
-----------------------------
ECMAScript 6 introduces Number.isSafeInteger() to better identify integers that can accurately be represented in the language. There is also Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER that represent the upper and lower bounds of the same range, respectively. The Number.isSafeInteger() method ensures that a value is an integer and falls within the safe range of integer values:
var inside = Number.MAX_SAFE_INTEGER,
outside = inside + 1;
console.log(Number.isInteger(inside)); // true
console.log(Number.isSafeInteger(inside)); // true
console.log(Number.isInteger(outside)); // true
console.log(Number.isSafeInteger(outside)); // false
===================================================================
## FUNCTIONS
Default parameters
---------------------
ECMAScript 6 makes it easier to provide default values for parameters by providing initializations that are used when the parameter isn’t formally passed. For example:
function makeRequest(url, timeout = 2000, callback = function() {}) {
// the rest of the function
}
// uses default timeout
makeRequest("/foo", undefined, function(body) {
doSomething(body);
});
// uses default timeout
makeRequest("/foo");
// doesn't use default timeout
// In the case of default parameter values, the value of null is considered to be valid and the default value will not be used.
makeRequest("/foo", null, function(body) {
doSomething(body);
});
=============================================================================
Rest Parameters
-------------------
Rest parameters are indicated by three dots (...) preceding a named parameter. That named parameter then becomes an Array containing the rest of the parameters (which is why these are called “rest” parameters). For example, sum() can be rewritten using rest parameters like this:
function sum(first, ...numbers) {
let result = first,
i = 0,
len = numbers.length;
while (i < len) {
result += numbers[i];
i++;
}
return result;
}
==============================================================================
Destructured Parameters
------------------------------
Old way:
function setCookie(name, value, options) {
options = options || {};
var secure = options.secure,
path = options.path,
domain = options.domain,
expires = options.expires;
// ...
}
setCookie("type", "js", {
secure: true,
expires: 60000
});
ES6 way:
function setCookie(name, value, { secure, path, domain, expires } = {}) {
// ...
}
setCookie("type", "js", {
secure: true,
expires: 60000
});
This example now works exactly the same as the first example in this section. Providing the default value for the destructured parameter means that secure, path, domain, and expires will all be undefined if the third argument to setCookie() isn’t provided.
===================================================================================
The Spread Operator
---------------------------
The spread operator allows you to specify an array that should be split and have its items passed in as separate arguments to a function. The spread operator for argument passing makes using arrays for function arguments much easier.
Old way:
let values = [25, 50, 75, 100]
console.log(Math.max.apply(Math, values)); // 100
ES6 way:
let values = [25, 50, 75, 100]
// equivalent to
// console.log(Math.max(25, 50, 75, 100));
console.log(Math.max(...values)); // 100
============================================================================
The name Property
--------------------
ECMAScript 6 adds the name property to all functions for easier function debugging.
var doSomething = function doSomethingElse() {
// ...
};
var person = {
get firstName() {
return "Nicholas"
},
sayName: function() {
console.log(this.name);
}
}
console.log(doSomething.name); // "doSomethingElse"
console.log(person.sayName.name); // "sayName"
console.log(person.firstName.name); // "get firstName"
console.log(doSomething.bind().name); // "bound doSomething"
console.log((new Function()).name); // "anonymous"
===================================================================================
new.target, [[Call]], and [[Construct]]
---------------------------------------------
Old way:
The 'this' value below is checked to see if it’s an instance of the constructor, and if so, it continues as normal. If this isn’t an instance of Person, then an error is thrown. This works because the [[Construct]] method creates a new instance of Person and assigns it to this. Unfortunately, this approach is not completely reliable because this can be an instance of Person without using new:
function Person(name) {
if (this instanceof Person) {
this.name = name; // using new
} else {
throw new Error("You must use new with Person.")
}
}
var person = new Person("Nicholas");
var notAPerson = Person("Nicholas"); // throws error
var notAPerson = Person.call(person, "Michael"); // works!
==> The call to Person.call() passes the person variable as the first argument, which means this is set to person inside of the Person function. To the function, there’s no way to distinguish this from being called with new.
ES6 way:
function Person(name) {
if (typeof new.target !== "undefined") {
this.name = name; // using new
} else {
throw new Error("You must use new with Person.")
}
}
var person = new Person("Nicholas");
var notAPerson = Person.call(person, "Michael"); // error!
By using new.target instead of this instanceof Person, the Person constructor is now correctly throwing an error when used without new.
=====================================================================================
Block-Level Functions
--------------------------------
Block level functions are hoisted to the top of the block in which they are defined, so typeof doSomething returns "function" even though it appears before the function declaration in the code. Once the if block is finished executing, doSomething() no longer exists. See below example:
"use strict";
if (true) {
console.log(typeof doSomething); // "function"
function doSomething() {
// ...
}
doSomething();
}
console.log(typeof doSomething); // "undefined"
ECMAScript 6 also allows block-level functions in nonstrict mode, but the behavior is slightly different. Instead of hoisting these declarations to the top of the block, they are hoisted all the way to the containing function or global environment. For example:
// ECMAScript 6 behavior
if (true) {
console.log(typeof doSomething); // "function"
function doSomething() {
// ...
}
doSomething();
}
console.log(typeof doSomething); // "function"
In this example, doSomething() is hoisted into the global scope so that it still exists outside of the if block. ECMAScript 6 standardized this behavior to remove the incompatible browser behaviors that previously existed. ECMAScript 6 runtimes will all behave in the same way.
=========================================================================
Arrow Functions
-----------------------------
# Single argument
var reflect = value => value;
// effectively equivalent to:
var reflect = function(value) {
return value;
};
# Multiple arguments
var sum = (num1, num2) => num1 + num2;
OR
var sum = (num1, num2) => {
return num1 + num2;
}
// effectively equivalent to:
var sum = function(num1, num2) {
return num1 + num2;
};
# NO arguments
var getName = () => "Nicholas";
// effectively equivalent to:
var getName = function() {
return "Nicholas";
};
# Return obj literal
var getTempItem = id => ({ id: id, name: "Temp" });
// effectively equivalent to:
var getTempItem = function(id) {
return {
id: id,
name: "Temp"
};
};
# IIFE
Old way:
let person = (function(name) {
return {
getName() {
return name;
}
};
}("Nicholas"));
console.log(person.getName()); // "Nicholas"
Using arrow function:
let person = ((name) => {
return {
getName() {
return name;
}
};
})("Nicholas");
console.log(person.getName()); // "Nicholas"
# Lexical this Binding
The call to this.doSomething() is broken because this is a reference to the element object (in this case document) that was the target of the event, instead of being bound to PageHandler:
var PageHandler = {
init: function() {
document.addEventListener("click", function(event) {
this.doSomething(event.type); // error
}, false);
},
doSomething: function(type) {
console.log("Handling " + type + " for " + this.id);
}
};
Now the code below works as expected, but may look a little bit strange. By calling bind(this), you’re actually creating a new function whose this is bound to the current this, which is PageHandler:
var PageHandler = {
init: function() {
document.addEventListener("click", (function(event) {
this.doSomething(event.type); // no error
}).bind(this), false);
},
doSomething: function(type) {
console.log("Handling " + type + " for " + this.id);
}
};
Arrow functions have implicit 'this' binding, which means that the value of this inside of an arrow function is always the same as the value of this in the scope in which the arrow function was defined. For example:
var PageHandler = {
init: function() {
document.addEventListener("click",
event => this.doSomething(event.type), false);
},
doSomething: function(type) {
console.log("Handling " + type + " for " + this.id);
}
};
# Array processing
The concise syntax for arrow functions makes them ideal for use with array processing. For example, if you want to sort an array using a custom comparator, you typically write something like this:
var result = values.sort(function(a, b) {
return a - b;
});
That’s a lot of syntax for a very simple procedure. Compare that to the more terse arrow function version:
var result = values.sort((a, b) => a - b);
# Lexical arguments Binding
function createArrowFunctionReturningFirstArg() {
return () => arguments[0];
}
var arrowFunction = createArrowFunctionReturningFirstArg(5);
console.log(arrowFunction()); // 5
Inside of createArrowFunctionReturningFirstArg(), arguments[0] is referenced by the created arrow function. That reference contains the first argument passed to createArrowFunctionReturningFirstArg().
When the arrow function is later executed, it returns 5, which was the first argument passed in to createArrowFunctionReturningFirstArg(). Even though the arrow function is no longer in the scope of the function that created it, arguments remains accessible as a lexical binding.
# Identifying Arrow Functions
var comparator = (a, b) => a - b;
console.log(typeof comparator); // "function"
console.log(comparator instanceof Function); // true
Both typeof and instanceof behave the same with arrow functions as they do with other functions.
var sum = (num1, num2) => num1 + num2;
console.log(sum.call(null, 1, 2)); // 3
console.log(sum.apply(null, [1, 2])); // 3
var boundSum = sum.bind(null, 1, 2);
console.log(boundSum()); // 3
In this example, the sum() function is called using call() and apply() to pass arguments as you would with any function. The bind() method is used to create boundSum(), which has its two arguments bound to 1 and 2 so that they don’t need to be passed directly.
OBJECTS
Property Initializer Shorthand
---------------------------------
Old way: duplication of name and age
function createPerson(name, age) {
return {
name: name,
age: age
};
}
ES6 way:
When a property in an object literal only has a name and no value, the JavaScript engine looks into the surrounding scope for a variable of the same name. If found, that value is assigned to the same name on the object literal. So in this example, the object literal property 'name' is assigned the value of the local variable 'name'.
function createPerson(name, age) {
return {
name,
age
};
}
===============================================================================
Method Initializer Shorthand
-----------------------------------
Old way:
Need to specify a name and then the full function definition to add a method to an object.
var person = {
name: "Nicholas",
sayName: function() {
console.log(this.name);
}
};
ES6 way:
var person = {
name: "Nicholas",
sayName() {
console.log(this.name);
}
};
==================================================
Computed Property Names
-------------------------
The square brackets inside of the object literal indicate that the property name is computed, which ES5 don't have and will generate syntax error if used.
var suffix = " name";
var person = {
["first" + suffix]: "Nicholas",
["last" + suffix]: "Zakas"
};
console.log(person["first name"]); // "Nicholas"
console.log(person["last name"]); // "Zakas"
===========================================================
Object.assign()
-------------------
Read directly from source
==========================================================================
Duplicate Object Literal Properties
------------------------------------------
ES5 way:
var person = {
name: "Nicholas",
name: "Greg" // syntax error in ES5 strict mode
};
ES6 way:
In ECMAScript 6, the duplicate property check has been removed. Both strict and nonstrict mode code no longer check for duplicate properties and instead take the last property of the given name as the actual value.
var person = {
name: "Nicholas",
name: "Greg" // not an error in ES6
};
console.log(person.name); // "Greg"
==========================================================================
Changing Prototypes
-------------------------
Read directly from source
==========================================================================
Merge Objects in Array
----------------------
[{a: 1, b: 2}, {a: 3, b: 4}, {a: 5, b: 6, c: 7}].reduce((acc, curr) => ({...acc, ...curr}), {});
==========================================================================
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment