JavaScript Starter Guide for Python Developers

Photo by Irvan Smith on Unsplash

JavaScript Starter Guide for Python Developers

This is all the JavaScript you need to start web dev journey as a Python Dev

Introduction

This guide is designed to help Python developers quickly get up to speed with JavaScript (JS).


1. Variables and Data Types

JavaScript Example

var userArr = ['name1', 1, 'name3'];
console.log(typeof userArr[0]);

Key Points:

  • var, let, and const:

    • var: Function-scoped, prone to hoisting.

    • let: Block-scoped, recommended for mutable variables.

    • const: Block-scoped, for constants (values cannot be reassigned).

  • Prefer let over var.

  • Dynamic Typing: Like Python, JavaScript is dynamically typed. However, JS has more quirks due to implicit type coercion.

Python Comparison

user_arr = ['name1', 1, 'name3']
print(type(user_arr[0]))  # <class 'str'>

In Python, variable scope depends on indentation (function or global scope), and const is not natively supported.


2. Primitives and Type Coercion

JavaScript Example

let xConst = '1';
let y = 1 + xConst; // Implicit type coercion
console.log(typeof xConst); // 'string'
console.table([xConst, y]);

Quirks:

  • JavaScript implicitly converts types (e.g., adding a number to a string results in string concatenation).

  • Use console.table() for debugging.

Python Comparison

x_const = '1'
y = 1 + int(x_const)  # Explicit type conversion
print(type(x_const))  # <class 'str'>

Python requires explicit type conversions and avoids implicit coercion.


3. Arrays

JavaScript Example

let arr = [1, 2, 5];
arr.push({ name: 'aditya' });
console.log(typeof arr[3]); // 'object'

Key Points:

  • Flexible Types: Arrays can hold mixed types, unlike Python lists.

  • Methods: Use .push() to append elements (similar to Python’s .append()).

  • No native tuple equivalent.

Python Comparison

arr = [1, 2, 5]
arr.append({'name': 'aditya'})
print(type(arr[3]))  # <class 'dict'>

4. Objects

JavaScript Example

let userObj = {
  age: 2,
  isAvailable: function() {
    return 'yes';
  }
};
console.log(Object.keys(userObj)); // ['age', 'isAvailable']

Key Points:

  • Versatile: Objects in JS are more dynamic and flexible than Python dictionaries.

  • Methods: You can define functions directly within objects.

  • Use Object.keys() to get property names.

Python Comparison

user_obj = {
  'age': 2,
  'is_available': lambda: 'yes'
}
print(list(user_obj.keys()))  # ['age', 'is_available']

5. Loops and Iterations

JavaScript Example

for (let i = 0; i < userArr.length; i++) {
  userArr[i] = userArr[i] + 1;
}

Python Comparison

for i in range(len(user_arr)):
    user_arr[i] = user_arr[i] + 1

6. Functions

JavaScript Example

// Function Declaration
function add(a) {
  return a[0] + a[1];
}

// Arrow Function
const sum = (n) => {
  let output = 0;
  for (let i = 0; i < n; i++) {
    output += i;
  }
  return output;
};

Key Points:

  • Multiple ways to define functions: function, const, and arrow.

  • Arrow functions do not bind their own this (lexical this).

  • Quirk: Functions can accept more arguments than required, and these arguments can be of any datatype (e.g., Object, Array, or a single variable) without throwing an error. Even if a function doesn’t require an argument, passing one won’t result in an error.

Python Comparison

def add(a):
    return a[0] + a[1]

def sum(n):
    return sum(range(n))

7. Equality Operators

JavaScript Example

console.log(1 == '1');  // true
console.log(1 === '1'); // false
    true === 1     // false (boolean vs. number) |Loose Equality
    null === undefined // false (different types) |Loose Equality
    1 == [1] // true |Loose Equality
    5 === 5        // true (both value and type are the same) |Strict Equality
    5 === '5'      // false (different types: number vs. string) |Strict Equality
    ```
### Key Points:

- **`==`**** vs ****`===`****:**
  - `==` performs type coercion.
  - `===` checks both value and type (strict equality).

### Python Comparison

Python has only strict equality:

```python
print(1 == '1')  # False

8. Asynchronous Programming

Callbacks

JavaScript execution is single-threaded in nature. JavaScript’s power lies in callbacks and asynchronous programming, which can be confusing. Many modern APIs, functions, and libraries have built-in callbacks, which may feel unintuitive for Python developers.

function fetchData(callback) {
  setTimeout(() => {
    callback('Data received!');
  }, 1000);
}

Note: Async programming in JavaScript doesn’t mean multi-threading but is a way to asynchronously run the code without waiting for the current operation to complete. This is particularly useful for database queries, I/O operations, API calls, etc.

Callback Hell

function fetchDataStep1(data, callback) {
  console.log(`Step 1: Processing ${data}`);
  setTimeout(() => {
    callback(`${data} -> Step 1 done`);
  }, 1000);
}

function fetchDataStep2(data, callback) {
  console.log(`Step 2: Processing ${data}`);
  setTimeout(() => {
    callback(`${data} -> Step 2 done`);
  }, 1000);
}

function fetchDataStep3(data, callback) {
  console.log(`Step 3: Processing ${data}`);
  setTimeout(() => {
    callback(`${data} -> Step 3 done`);
  }, 1000);
}

// Chained callbacks (callback hell)
fetchDataStep1("Initial data", (step1Result) => {
  fetchDataStep2(step1Result, (step2Result) => {
    fetchDataStep3(step2Result, (step3Result) => {
      console.log(`Final Result: ${step3Result}`);
    });
  });
});

Though nested callbacks make the work done, it is very hard to read the logic, especially at debug time.

Promises - To Avoid Callback Hell

A promise represents a placeholder for a future value. It’s an object that will either resolve (success) or reject (failure) at some point in time. Promises help to manage asynchronous operations more elegantly, avoiding callback hell—a situation where multiple nested callbacks make code difficult to read and maintain.

Understand Callback Hell Example As Python Dev -

def fetch_data_step1(data, callback):
    print(f"Step 1: Processing {data}")
    callback(f"{data} -> Step 1 done")

def fetch_data_step2(data, callback):
    print(f"Step 2: Processing {data}")
    callback(f"{data} -> Step 2 done")

def fetch_data_step3(data, callback):
    print(f"Step 3: Processing {data}")
    callback(f"{data} -> Step 3 done")

# Chained callbacks
fetch_data_step1("Initial data", lambda step1_result: 
    fetch_data_step2(step1_result, lambda step2_result: 
        fetch_data_step3(step2_result, lambda step3_result: 
            print(f"Final Result: {step3_result}")
        )
    )
)

"""
OR (look at the below function with the same example but without using lambda )
"""

def fetch_data_step1(data, callback):
    print(f"Step 1: Processing {data}")
    data_send_to_callback = f"{data} -> Step 1 done"
    callback(data_send_to_callback)

def fetch_data_step2(data, callback):
    print(f"Step 2: Processing {data}")
    data_send_to_callback = f"{data} -> Step 2 done"
    callback(data_send_to_callback)

def fetch_data_step3(data, callback):
    print(f"Step 3: Processing {data}")
    data_send_to_callback = f"{data} -> Step 3 done"
    callback(data_send_to_callback)

# Callback functions
def handle_step1_result(step1_result):
    fetch_data_step2(step1_result, handle_step2_result)

def handle_step2_result(step2_result):
    fetch_data_step3(step2_result, handle_step3_result)

def handle_step3_result(step3_result):
    print(f"Final Result: {step3_result}")

# Start the process
fetch_data_step1("Initial data", handle_step1_result)

"""
[Output]:
    Step 1: Processing Initial data
    Step 2: Processing Initial data -> Step 1 done
    Step 3: Processing Initial data -> Step 1 done -> Step 2 done
    Final Result: Initial data -> Step 1 done -> Step 2 done -> Step 3 done
"""

While code it functional, this is hard to read and maintain due to the deeply nested structure. To fix this readability, use Promise

Promise Syntax -

function orderPizza() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('Order Delivered');
    }, 2000);
  });
}
orderPizza()
  .then((result) => console.log(result))
  .catch((error) => console.error(error));

Explaining Using Callback Hell Example -

function fetchDataStep1(data) {
    return new Promise((resolve) => {
        console.log(`Step 1: Processing ${data}`);
        resolve(`${data} -> Step 1 done`);
    });
}

function fetchDataStep2(data) {
    return new Promise((resolve) => {
        console.log(`Step 2: Processing ${data}`);
        resolve(`${data} -> Step 2 done`);
    });
}

function fetchDataStep3(data) {
    return new Promise((resolve) => {
        console.log(`Step 3: Processing ${data}`);
        resolve(`${data} -> Step 3 done`);
    });
}

// Chained Promises
fetchDataStep1("Initial data")
    .then(step1Result => fetchDataStep2(step1Result))
    .then(step2Result => fetchDataStep3(step2Result))
    .then(step3Result => {
        console.log(`Final Result: ${step3Result}`);
    });
/*
[output]:
Step 1: Processing Initial data
Step 2: Processing Initial data -> Step 1 done
Step 3: Processing Initial data -> Step 1 done -> Step 2 done
Final Result: Initial data -> Step 1 done -> Step 2 done -> Step 3 done
*/

Python Comparison

Python typically uses asyncio for async tasks:

import asyncio

async def order_pizza():
    await asyncio.sleep(2)
    return 'Order Delivered'

asyncio.run(order_pizza())

9. Error Handling

JavaScript Example

try {
  console.log(undeclaredVar);
} catch (error) {
  console.error('An error occurred:', error);
}

Python Comparison

try:
    print(undeclared_var)
except NameError as e:
    print(f'An error occurred: {e}')

10. Event Loop Basics

JavaScript Example

console.log('Start');
setTimeout(() => {
  console.log('Timeout');
}, 1000);
console.log('End');

Key Points:

  • JavaScript’s event loop handles asynchronous tasks.

  • Outputs: Start, End, Timeout.


Additional Quirks for Python Developers

  1. No built-in integer division (//); use Math.floor(a / b).

  2. undefined is similar to Python’s None.

  3. No elif; use else if.

  4. Semicolons are optional but recommended for consistency.

  5. Prototype-based inheritance instead of class-based.


Modern JavaScript Practices

  • Use const by default.

  • Prefer let over var.

  • Use arrow functions for concise syntax.

  • Embrace promises and async/await for asynchronous code.

  • Always use strict equality (===).