Posted on

ES2023 (ECMAScript 2023) Features

ES2023 focused on minor improvements and consistency updates.

1. Array.prototype.toSorted(), toSpliced(), and toReversed()

  • Immutable versions of sort(), splice(), and reverse(), preventing in-place modifications.

Example:

const nums = [3, 1, 4];

console.log(nums.toSorted()); // ✅ [1, 3, 4] (original array remains unchanged)
console.log(nums.toReversed()); // ✅ [4, 1, 3]
console.log(nums.toSpliced(1, 1, 99)); // ✅ [3, 99, 4] (removes index 1, adds 99)

console.log(nums); // ✅ [3, 1, 4] (unchanged)

2. Array.prototype.findLast() and findLastIndex()

  • Similar to find() and findIndex(), but search from the end.

Example:

const arr = [1, 2, 3, 4, 5];

console.log(arr.findLast(n => n % 2 === 0)); // ✅ 4
console.log(arr.findLastIndex(n => n % 2 === 0)); // ✅ 3

3. RegExp.prototype.hasIndices

  • Checks if a regex was created with the /d flag.

Example:

const regex = /test/d;
console.log(regex.hasIndices); // ✅ true

4. Symbol.prototype.description Now Writable

  • The description property of Symbol objects can be modified.

Example:

const sym = Symbol("original");
console.log(sym.description); // ✅ "original"

5. WeakMap.prototype.emplace() and WeakSet.prototype.emplace() (Proposal)

  • A shortcut for setting values only if a key doesn’t already exist. (Not finalized but expected in future updates.)

Example:

const weakMap = new WeakMap();
weakMap.emplace({}, () => "newValue"); // ✅ Sets value only if key doesn’t exist

Summary of Features

FeatureES2022ES2023
Private fields/methods in classes
Static fields/methods in classes
Object.hasOwn()
RegExp /d flag (match indices)
Error.cause
Array.prototype.at()
Top-level await in modules
Array.prototype.toSorted(), toReversed(), toSpliced()
Array.prototype.findLast() and findLastIndex()
RegExp.prototype.hasIndices
Symbol.prototype.description writable
Posted on

ES2022 (ECMAScript 2022) Features

ES2022 introduced several improvements, including new class features, array and object enhancements, and top-level await.

1. Class Fields and Private Methods

  • Public and private fields (# prefix denotes private).
  • Private methods and accessors (# for methods and getters/setters).

Example:

class Person {
    name; // Public field
    #age; // Private field

    constructor(name, age) {
        this.name = name;
        this.#age = age;
    }

    #getAge() { // Private method
        return this.#age;
    }

    getInfo() {
        return `${this.name} is ${this.#getAge()} years old`;
    }
}

const alice = new Person("Alice", 25);
console.log(alice.getInfo()); // ✅ "Alice is 25 years old"
// console.log(alice.#age); // ❌ SyntaxError: Private field '#age' must be declared in an enclosing class

2. Static Class Fields and Methods

  • Classes can now define static fields and private static fields.

Example:

class Counter {
    static count = 0; // Public static field
    static #secret = 42; // Private static field

    static increment() {
        this.count++;
    }

    static getSecret() {
        return this.#secret;
    }
}

Counter.increment();
console.log(Counter.count); // ✅ 1
console.log(Counter.getSecret()); // ✅ 42

3. Object.hasOwn() (Finalized)

  • A safer alternative to Object.prototype.hasOwnProperty().

Example:

const obj = { a: 1 };
console.log(Object.hasOwn(obj, "a")); // ✅ true
console.log(Object.hasOwn(obj, "b")); // ✅ false

4. RegExp Match Indices (/d Flag)

  • Provides start and end positions of matches.

Example:

const regex = /hello/d;
const match = regex.exec("hello world");
console.log(match.indices[0]); // ✅ [0, 5] (start and end positions)

5. Error.cause Property

  • Allows errors to store their original cause.

Example:

try {
    throw new Error("Something went wrong", { cause: "Database connection failed" });
} catch (error) {
    console.log(error.message); // ✅ "Something went wrong"
    console.log(error.cause);   // ✅ "Database connection failed"
}

6. Array.prototype.at()

  • Allows negative indexing for arrays and strings.

Example:

const arr = [10, 20, 30];
console.log(arr.at(-1)); // ✅ 30 (last element)

7. Top-Level await in Modules

  • await can be used outside async functions in ES modules.

Example:

const data = await fetch("https://jsonplaceholder.typicode.com/todos/1").then(res => res.json());
console.log(data);

(Works in ES modules, not in CommonJS.)


Posted on

Summary of ES2021 feature

ES2021 (ECMAScript 2021) introduced several new features to JavaScript. Here are the key additions:

1. Numeric Separators (_)

  • Helps improve the readability of large numbers.
  • Example: const billion = 1_000_000_000; // Same as 1000000000 const bytes = 0xFF_FF_FF_FF; // Hexadecimal format

2. String replaceAll()

  • Adds a built-in way to replace all occurrences of a substring.
  • Example: const text = "hello world, world!"; console.log(text.replaceAll("world", "JS")); // Output: "hello JS, JS!"

3. Promise any()

  • Similar to Promise.race(), but resolves with the first fulfilled promise (ignores rejected ones).
  • If all promises reject, it throws an AggregateError.
  • Example:
  • const p1 = Promise.reject("Error 1"); const p2 = new Promise(resolve => setTimeout(resolve, 100, "Success!")); const p3 = Promise.reject("Error 2"); Promise.any([p1, p2, p3]).then(console.log).catch(console.error); // Output: "Success!"

4. WeakRefs and FinalizationRegistry

  • Allows for weak references to objects, preventing memory leaks in certain cases.
  • Used for caching and cleaning up resources.
  • Example:
  • let obj = { name: "Alice" }; const weakRef = new WeakRef(obj); obj = null;
  • // The object can now be garbage collected
  • const registry = new FinalizationRegistry((heldValue) => { console.log(`${heldValue} was garbage collected`); }); registry.register(weakRef.deref(), "Alice");

5. Logical Assignment Operators (&&=, ||=, ??=)

  • Shorter syntax for conditional assignments.
  • &&= (AND assignment): let x = true; x &&= false; // x becomes false
  • ||= (OR assignment): let y = null; y ||= "default"; // y becomes "default"
  • ??= (Nullish coalescing assignment): let z = undefined; z ??= "fallback"; // z becomes "fallback"

6. Object.hasOwn()

  • A safer alternative to Object.prototype.hasOwnProperty, avoiding prototype chain issues.
  • Example: const obj = { a: 1 }; console.log(Object.hasOwn(obj, "a")); // true console.log(Object.hasOwn(obj, "b")); // false

Summary of ES2021 Features:

FeatureDescription
Numeric Separators (_)Improves number readability
String.prototype.replaceAll()Replaces all occurrences of a substring
Promise.any()Resolves with the first fulfilled promise
WeakRefs & FinalizationRegistryEnables weak references for memory management
Logical Assignment Operators (&&=, `
Object.hasOwn()A safer alternative to hasOwnProperty

Posted on

What happens if func is an arrow function? Will this behave as expected?

In JavaScript, arrow functions behave differently from regular functions regarding the this keyword. Arrow functions inherit this from the surrounding lexical context (i.e., the context in which they are defined), rather than having their own this binding. This can affect how this behaves inside the debounced function when func is an arrow function.

Key Points:

  • Regular Functions: When func is a regular function, the this inside func depends on how the debounced function is called. Using func.apply(this, args) ensures that this is set to the context in which the debounced function was invoked.
  • Arrow Functions: If func is an arrow function, it ignores the this binding provided by apply or call and instead uses the this from its lexical scope (where it was defined). This means func.apply(this, args) won’t set this as expected for arrow functions.

Expected Behavior:

  • If func is a regular function: this will be correctly set to the context in which the debounced function is called.
  • If func is an arrow function: this will be the this from the scope where func was defined, not the context in which the debounced function is called.

Example:

javascript

const obj = {
    name: "Test",
    regularFunc: function() {
        console.log(this.name);
    },
    arrowFunc: () => {
        console.log(this.name);  <em>// 'this' is from the surrounding scope, not 'obj'</em>
    }
};

const debouncedRegular = debounce(obj.regularFunc, 500);
const debouncedArrow = debounce(obj.arrowFunc, 500);

debouncedRegular.call(obj);  <em>// Logs "Test" after 500ms</em>
debouncedArrow.call(obj);    <em>// Logs undefined or the global 'this' after 500ms</em>
  • For debouncedRegular, this is correctly set to obj.
  • For debouncedArrow, this is not bound to obj because arrow functions ignore call, apply, or bind.

Solution:

  • If func is an arrow function and you need this to refer to a specific context, ensure that the arrow function is defined in the correct scope, or consider using a regular function instead.

Can you modify your debounce function to include an option for immediate execution on the first call?

Yes, you can modify the debounce function to execute immediately on the first call and then debounce subsequent calls. This is often called “leading-edge debouncing.”

Modified Debounce Function with Immediate Option:

javascript

function debounce(func, delay, immediate = false) {
    let timeoutId;
    return function(...args) {
        const callNow = immediate && !timeoutId;
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => {
            timeoutId = null;
            if (!immediate) {
                func.apply(this, args);
            }
        }, delay);
        if (callNow) {
            func.apply(this, args);
        }
    };
}

How It Works:

  • Parameters:
    • func: The function to debounce.
    • delay: The time (in milliseconds) to wait before executing func.
    • immediate: A boolean indicating whether to execute func immediately on the first call (default: false).
  • Behavior:
    • If immediate is true, func is called immediately on the first invocation.
    • Subsequent calls within the delay period reset the timer, and func is not called again until the delay has passed without further invocations.
    • If immediate is false, it behaves like the original debounce (trailing-edge debouncing).

Example Usage:

javascript

const log = () => console.log("Executed");
const debouncedLog = debounce(log, 500, true);

<em>// First call: executes immediately</em>
debouncedLog();  <em>// "Executed"</em>

<em>// Subsequent calls within 500ms: no execution</em>
debouncedLog();

<em>// After 500ms of inactivity: no execution (since immediate is true)</em>

How would you implement throttling instead?

Throttling limits how often a function can be called, ensuring it’s executed at most once every specified time interval. Unlike debouncing, which waits for inactivity, throttling enforces a regular cadence.

Simple Throttling Function:

javascript

function throttle(func, limit) {
    let inThrottle;
    return function(...args) {
        if (!inThrottle) {
            func.apply(this, args);
            inThrottle = true;
            setTimeout(() => {
                inThrottle = false;
            }, limit);
        }
    };
}

How It Works:

  • Parameters:
    • func: The function to throttle.
    • limit: The minimum time (in milliseconds) between executions.
  • Behavior:
    • When the throttled function is called, it checks if inThrottle is false.
    • If inThrottle is false, it executes func immediately and sets inThrottle to true.
    • It then sets a timeout to reset inThrottle to false after limit milliseconds.
    • If the throttled function is called again while inThrottle is true, it does nothing.

Example Usage:

javascript

const log = () => console.log("Throttled execution");
const throttledLog = throttle(log, 1000);

<em>// Rapid calls</em>
throttledLog();  <em>// Executes immediately</em>
throttledLog();  <em>// Ignored</em>
<em>// After 1 second, the next call will execute</em>
setTimeout(throttledLog, 1000);  <em>// Executes after 1 second</em>

Key Differences from Debouncing:

  • Throttling: Executes the function at regular intervals, regardless of how often it’s triggered.
  • Debouncing: Delays execution until after a period of inactivity.

Both techniques are useful for optimizing performance in scenarios like handling user input, scrolling, or resizing events.

Posted on

What are closures in JavaScript?

A closure in JavaScript is a function that retains access to its lexical scope, even after the outer function in which it was defined has finished executing. This means the function can still access and manipulate variables from its containing scope, even though that scope is no longer active. Closures “close over” the variables they need from their outer scope, preserving them for as long as the closure exists.

This concept is fundamental in JavaScript and enables powerful patterns such as:

  • Data encapsulation
  • Private variables and methods
  • Maintaining state in asynchronous operations

Example of Using Closures in Projects

In my projects, I have used closures in several scenarios. Below are some examples:


1. Event Handlers

When attaching event listeners in a loop, especially in older JavaScript using var (which is function-scoped), closures were essential to capture the correct value for each iteration. Without closures, all event handlers would reference the final value of the loop variable. To solve this, I used Immediately Invoked Function Expressions (IIFEs) to create a closure for each iteration.

Example:

javascript

for (var i = 1; i <= 5; i++) {
    (function(index) {
        document.getElementById('button' + index).addEventListener('click', function() {
            console.log(index);
        });
    })(i);
}
  • In this example, each button (button1 to button5) has an event listener attached.
  • The IIFE creates a new scope for each iteration, and the inner event handler function forms a closure over the index parameter.
  • When a button is clicked, it logs its respective index (e.g., clicking button3 logs 3).

In modern JavaScript, using let (which is block-scoped) simplifies this, but the closure concept still applies.


2. Module Pattern for Encapsulation

Closures are often used to create modules with private variables and methods, exposing only the necessary functionality to the outside world. This mimics private members in object-oriented programming.

Example:

javascript

function createModule() {
    let privateVar = 'secret';
    function privateMethod() {
        console.log(privateVar);
    }
    return {
        publicMethod: function() {
            privateMethod();
        }
    };
}

const module = createModule();
module.publicMethod();  <em>// Logs 'secret'</em>
  • Here, createModule defines a private variable privateVar and a private function privateMethod.
  • The returned object contains publicMethod, which is a closure that retains access to privateVar and privateMethod.
  • Outside the module, privateVar and privateMethod are inaccessible, but publicMethod can still use them due to the closure.

This pattern is useful for encapsulating data and exposing only a controlled interface.


3. Asynchronous Code

Closures are crucial in asynchronous programming, such as when using setTimeout or working with promises. Callback functions often need to access variables from their outer scope, and closures make this possible.

Example:

javascript

function delayedLog(message) {
    setTimeout(function() {
        console.log(message);
    }, 1000);
}

delayedLog('Hello');  <em>// Logs 'Hello' after 1 second</em>
  • In this example, delayedLog defines a message parameter.
  • The callback function passed to setTimeout is a closure that remembers the message variable from its outer scope.
  • Even though delayedLog finishes executing immediately, the callback retains access to message and logs it after 1 second.

This pattern is common in asynchronous operations where callbacks need to maintain state.


Conclusion

Closures are a fundamental concept in JavaScript that allow functions to access variables from their lexical scope, even after the outer function has returned. They enable functional programming techniques, help manage scope, and maintain state in various scenarios, including:

  • Capturing values in event handlers
  • Creating encapsulated modules with private members
  • Preserving state in asynchronous code

By leveraging closures, JavaScript developers can write more modular, maintainable, and powerful code.


1. Can you give an example of how closures help with private variables in JavaScript?

Closures are a powerful mechanism in JavaScript for creating private variables, enabling encapsulation—a way to hide data and control access to it. This is achieved because a function retains access to the variables in its outer scope even after that outer function has finished executing. By returning a function (or an object containing functions) that “closes over” these variables, you can expose specific behaviors while keeping the variables themselves inaccessible from the outside.

Here’s an example of using closures to implement a counter with private variables:

javascript

function createCounter() {
    let count = 0; <em>// Private variable</em>

    return {
        increment: function() {
            count++;
            console.log(count);
        },
        decrement: function() {
            count--;
            console.log(count);
        },
        getCount: function() {
            return count;
        }
    };
}

const counter = createCounter();
counter.increment();  <em>// Output: 1</em>
counter.increment();  <em>// Output: 2</em>
counter.decrement();  <em>// Output: 1</em>
console.log(counter.getCount());  <em>// Output: 1</em>
console.log(counter.count);  <em>// Output: undefined</em>

How it works:

  • The createCounter function defines a variable count, which is private because it’s only accessible within the scope of createCounter.
  • It returns an object with three methods (increment, decrement, and getCount), each of which is a closure that retains access to count.
  • Outside the createCounter function, you cannot directly access or modify count (e.g., counter.count is undefined). Instead, you must use the provided methods, enforcing controlled access to the private variable.
  • This mimics the behavior of private members in object-oriented programming, where data is hidden and only accessible through designated interfaces.

This pattern is widely used for data privacy and encapsulation in JavaScript.


2. How do closures impact memory usage, and what potential issues can they cause?

Closures impact memory usage because they maintain references to variables in their outer scope, preventing those variables from being garbage collected as long as the closure exists. While this is what makes closures powerful, it can also lead to increased memory consumption and potential issues if not handled carefully.

Impact on Memory

  • When a closure is created, it “captures” the entire lexical environment of its outer scope—not just the variables it uses, but all variables available in that scope. These captured variables remain in memory as long as the closure is alive, even if the outer function has finished executing.
  • For example, if a closure captures a large object or array, that object or array will persist in memory until the closure itself is no longer referenced.

Potential Issues

  1. Memory Leaks
    • If a closure is unintentionally kept alive (e.g., attached to an event listener that’s never removed), the variables it captures cannot be garbage collected, leading to memory leaks.
    • Example: An event listener with a closure capturing a large dataset will keep that dataset in memory until the listener is removed, even if the dataset is no longer needed elsewhere.
  2. Unintended Variable Retention
    • Closures capture all variables in their outer scope, not just the ones they need. This can result in memory being allocated to unused variables.
    • Example: If a function defines multiple large variables but the closure only needs one, all of them are retained, wasting memory.
  3. Performance Overhead
    • In scenarios with many closures (e.g., created in loops or recursive functions), the cumulative memory and processing overhead can degrade performance, especially in resource-constrained environments.

Mitigation Strategies

  • Limit Captured Variables: Reduce the scope of variables captured by closures by passing only what’s needed as arguments instead of relying on the outer scope.
  • Clean Up Closures: Release closures when they’re no longer needed, such as removing event listeners with removeEventListener.
  • Use Weak References: Leverage WeakMap or WeakSet to allow garbage collection of objects even if they’re referenced by a closure, where applicable.

By being mindful of these factors, you can harness the benefits of closures while minimizing their downsides.


3. Can closures be used in event listeners? If so, how?

Yes, closures are commonly used in event listeners in JavaScript. Event listeners often need to access variables from their surrounding scope when an event occurs, and closures make this possible by preserving that scope even after the outer function has executed.

Here’s an example of using closures with event listeners:

javascript

function setupButton(index) {
    const button = document.getElementById(`button${index}`);
    button.addEventListener('click', function() {
        console.log(`Button ${index} was clicked`);
    });
}

<em>// Assume buttons with IDs "button1", "button2", "button3" exist in the HTML</em>
for (let i = 1; i <= 3; i++) {
    setupButton(i);
}

How it works:

  • The setupButton function takes an index parameter and attaches an event listener to a button with the corresponding ID (e.g., button1).
  • The event listener’s callback is a closure that captures the index variable from the setupButton scope.
  • When a button is clicked, the closure executes and logs the correct message (e.g., “Button 1 was clicked”).
  • The use of let in the for loop ensures each iteration has its own block scope, so each closure captures a unique index. (In older JavaScript with var, you’d need an IIFE to achieve this.)

Alternative with IIFE (for older JavaScript)

If you were using var instead of let, the closure would capture the same i value across iterations due to var’s function scope. Here’s how to fix it with an Immediately Invoked Function Expression (IIFE):

javascript

for (var i = 1; i <= 3; i++) {
    (function(index) {
        const button = document.getElementById(`button${index}`);
        button.addEventListener('click', function() {
            console.log(`Button ${index} was clicked`);
        });
    })(i);
}
  • The IIFE creates a new scope for each iteration, passing the current value of i as index, which the closure then captures.

Why Closures are Useful Here

  • Closures allow event handlers to “remember” their context, such as the index of a button or other configuration data, making them dynamic and reusable.
  • They enable you to write concise, context-aware code without relying on global variables.

Potential Pitfall

  • If an event listener’s closure captures large objects and the listener isn’t removed (e.g., when the element is removed from the DOM), it can cause memory leaks. To avoid this, use removeEventListener when the listener is no longer needed.

Conclusion

  • Private Variables: Closures enable encapsulation by allowing controlled access to variables while keeping them hidden from the outside world, as seen in the counter example.
  • Memory Usage: Closures increase memory usage by retaining outer scope variables, potentially causing leaks or performance issues if not managed properly.
  • Event Listeners: Closures are a natural fit for event listeners, preserving context and enabling dynamic behavior, though care must be taken to avoid memory pitfalls.

Understanding these applications and implications of closures will help you write more effective and efficient JavaScript code!

Posted on

JavaScript – let, const, var

In JavaScript, var, let, and const are three different ways to declare variables, each with distinct behaviors regarding scope, redeclaration, updating, and hoisting. Here’s a detailed explanation of their differences and when to use each:


var

  • Scope:
    var is function-scoped, meaning it is accessible throughout the function in which it is declared. If declared outside any function, it becomes globally scoped.javascriptfunction example() { var x = 10; if (true) { var x = 20; <em>// Same variable, redeclared</em> } console.log(x); <em>// 20</em> }Unlike block-scoping, var ignores block boundaries like {} unless they are part of a function.
  • Redeclaration:
    You can redeclare a var variable within the same scope without errors.javascriptvar a = 5; var a = 10; <em>// No error</em>
  • Updating:
    The value of a var variable can be updated anytime.javascriptvar b = 1; b = 2; <em>// Allowed</em>
  • Hoisting:
    Variables declared with var are hoisted to the top of their scope and initialized with undefined. This means you can use them before their declaration, though their value will be undefined until assigned.javascriptconsole.log(c); <em>// undefined</em> var c = 3;
  • When to Use:
    Use var only when working with older JavaScript code (pre-ES6) or if you specifically need function-scoping. In modern JavaScript, var is generally avoided because its loose scoping rules can lead to bugs, such as unintentional variable overwrites.

let

  • Scope:
    let is block-scoped, meaning it is only accessible within the block (e.g., {}) where it is declared, such as inside loops or if statements.javascriptif (true) { let y = 5; console.log(y); <em>// 5</em> } console.log(y); <em>// ReferenceError: y is not defined</em>
  • Redeclaration:
    You cannot redeclare a let variable within the same scope.javascriptlet z = 10; let z = 20; <em>// SyntaxError: Identifier 'z' has already been declared</em>
  • Updating:
    You can update the value of a let variable after declaration.javascriptlet w = 15; w = 25; <em>// Allowed</em>
  • Hoisting:
    let variables are hoisted to the top of their block but are not initialized. Attempting to use them before declaration results in a ReferenceError (this is known as the “temporal dead zone”).javascriptconsole.log(d); <em>// ReferenceError: Cannot access 'd' before initialization</em> let d = 4;
  • When to Use:
    Use let when you need a variable whose value will change over time, such as a loop counter or a value that will be reassigned.javascriptfor (let i = 0; i < 3; i++) { console.log(i); <em>// 0, 1, 2</em> } console.log(i); <em>// ReferenceError: i is not defined</em>

const

  • Scope:
    Like let, const is block-scoped and is only accessible within the block where it is declared.javascriptif (true) { const k = 100; console.log(k); <em>// 100</em> } console.log(k); <em>// ReferenceError: k is not defined</em>
  • Redeclaration:
    You cannot redeclare a const variable in the same scope.javascriptconst m = 50; const m = 60; <em>// SyntaxError: Identifier 'm' has already been declared</em>
  • Updating:
    You cannot reassign a const variable after its initial assignment. However, if the variable holds an object or array, you can modify its properties or elements (because the reference remains constant, not the content).javascriptconst n = 30; n = 40; <em>// TypeError: Assignment to constant variable</em> const obj = { value: 1 }; obj.value = 2; <em>// Allowed</em> console.log(obj.value); <em>// 2</em> obj = { value: 3 }; <em>// TypeError: Assignment to constant variable</em>
  • Hoisting:
    Like let, const is hoisted but not initialized, so it cannot be accessed before declaration.javascriptconsole.log(e); <em>// ReferenceError: Cannot access 'e' before initialization</em> const e = 5;
  • When to Use:
    Use const for variables that should not be reassigned after initialization, such as constants, configuration values, or references to objects/arrays whose structure might change but whose reference should remain fixed.javascriptconst PI = 3.14159; const settings = { theme: "dark" }; settings.theme = "light"; <em>// Allowed</em>

Key Differences at a Glance

Featurevarletconst
ScopeFunction-scopedBlock-scopedBlock-scoped
RedeclarationAllowedNot allowedNot allowed
UpdatingAllowedAllowedNot allowed (except object/array contents)
HoistingHoisted, initialized with undefinedHoisted, not initializedHoisted, not initialized

When to Use Each

  • var:
    Use sparingly, typically only in legacy code or when you intentionally need function-scoping. Modern JavaScript favors let and const for better predictability.
  • let:
    Use when you need a variable that will be reassigned, such as in loops or when tracking changing state.javascriptlet count = 0; count += 1; <em>// Valid use case</em>
  • const:
    Use by default for variables that won’t be reassigned. This improves code readability and prevents accidental reassignments. It’s especially useful for constants or fixed references.javascriptconst MAX_USERS = 100; const userData = []; userData.push({ name: "Alice" }); <em>// Valid</em>

Best Practices

  • Prefer let and const over var because block-scoping reduces the risk of scope-related bugs.
  • Use const as your default choice to signal intent and prevent unintended reassignments. Switch to let only when reassignment is explicitly required.
  • Avoid var in modern JavaScript unless you have a specific reason tied to its function-scoping behavior.

By understanding these differences and applying these guidelines, you can write cleaner, more maintainable JavaScript code.

Posted on

nodejs and express, my first app

So I was working on my ubuntu servers this weekend.

Trying to get some extra work in. Work that shouldn’t feel like work. I was thinking about how natural/intuitive asynchronous event driven applications design might be with a language like JavaScript. I was sort of redesigning the BPM we worked on at Disney in my head.

Defacto server side engine for JS right now is node, so with a few commands I had my first web app launched.


sudo apt-get install nodejs
sudo apt-get install npm
npm install -g express
express --sessions --css stylus --ejs myapp
cd myapp && npm install
node app

So if you point your browser at http://localhost:3000/ you will see the hello word app.

Now one of the first things I noticed is there is a templating system at work here called EJS.
The good news is that, at first glance, it looks pretty much like PHP!

/routes/index.js

/*
 * GET home page.
 */

exports.index = function(req, res){
  res.render('index', { title: 'Express' });
};

/views/index.ejs

<title> <%= title %> </title>
<ul>
<% for(var i=0; i<supplies.length; i++) {%>
   <li><%= supplies[i] %></li>
<% } %>
</ul>

Okay so the templating system looks easy enough, next thing is I need a way to do job scheduling functionality. Basically need a nodejs clone of the quartz job scheduler in grails.

I did a quick google and saw there is a github project for this.
And this pretty much where I ran out of steam in my investigation.
And I concluded that “I will look into https://github.com/ncb000gt/node-cron later.”

Honestly, I think a technology solution presented on this platform can be rather elagent!
I’ve been pushing unified front and back end language and synchronization for long time in my casual conversations here and there. None of my employers have had a project like this for me in the past.

If I can find a decent IDE, I just may try something out.