Exploring The Dinamic of Mutability and Valuassignability in JavaScript Variables

Exploring The Dinamic of Mutability and Valuassignability in JavaScript Variables

JavaScript Essential Fundamentals - The Series

In the realm of JavaScript programming, the nuanced concepts of Mutability and Valuassignability play a pivotal role in shaping the behavior of variables. These characteristics influence how data is manipulated, shared, and updated within a program. As developers navigate the intricacies of web development and application design, a profound understanding of Mutability and Valuassignability becomes essential. This article delves into the significance of these concepts, exploring their impact on variable dynamics and their broader implications for effective JavaScript coding practices.

Mutability

In JavaScript, Mutability refers to the ability to modify the value of a variable without changing its memory reference. In other words, this is the ability to reassign a variable by reference.

In JavaScript, the Mutability feature is determined by the variable data type. All Primitive Data Types (Number, String, Boolean, Undefined, Null) are Immutable, while all Complex Data Types (Object, Array, Function, Date, RegExp) are Mutable.

In other words, this means that values of Primitive Data Types cannot be changed after creation within the same memory location.

It's important to note the fact that Primitive Data Types cannot be reassigned by reference doesn't automatically mean they are reassigned by value, as we will see shortly. However, for sure, the only way for them to be reassigned is by value.

Valuassignability

On the other hand, there is an ability without a name. The ability to reassign a variable by value. Let´s call it Valuassignability (value + reassign - e - re + ability). The derived neologisms are "to valuassign" and "valuassignable".

Therefore, Valuassignability implies the capability to reassign a variable's value by creating a new value in a fresh memory location and pointing its variable name to this new location.

Okay, We've just coined a term in JS, and logically, it is not widely used in the world of JavaScript. However, even if this term has never been used before, it exists in the code and know this concept will be crucial for us to understand how variable declarators and data types work.

One of the most valuable contributions of this neologism could be to prevent misunderstandings, such as assuming an object in JavaScript cannot be reassigned by value or confusing the term Immutable with the ability to reassign by value.

Actually, the Valuassignability feature is determined by the variable declaration type. The var and let declarator keywords define valuassignable variables (variables that are reasigned by value), while the const declaration defines unvaluassignable variables (variables that are not reasigned by value).

Hence, it is possible to have objects that are mutable and valuassignable, much like primitive variables that cannot be reassigned by value, as we will explore more thoroughly in the upcoming explanation.

This article won't delve into scope issues, despite their intrinsic importance in declaration keywords. Understanding scope is vital for making smart choices in variable declaration, and we'll cover it in a future article.

Valuassignability x Mutability

Considering all possible combinations between data types and declarator types in JavaScript, we have the following table that illustrates when a variable will demonstrate Valuassignability and/or Mutability.

As we can observe, we essentially have four groups of variables:

  1. Valuassignable and Immutable Group: Variables falling into the Valuassignable and Immutable Group are those formed by Primitive Data Types and declared using var or let keywords. In this group, the change of variable values is only possible through the creation of a new value in memory and by redirecting the variable's name to this new memory location.

     let myNumber = 10
     // Obtaining original value
     const originalValue = myNumber
     // Attempt to reassign the variable by value.
     myNumber = 20
     console.log(originalVariable === myNumber)
     // Output: "false"
    

    The code illustrates the "Valuassignable and Immutable Group" concept. The variable myNumber is assigned a value of 10, and its original value is stored in the constant originalValue. When attempting to reassign myNumber to 20, the original value remains unchanged, showcasing immutability. The comparison console.log(originalVariable === myNumber) confirms that the variables point to different values, aligning with Valuassignability for Primitive Data Types using var or let.

  2. Unvaluassignable and Immutable Group: On the other hand, the Unvaluassignable and Immutable Group consists of variables formed by Primitive Data Types and declared with const.

     // Declaration of a constant variable 'myConstVariable' with the value 10.
     const myConstVariable = 10 
     // Attempt to valuassign
     // This will result in an error: 
     //"TypeError: Assignment to constant variable."
     myConstVariable = 20 //comment this line after check Error. 
     // Attempt to modify the internal value (won't affect the constant)
     myConstVariable.prop = 'new value'
     console.log(myConstVariable)  
     // Output: 10
     console.log(typeof myConstVariable.prop)
     // Output: undefined
    

    The code exemplifies the "Unvaluassignable and Immutable Group" with a constant variable (myConstVariable) declared using const and assigned the value 10. Any attempt to reassign it (myConstVariable = 20) results in an error, highlighting its unvaluassignable nature. The output of typeof myConstVariable.prop being undefined confirms that modifications to internal properties are restricted, reinforcing the immutability aspect.

  3. Valuassignable and Mutable Group: In the Valuassignable and Mutable Group, you find variables composed of Complex Data Types and declared using var or let.

     // Creates an object and assigns it to the variable myObjectLet
     let myObjectLet = { key: 'value' };
     console.log("Original (let):", myObjectLet);
     // Creates another variable (myAnotherObjectLet) referencing 
     // the same object as myObjectLet
     let myAnotherObjectLet = myObjectLet;
     // Modifies the internal content of myObjectLet by changing
     //the value associated with the 'key'
     myObjectLet.key = 'newValue';
     console.log("Modified (let):", myObjectLet);
     // Valuassigns myObjectLet to a new object with a different key
     myObjectLet = { newKey: 'newValue' };
     console.log("Reassigned (let):", myObjectLet);
     // Outputs the value of myAnotherObjectLet, demonstrating 
     // that it still references the original object and 
     // myObjectLet is now pointing to another place in memory.
     console.log("myAnotherObjectLet (let):", myAnotherObjectLet);
     // Output:
     // Original (let): { key: 'value' }
     // Modified (let): { key: 'newValue' }
     // Reassigned (let): { newKey: 'newValue' }
     // myAnotherObjectLet (let): { key: 'newValue' }
    

    The code exemplifies the "Valuassignable and Mutable Group" by creating an object myObjectLet, modifying its internal content, and then reassigning it to a new object. The output confirms that another variable, myAnotherObjectLet, still references the original object, showcasing mutability. Simultaneously, the reassignment of myObjectLet to a new object demonstrates the Valuassignability of variables in this group, as it now points to a different place in memory.

  4. Unvaluassignable and Mutable Group: Lastly, the Unvaluassignable and Mutable Group comprises variables composed by Complex Data Types and const declarator.

     // Declaration of a constant with an object
     const myConstObject = { prop: 'value' }
     // Attempt to valuassign the variable.
     // This will result in an error: 
     //"TypeError: Assignment to constant variable."
     myConstObject = { newProp: 'newValue' } // Comment this line after Error.
     // Attempt to modify an internal property
     myConstObject.prop = 'newValue'
     console.log(myConstObject)  // Output: { prop: 'newValue' }
     // Attempt to valuassign an array to the variable
     // This will also result in an error: 
     //"TypeError: Assignment to constant variable."
     const myConstArray = [1, 2, 3] // Comment this line after check Error.
     myConstArray = [4, 5, 6]  // Attempt to valuassign 
     // Modifying internal elements (won't affect the variable reassignment)
     myConstArray[0] = 4
     console.log(myConstArray)  // Output: [4, 2, 3]
    

    The code illustrates the "Unvaluassignable and Mutable Group" where variables are composed of Complex Data Types and declared using const. It begins by declaring a constant myConstObject with an object. Attempts to reassign the variable, as seen in the lines commented for both object and array, result in errors, showcasing its unvaluassignable nature. However, the code allows modifications to internal properties and elements, exemplified by changing the internal property of myConstObject and modifying elements within myConstArray. This highlights the mutability of variables in this group, even though reassignment is restricted due to the constant declaration.

Best Practices

Based on the provided information, we can infer some best practices when declaring and choosing variable types in JavaScript:

  1. UseconstWhenever Possible for Primitive Variables:
    If you're unsure whether a variable storing a primitive data type will need Valuassignability, declare it using the const keyword. By using const, you start with the intention of making the variable immutable. This reflects the practice of defensive programming, where it's preferable to begin with the maximum restriction and relax it when necessary.

  2. UseconstWhenever Possible for Complex Variables:

    Rarely is it necessary to reassign the entire object, given its mutability and the ability to modify internal values by reference. The common approach is to maintain the object's structure and only modify specific attributes and methods rather than replacing the entire structure with new values. Therefore, when declaring objects that will remain unchanged throughout the program, use const. This practice enhances code clarity and guards against unintentional reassignment of values.

  3. Use primitive types whenever possible:

    For simple variables that store individual values, such as numbers, strings, and booleans, use primitive types. This is often more efficient in terms of memory consumption.

Warnings

As we explore best practices in wisely choosing keywords, it's crucial to consider aspects related to variable reassignment and scoping. Now, let's delve into some important warnings:

  1. Preferletfor Variables Needing Valuassignability

    If a variable needs to be reassigned by value throughout the program, use let, and in some cases, var. Prefer let for its more restricted block scope, which is more predictable and less error-prone than the broader function scope associated with var.

  2. Understand Scope Differences: Comprehend the scope differences between var, let, and const, and choose the appropriate declaration based on visibility and scope needs. We will cover this topic in our next article.

  3. Pay Attention to Mutable Types:

    If you're using mutable types, such as objects or arrays, be aware that they can be modified by reference. This can lead to unexpected side effects, especially if you pass these values to functions or modify internal properties.

After all the analysis above, we can conclude that in most cases and as a best practice, it is advisable to prefer the use of the following variable groups:

  • Valuassignable and Immutable Group (with let);

  • Unvaluassignable and Immutable Group;

  • Unvaluassignable and Mutable Group

Conclusion

This article unveiled the intricacies of mutability and valuassignability in the context of JavaScript programming, highlighting how these concepts directly influence the behavior of variables. By categorizing variables into distinct groups based on their characteristics, the importance of thoughtful choices was underscored, emphasizing the use of const to promote non-valuassignability and let in situations that require reassignment by value.

By adhering to the outlined best practices, developers can enhance the quality of their code, resulting in a more solid and comprehensible foundation. This refined understanding of variable dynamics in JavaScript not only contributes to the creation of robust code but also empowers programmers to make more informed and effective decisions during web development and software design.