JavaScript Tricks
- Understanding
Scoping
andHoisting
Scoping
- Scope determines the accessibility of variables inside a JS program. There are 3 types of scope in javascript as follows:
Functional Scope
- A variable declared inside a function is only accessible within or to other functions which are declared inside the said function. These variables are also knows as
Local variables
. - In the example below we are declaring a variable inside the function scope and printing the value of it. Which will successfully output as
9
as it was inside the scope of the function. When we try to access the variable outside the function scope it will print the error that the variable is not defined.
function scope() { var variable = "9"; console.log(variable); // Outputs 9 } scope(); console.log(variable); // Error: Variable is not defined.
- Now, consider the example below when we create a variable outside the functional scope and print the value of the variable, we get separate value for the same variable on the basis of the scopes they were defined in.
var variable = "7"; function scope() { var variable = "9"; console.log(variable); // Outputs 9 } scope(); console.log(variable); // Outputs 7
- A variable declared inside a function is only accessible within or to other functions which are declared inside the said function. These variables are also knows as
Global Scope
- A function or variable that is defined outside any functional scope but inside a global score are called global variables / functions. Global functions / variables can be accessed and manipulated by any member inside that program.
- Using the same example below we can say that, the variable defined in the first line is in the global scope (global scope variable), which can be accessed or changed by any member in the program.
var variable = "7"; function scope() { var variable = "9"; console.log(variable); // Outputs 9 } scope(); console.log(variable); // Outputs 7
- From the example, we can also conclude that local variable inside a function are given more priority then global variables. While both (global variable and local variable) has the same name, but when the variable was accessed inside the function scope the value of variable was printed as
9
due to it’s priority over the global variable.
Block Scope
- Variables declared inside a
{...}
block cannot be accessed from outside the block. Block Scope
was introduced with the release of ES6,let
andconst
are the two providers forBlock Scope
.- Important thing to note is that, variables declared inside a
{...}
block usingvar
are not block scope variables as they are accessible from outside of the{...}
block.
{ let x = 2; var y = 3; console.log(x, y); // Outputs 2 3 } console.log(y); // Outputs 3 (x can NOT be used here).
- Variables declared inside a
Hoisting
- In-formerly Hoisting is referred as a mechanism where function / variables declarations in the program are moved to the top of their scope before code execution.
- Meaning, that no matter where functions / variables are declared, they are moved to the top of their scope regardless of weather they were globally scoped or locally scoped.
- In-short, The variable / function declarations are put into memory during the compile phase, but stay exactly where you typed them in your code.
- JavaScript putting function declarations into memory before it executes any code segment is that it allows you to use a function before you declare it in your code as shown in the example below.
function catName(name) { console.log("My Cat's name is " + name); // outputs My Cat's name is Burgers } catName("Burgers");
catName("Burgers"); function catName(name) { console.log("My Cat's name is " + name); // outputs My Cat's name is Burgers }
- Hoisting let’s use initialize and use variables before they are declared as shown in the code snippet below.
cat = "Meow"; console.log(cat); // Outputs Meow var cat;
- JavaScript only hoists declarations not initialization / assignments to that variable. As only declarations are hoisted, initializing a declared variable after use will give undefined as shown in the following code snippet.
console.log(cat); // Outputs undefined var cat; cat = "Meow";
- Scope determines the accessibility of variables inside a JS program. There are 3 types of scope in javascript as follows:
- When to use
var
,let
andconst
.
Using
var
var
declarations are globally scoped or function / locally scoped. The scope is global when avar
variable is declared outside a function. This means that any variable that is declared with var outside a function block is available for use in the whole program.var
is function scoped when it is declared within a function. This means that it is available and can be accessed only within that function.- Let’s take a look at an example that defines the problem with
var
. - In the following code, greeter is redefined to “
say Hello instead
“. While this is not a problem if you knowingly want greeter to be redefined, it becomes a problem when you do not realize that a variable greeter has already been defined before.
var greeter = "hey hi"; if (true) { var greeter = "say Hello instead"; console.log(greeter); // outputs "say Hello instead" } console.log(greeter); // outputs "say Hello instead"
var
can be updated as well as re-defined regardless of the scope so the following code snippet would also work like a charm, but result in the same output as above.
if (true) { var greeter = "hey hi"; var greeter = "say Hello instead"; console.log(greeter); // outputs "say Hello instead" } console.log(greeter); // outputs "say Hello instead"
- If you have used greeter in other parts of your code, you might be surprised at the output you might get. This will likely cause a lot of bugs in your code. This is why
let
andconst
are necessary. var
variables are hoisted to the top of their scope and initialized with a value ofundefined
.
Using
let
let
is now preferred for variable declaration. It’s no surprise as it comes as an improvement to var declarations. It also solves the problem with var that we just covered above.let
is block scoped. A block is a chunk of code bounded by{...}
. Anything within curly braces is a block. So a variable declared in a block with let is only available for use within that block as shown below.
let greeter = "hey hi"; if (true) { let greeter = "say Hello instead"; console.log(greeter); // outputs "say Hello instead" } console.log(greeter); // outputs "hey hi"
let
can be updated but not re-declared. Just likevar
, a variable declared withlet
can be updated within its scope. Unlikevar
, alet
variable cannot be re-declared within its scope.
if (true) { let greeter = "hey hi"; let greeter = "say Hello instead"; console.log(greeter); // outputs "SyntaxError: Identifier 'greeter' has already been declared" }
- Why is there no error in the code snippet earlier than the one above? This is because both instances are treated as different variables since they have different scopes.
- This fact makes
let
a better choice thanvar
. When usinglet
, you don’t have to bother if you have used a name for a variable before as a variable exists only within its scope. - Also, since a variable cannot be declared more than once within a scope, then the problem discussed earlier that occurs with
var
does not happen. - Just like
var
,let
declarations are hoisted to the top. Unlikevar
which is initialized asundefined
, thelet
keyword isnot initialized
. So if you try to use alet
variable before declaration, you’ll get aReference Error
.
Using
const
- Variables declared with the
const
maintain constant values.const
declarations share some similarities withlet
declarations. const
declarations are block scoped just likelet
declarations.const
declarations can only be accessed within the block they were declared.
const greeter = "hey hi"; if (true) { const greeter = "say Hello instead"; console.log(greeter); // outputs "say Hello instead" } console.log(greeter); // outputs "hey hi"
const
cannot be updated or re-declared. Meaning that the value of a variable declared withconst
remains the same within its scope. It cannot be updated or re-declared. So if we declare a variable withconst
, everyconst
declaration, therefore, must be initialized at the time of declaration.
// Type error is when you try to update the value of the const in the same scope. if (true) { const greeter = "hey hi"; greeter = "say Hello instead"; // TypeError: Assignment to constant variable. console.log(greeter); }
// Syntax error is when you try to re-declare the const variable in the same scope. if (true) { const greeter = "hey hi"; const greeter = "say Hello instead"; // SyntaxError: Identifier 'greeter' has already been declared console.log(greeter); }
- This behavior is somehow different when it comes to objects declared with
const
. While aconst
object cannot be updated, the properties of this objects can be updated.
const greeting = { message: "say Hi", } console.log(greeting); // outputs "say hi" greeting.message = "say Hello instead"; console.log(greeting); // outputs "say Hello instead" const greeting = { message: "say Hello instead", } // SyntaxError: Identifier 'greeting' has already been declared greeting = { message: "say Hello instead", } // TypeError: Assignment to constant variable.
- Hoisting of
const
is just likelet
.const
declarations are hoisted to the top but are not initialized.
Notable Difference
- Difference between
var
,let
andconst
:
var
declarations areglobally scoped
orfunction scoped
whilelet
andconst
areblock scoped
.var
variables can be updated and re-declared within its scope;let
variables can be updated but not re-declared;const
variables can neither be updated nor re-declared.- They are all hoisted to the top of their scope. But while
var
variables are initialized withundefined
,let
andconst
variables arenot initialized
.- While
var
andlet
can be declared without being initialized,const
must be initialized during declaration.
var let const Globally scoped or function scoped Block scoped Block scoped Updated and re-declared Updated but not re-declared cannot be Updated nor re-declared initialized as Undefined Not Initialized Not Initialized Can be declared without being initialized Can be declared without being initialized Cannot be declared without being initialized
- Use
===
and!==
instead of==
and!=
- The “==” and “!=” operator performs and an automatic type conversion if needed, Whereas the “===” and “!==” will not perform any conversion. The “===” and “!==” operators will compare the value and the type, which is also considered to be a faster approach.
[10] === 10 // is false [10] == 10 // is true '10' == 10 // is true '10' === 10 // is false [] == 0 // is true [] === 0 // is false '' == false // is true but true == "a" is false '' === false // is false
- The “==” and “!=” operator performs and an automatic type conversion if needed, Whereas the “===” and “!==” will not perform any conversion. The “===” and “!==” operators will compare the value and the type, which is also considered to be a faster approach.
- Difference Between
typeof
,instanceof
andconstructor
are as follows:typeof
: a JavaScript unary operator used to return a string that represents the primitive type of a variable, don’t forget thattypeof
null will returnobject
, and for the majority of object types (Array, Date, and others) will also returnobject
.constructor
: is a property of the internal prototype property, which could be overridden by code.instanceof
: is another JavaScript operator that check in all the prototypes chain the constructor it returns true if it’s found and false if not.
var arr = ["a", "b", "c"]; typeof arr; // return "object" arr instanceof Array // true arr.constructor(); // []
- Self Calling Function
- This is often referred as a
Self-Invoked Anonymous Function
or aImmediately Invoked Function Expression (IIFE)
. It’s a function that runs on its own once created. - An IIFE can be used for some default calculations like calculating percentage, addition of some numbers etc.
// Syntax : (function(){ // some private code that will be executed automatically })();
- Consider a scenario where there is a discount day for all customers at McDonald’s offering 25% off on all products.
- Instead of adding the discount every time in the bill a
Self-Invoked Anonymous Function
can achieve this task in the following manner.
// Example: var bill_amount = 2500; (function(bill_amount){ var totalPrice = bill_amount - (bill_amount * 0.25) ; console.log(totalPrice); // 1875 })(bill_amount)
- This is often referred as a
- Generate a random set of Alpha-Numeric Characters
- A random alpha-numeric generator can be used for many purposes ranging from generating different keys for different users which can stay unique or generating keys for encryption and decryption of data.
console.log(generateRandomAlphaNum(50)) function generateRandomAlphaNum(len) { var randomString = ""; for( ; randomString.length < len; randomString += Math.random().toString(36).substr(2)); return randomString.substr(0, len); } // output: // gvc88xtu4v8bg9nax9977d7qpft22ek5idqk8pzij4hcc57f3s
- A random alpha-numeric generator can be used for many purposes ranging from generating different keys for different users which can stay unique or generating keys for encryption and decryption of data.
- Use
Splice
instead ofDelete
- Deleting an element from an array will not actually delete it, but instead it will replace the element with
undefined
- Splicing an element from an array will remove the element as well as it’s index from the array.
var fruits = ["Banana", "Orange", "Apple", "Mango"]; fruits.length; // returns 4 delete fruits[2]; // returns true fruits.length; // returns 4 because array is now ["Banana", "Orange", undefined x 1, "Mango"] fruits.length; // returns 4 fruits.splice(2, 1); // return Apple fruits.length; // returns 3 because array is now ["Banana", "Orange", "Mango"]
- Deleting an element from an array will not actually delete it, but instead it will replace the element with
- Use Logical AND
(&&)
/ OR(||)
for conditions also known as Short circuit conditionals.- The logical AND
(&&)
/ OR(||)
operators will reduce the lines of code as well as provide more code readability compared to using if statements. - Logical AND
(&&)
/ OR(||)
operators can also be used to set default values for function argument.
var foo = 10; // Instead of using an if Statement if (foo === 10) { doSomething(); } // Use Logical AND operator foo === 10 && doSomething(); // Instead of using an if Statement if (foo !== 10) { doSomething(); } // Use Logical OR operator foo !== 10 || doSomething(); // Default value using logical OR function defaultArguments(arg1){ return arg1 = arg1 || 10; // arg1 will have 10 as a default value if it’s not already set } console.log(defaultArguments()); // Sets the value of arg1 to default and prints 10 console.log(defaultArguments(20)); // Sets the value of arg1 to 20 and prints 20
- The logical AND
- Use the
map()
function method to loop through an array’s items- The
map()
function method creates a new array with the results of calling a function for every array element. - The
map()
function method does not execute the function for empty elements neither does it change the original array.
// Array Looping var squares = [1,2,3,4]; for(let val = 0; val < squares.length; val++) { squares[val] = squares[val] * squares[val]; }// squares will be equal to [1, 4, 9, 16] // Instead of looping over an array use map() as follows: var squares = [1,2,3,4].map(function (val) { return val * val; }); // squares will be equal to [1, 4, 9, 16]
- The
- Comma Operator
- The comma operator
(,)
evaluates each of its operands (from left to right) and returns the value of the last operand. - This lets you create a compound expression in which multiple expressions are evaluated, with the compound expression’s final value being the value of the rightmost of its member expressions.
- This is commonly used to provide multiple parameters to a for loop.
- It can also be used to assign values to a variables as shown in the following code snippet.
var a = 0; var b = ( a++, 99 ); console.log(a); // a will be equal to 1 console.log(b); // b is equal to 99 var a = 0; var b = ( a+++a++, 99+a ); console.log(a); // a will be equal to 2 console.log(b); // b is equal to 101
- The comma operator
- Primitive Operations vs Function calls
- A Primitive operation is an operation like finding minimum or maximum from two given numbers without calling the
min()
ormax()
function call from theMATH
library. - Math.max() uses the ternary operator to return the max.
- Avoiding the call to the
Math.min()
function and directly using the ternary operator will spare some nanosecond. However, this works differently inMath.max()
. - Using ternary operators, improve performance compared to making a call to the
Math.min()
orMath.max()
functions.
// Variable declarations var a = 5, b = 10; // Function Call Math.min(a,b); // returns 5 Math.max(a,b); // returns 10 // Primitive Operation a < b ? a : b; // returns 5 a > b ? a : b; // returns 10
- A Primitive operation is an operation like finding minimum or maximum from two given numbers without calling the
- Debugging using
console.time()
andconsole.timeEnd()
- Boosting performance is one of the major concerns for all the developers as the end users always wants everything working correctly as well as fast.
- Debugging is not just about using a
console.log()
every time and everywhere, instead to analyze the performance of a code block you can simply debug it withconsole.time()
andconsole.timeEnd()
. - Calling the
console.time()
function is easy and it is also very handy tool to measure the performance. This function is usually written at top of the code block which a developer wants to test. console.time()
function takes in one argument which is a string that describes what you want to analyze.console.timeEnd()
function is written at the bottom of the code block that is to be tested.console.timeEnd()
function takes in one argument which is a string that is same as the one given inconsole.time()
function. You’ll then see the time it took to run the code in your console.
console.time('Loop_items'); var items = []; for(var i = 0; i < 1000000; i++) { items.push({index: i}); } console.timeEnd('Loop_items'); // outputs "Loop_items: 667.908ms"
- Passing arguments as objects instead of passing arguments individually to functions.
- This way of passing arguments has lots of benefits:
- The order of the parameters does not matter anymore, allowing you to concentrate on delivering high-quality code instead of repeatedly checking the function definition.
- Auto-completion becomes easier as the IDE will focus on the specific argument that you are providing.
- This method communicates intent in a clear way as function calls specify the value of each property.
- Large codebase will benefit tremendously from the additional verbosity.
// Instead of using function personPrintArguments(firstName, lastName, age) { console.log(firstName + " " + lastName + " is " +age + " Years Old."); // John 40 is Doe Years Old. } personPrintArguments("John", 40, "Doe" ) // While passing arguments in the functions with parameters the order matters a lot or instead use named parameters. // Using Object as argument function personPrintObject(args) { console.log(args.firstName + " " + args.lastName + " is " + args.age + " Years Old."); // prints John Doe is 40 Years Old. } personPrintObject({ firstName : "John", age : 40, lastName : "Doe" })
- This way of passing arguments has lots of benefits:
- Dynamic property names with object passing as an argument
- Assigning a dynamic property name can be helpful in the long run. There could be multiple scenarios where name of the property needs to be changed or updated due to some unforeseen reasons.
- Dynamic property names can also be very code efficient as a developer does not need to change the property name very where in the entire script, changing a single variable value will affect in changing the property name in the entire script.
// Dynamic property variables const dynamicFName = 'firstName'; const dynamicLName = 'lastName'; const dynamicAge = 'age'; // Function calling function personPrintObject(args) { console.log(args[dynamicFName] + " " + args[dynamicLName] + " is " + args[dynamicAge] + " Years Old."); // prints John Doe is 40 Years Old. } personPrintObject({ [dynamicFName] : "John", [dynamicAge] : 40, [dynamicLName] : "Doe" })
NOTE
- The assigning and calling syntax for Dynamic property names are slightly different from the Passing arguments as object.
- The Setting for Passing arguments as object uses the
"firstName"
property name directly. Whereas the Dynamic property name uses the"[dynamicFName]"
variable to set the property name. - The calling for Passing arguments as object uses the
(.)
dot notation to access it’s member"args.firstName"
. Whereas the Dynamic property name uses the"args[dynamicFName]"
dictionary format to call it’s property.
- Required Function Parameters
- Another useful trick in javascript for Passing arguments as objects or Dynamic property name. Required function parameter is just another useful technique that will always make the user enter a value.
- Expanding on the default parameter technique, we can mark a parameter as mandatory. First define a function to throw an error with an error message, as shown in the following code snippet:
let isRequired = () => { throw new Error('This is a mandatory parameter.'); }
- Then assign the function as the default value for the required parameters. Remember, the default values are ignored when a value is passed for a parameter at the invocation time. But, the default value is considered if the parameter value is
undefined
.
function personPrintObject(args = isRequired()) { console.log(args.firstName + " " + args.lastName + " is " + args.age + " Years Old."); // prints John Doe is 40 Years Old. } personPrintObject({ firstName : "John", age : 40, lastName : "Doe" }) personPrintObject() // will call the is require function as no parameter is passed. personPrintObject( {} ) // Will call the personPrintObject() function as a blank object is equal to "undefined".
- In the above code, args will be undefined and that will try to set the default value for it which is the isRequired() function. It will throw an error as,
- Object Destructing
- The object destructuring is a useful JavaScript feature to extract properties from objects and bind them to variables.
- Object destructuring can extract multiple properties in one statement, can access properties from nested objects, and can set a default value if the property doesn’t exist.
- Object Destructuring in simple term means, extracting data from arrays, objects, and maps alongside setting them into new, distinct variables.
- Three different examples for Object destructing:
- Assigning to existing variable names
var employee = // Object we want to destructure { firstname: 'Jon', lastname: 'Snow', dateofbirth: '1990' }; // Destructuring the object into our variables var { firstname, lastname, dateofbirth } = employee; console.log( firstname, lastname, dateofbirth); // outputs Jon Snow 1990
- Assigning to new variable names
- The following code destructured the object into variables with a different name than the object property
var employee = // Object we want to destructure { firstname: 'Jon', lastname: 'Snow', dateofbirth: '1990' }; // Destructuring the object into variables with // different names than the object variables var { firstname: fn, lastname: ln, dateofbirth: dob } = employee; console.log( fn, ln, dob); // outputs Jon Snow 1990
- Assigning to a variable with default values
- We can also assign default values to variables whose keys may not exist in the object we want to destructure. This will prevent our variable from having an undefined value being assigned to it.
var employee = // Object we want to destructure { firstname: 'Jon', lastname: 'Snow', dateofbirth: '1990' }; // Destructuring the object into variables without // assigning default values var { firstname, lastname, country } = employee; console.log( firstname, lastname, country); // outputs "Jon Snow undefined" // Destructuring the object into variables by // assigning default values var { firstname = 'default firstname', lastname = 'default lastname', country = 'default country' } = employee; console.log( firstname, lastname, country); // outputs "Jon Snow default country"
- Assigning to existing variable names
NOTE
- Hope these tricks were helpful and you learnt something new out of it.
Mr. Ellis Tarmaster