javascriptnotesJavaScript Basics

Data types

  1. Primitive data types are passed by value, while non-primitive data types are passed by reference
  2. To know the type of a JavaScript variable, we can use the typeof operator.
Example
typeof "John Doe"; // Returns "string"
typeof 3.14; // Returns "number"
typeof true; // Returns "boolean"
typeof 234567890123456789012345678901234567890n; // Returns bigint
typeof undefined; // Returns "undefined"
typeof null; // Returns "object" (kind of a bug in JavaScript)
typeof Symbol("symbol"); // Returns Symbol

Primitive types

  1. String
    1. To use the same quotation marks inside a string and to delimit a string, use backslashes for each quotation inside the string, e.g. "This is Joe's \"favorite\" string;
    2. Write strings in multiple lines using backslashes
  2. Number
  3. BigInt
  4. Boolean
  5. Undefined: when a variable is declared but not assigned, variables that haven’t been assigned a value
  6. Null: non-existent or invalid value or empty value
  7. Symbol: introduced in ES6 version. It is used to to be an anonymous and unique value

Non-primitive types (objects)

  1. If the key contains spaces or special characters, you need to use square braces notation for assignment

    bird["where it lives"] = "forest";
    // this notation is useful for using variables to access properties
    // var query = "numPockets";
    // backpack[query]
  2. Delete keys with .delete keyword

    delete bird.color;
  3. Objects can be copied in multiple ways

    animal2 = Object.assign({}, animal);
    animal2 = { ...animal };
    animal2 = JSON.parse(JSON.stringify(animal));
  4. Check if Object has something:

    myString.hasOwnProperty("length");
  5. Check type

    typeof myString;
  6. Check for number

    Number.isNaN(12); // false
  7. Check for null

    typeof null; // object
     
    var thing = null;
    thing === null; // true

null vs undefined vs undeclared

TraitnullundefinedUndeclared
MeaningExplicitly set by the developer to indicate that a variable has no valueVariable has been declared but not assigned a valueVariable has not been declared at all
TypeobjectundefinedThrows a ReferenceError
Equality Comparisonnull == undefined is trueundefined == null is trueThrows a ReferenceError

Undeclared

Undeclared variables are created when you assign a value to an identifier that is not previously created using var, let or const. Undeclared variables will be defined globally, outside of the current scope. In strict mode, a ReferenceError will be thrown when you try to assign to an undeclared variable. Undeclared variables are bad just like how global variables are bad. Avoid them at all cost! To check for them, wrap its usage in a try/catch block.

Example
function foo() {
  x = 1; // Throws a ReferenceError in strict mode
}
 
foo();
console.log(x); // 1

undefined

A variable that is undefined is a variable that has been declared, but not assigned a value. It is of type undefined. If a function does not return any value as the result of executing it is assigned to a variable, the variable also has the value of undefined. To check for it, compare using the strict equality (===) operator or typeof which will give the 'undefined' string. Note that you should not be using the loose equality operator (==) to check, as it will also return true if the value is null.

Example
let foo;
console.log(foo); // undefined
console.log(foo === undefined); // true
console.log(typeof foo === "undefined"); // true
 
console.log(foo == null); // true. Wrong, don't use this to check if a value is undefined!
 
function bar() {} // Returns undefined if there is nothing returned.
let baz = bar();
console.log(baz); // undefined

null

A variable that is null will have been explicitly assigned to the null value. It represents no value and is different from undefined in the sense that it has been explicitly assigned. To check for null, simply compare using the strict equality operator. Note that like the above, you should not be using the loose equality operator (==) to check, as it will also return true if the value is undefined.

Example
const foo = null;
console.log(foo === null); // true
console.log(typeof foo === "object"); // true
 
console.log(foo == undefined); // true. Wrong, don't use this to check if a value is null!

Notes

  • As a good habit, never leave your variables undeclared or unassigned. Explicitly assign null to them after declaring if you don’t intend to use them yet.
  • Always explicitly declare variables before using them to prevent errors.
  • Using some static analysis tooling in your workflow (e.g. ESLint, TypeScript Compiler), will enable checks that you are not referencing undeclared variables.

Expression vs statement

There are two main syntactic categories in JavaScript: expressions and statements. A third one is both together, referred to as an expression statement. They are roughly summarized as:

  • Expression: produces a value
  • Statement: performs an action
  • Expression statement: produces a value and performs an action

A general rule of thumb:

If you can print it or assign it to a variable, it’s an expression. If you can’t, it’s a statement.

Statements

let x = 0;
 
function declaration() {}
 
if (true) {
}

Statements appear as instructions that do something but don’t produce values.

// Assign `x` to the absolute value of `y`.
var x;
if (y >= 0) {
  x = y;
} else {
  x = -y;
}

The only expression in the above code is y >= 0 which produces a value, either true or false. A value is not produced by other parts of the code.

Expressions

Expressions produce a value. They can be passed around to functions because the interpreter replaces them with the value they resolve to.

5 + 5; // => 10
 
lastCharacter("input"); // => "t"
 
true === true; // => true

Expression statements

There is an equivalent version of the set of statements used before as an expression using the conditional operator:

// Assign `x` as the absolute value of `y`.
var x = y >= 0 ? y : -y;

This is both an expression and a statement, because we are declaring a variable x (statement) as an evaluation (expression).

Implicit Type Coercion

Conversion of value from one data type to another. It takes place when the operands of an expression are of different data types.

String coercion

String coercion takes place while using the ‘ + ‘ operator. When a number is added to a string, the number type is always converted to the string type.

var x = 3;
var y = "3";
x + y; // Returns "33"

Type coercion also takes place when using the ‘ - ‘ operator, but the difference while using ‘ - ‘ operator is that, a string is converted to a number and then subtraction takes place.

var x = 3;
Var y = "3";
x - y // Returns 0

Boolean Coercion

  1. Truthy values are those which will be converted (coerced) to true. Falsy values are those which will be converted to false.

  2. All values except

    1. false
    2. 0
    3. 0n
    4. -0
    5. “”
    6. null
    7. undefined
    8. NaN

    are truthy values

Logical Operators

  1. Logical operators in JavaScript, unlike operators in other programming languages, do not return true or false. They always return one of the operands.
  2. OR ( || ) operator: If the first value is truthy, then the first value is returned. Otherwise, the second value is returned.
  3. AND ( && ) operator: If both the values are truthy, always the second value is returned. If the first value is falsy then the first value is returned or if the second value is falsy then the second value is returned.
var x = 220;
var y = "Hello";
var z = undefined;
 
x || y; // returns 220 since the first value is truthy
x || z; // returns 220 since the first value is truthy
x && y; // returns "Hello" since both the values are truthy
y && z; // returns undefined since the second value is falsy
 
if (x && y) {
  console.log("Code runs"); // runs because x && y returns "Hello" (truthy)
}
 
if (x || z) {
  console.log("Code runs"); // runs because x || y returns 220 (truthy)
}

Equality Coercion

The == operator compares values and not types.

var a = 12;
var b = "12";
a == b; // Returns true because both 'a' and 'b' are converted to the same type
 
var a = 226;
var b = "226";
a === b; // Returns false because coercion does not take place

Equality operators

  1. == is the abstract equality operator while === is the strict equality operator. The == operator will compare for equality after doing any necessary type conversions. The === operator will not do type conversion, so if two values are not the same type === will simply return false.

  2. Use === when you want to ensure both the value and the type are the same, which is the safer and more predictable choice in most cases.

  3. ESLint’s eqeqeq rule enforces the use of strict equality operators === and !== and even provides an option to always enforce strict equality except when comparing with the null literal.

  4. There’s one final value-comparison operation within JavaScript, that is the Object.is() static method. The only difference between Object.is() and === is how they treat of signed zeros and NaN values. The === operator (and the == operator) treats the number values -0 and +0 as equal, but treats NaN as not equal to each other.

  5. Rules:

    Same Type Comparison

    • If both operands are the same type, use strict equality (===) comparison
    • 1 == 1true
    • "hello" == "hello"true
    • NaN == NaNfalse (special case)
    • null == nulltrue
    • undefined == undefinedtrue

    null and undefined

    • null == undefinedtrue (only case where they equal something other than themselves)
    • null == 0false
    • undefined == 0false
    • null == falsefalse
    • undefined == falsefalse

    Number and BigInt

    • Compare numerically if the Number is a safe integer with no fractional part
    • 1n == 1true
    • 1n == 1.0true
    • 1n == 1.1false
    • 9007199254740993n == 9007199254740993false (precision loss in Number)

    String and Number

    • Convert string to number, then compare
    • "42" == 42true
    • "" == 0true (empty string converts to 0)
    • " " == 0true (whitespace-only string converts to 0)
    • "hello" == 0false (“hello” converts to NaN)

    Boolean Conversion

    • Convert boolean to number first (true → 1, false → 0), then restart comparison
    • true == 1true
    • false == 0true
    • true == "1"true (true becomes 1, then 1 == “1”)
    • false == ""true (false becomes 0, then 0 == "")

    Object to Primitive

    • Convert object to primitive using ToPrimitive operation (valueOf() then toString())
    • [] == ""true ([] converts to "")
    • [42] == "42"true ([42] converts to “42”)
    • [42] == 42true ([42] converts to “42”, then “42” == 42)
    • {} == "[object Object]"true ({} converts to “[object Object]”)
    • [1,2,3] == "1,2,3"true

    Symbol Comparison

    • Symbols never equal anything except identical symbols (using ===)
    • No type conversion occurs with symbols
    • Symbol("x") == "x"false
    • Symbol("x") == Symbol("x")false (different symbol instances)

    Default Case

    • If no conversion rule applies, return false

    Conversion Order

    1. Check if same type → use ===
    2. Check null/undefined special case
    3. Check Number ↔ BigInt
    4. Check String ↔ Number
    5. If Boolean involved → convert to Number and restart
    6. If Object involved → convert to Primitive and restart
    7. If Symbol involved → return false (no conversion)
    8. Otherwise → return false

    Common Gotchas

    • [] == ![]true ([] converts to "", ![] converts to false then 0, so "" == 0)
    • [] == 0true
    • [0] == 0true
    • "0" == [0]true
    • " \t\r\n " == 0true (whitespace converts to 0)
    • ({}) == falsefalse (“[object Object]” != 0)

Dynamically typed language

  1. JavaScript is a dynamically typed language, the type of a variable is checked during run-time, in contrast to a statically typed language, where the variable is checked during compile time
  2. Since JavaScript is a loosely (dynamically) typed language, variables in JavaScript are not associated with any type. A variable can hold the value of any data type.
  3. For example, a variable that is assigned a number type can be converted to a string type.

NaN

  1. NaN property type represents the Not a Number value. It indicates that it is not a legal number.
  2. typeof of NaN will return a Number.
  3. To check if a value is NaN, we use isNan() function
isNaN("Hello"); // Returns true
isNaN(345); // Returns false
isNaN("1"); // Returns false, since '1' is converted to Number type which results in 0 ( a number)
isNaN(true); // Returns false, since true converted to Number type results in 1 ( a number)
isNaN(false); // Returns false
isNaN(undefined); // Returns true

Passed by value and passed by reference

  1. In JavaScript, primitive data types are passed by value and non-primitive data types are passed by reference

  2. In this example, the assign operator knows that the value assigned to y is a primitive type (number type in this case), so when the second line code executes, where the value of y is assigned to z, the assign operator takes the value of y (234) and allocates a new space in the memory and returns the address. Therefore, variable z is not pointing to the location of variable y, instead, it is pointing to a new location in the memory.

    var y = 234;
    var z = y;
  3. Primitive data types when passed to another variable, are passed by value. Instead of just assigning the same address to another variable, the value is passed and new space of memory is created.

    var y = #8454; // y pointing to address of the value 234
    var z = y;
    var z = #5411; // z pointing to a completely new address of the value 234
    // Changing the value of y
    y = 23;
    console.log(z);  // Returns 234, since z points to a new address in the memory so changes in y will not affect z
  4. The assign operator directly passes the location of the variable obj to the variable obj2. In other words, the reference of the variable obj is passed to the variable obj2.

    var obj = { name: "Vivek", surname: "Bisht" };
    var obj2 = obj;
  5. While passing non-primitive data types, the assigned operator directly passes the address (reference).

    var obj = #8711;  // obj pointing to address of { name: "Vivek", surname: "Bisht" }
    var obj2 = obj;
    var obj2 = #8711; // obj2 pointing to the same address
    // changing the value of obj1
    obj.name = "Akki";
    console.log(obj2);
    // Returns {name:"Akki", surname:"Bisht"} since both the variables are pointing to the same address.