Chapter 4 variables, scope, and Memory

This is the 12th day of my participation in the August Challenge

Today we start to update the fourth chapter of JavaScript, today’s content is the copy problem, we usually in the use of JS, will encounter a value multi-purpose problem. So what do we do when we need this value to be different at different stages? How does deep copy and shallow copy work?

JavaScript variables are unique compared to other languages. As ecMA-262 dictates, JavaScript variables are loosely typed, and a variable is nothing more than the name of a particular value at a particular point in time. Since there are no rules defining what data types a variable must contain, its value and data type can change over the lifetime of the script. Such variables are interesting, powerful, and of course problematic. This chapter dissects the intricacies of variables.

4.1 Original and Reference Values

ECMAScript variables can contain two different types of data: raw and reference values. Primitive values are the simplest data, and reference values are objects made up of multiple values.

When assigning a value to a variable, the JavaScript engine must determine whether the value is a primitive or a reference value. The previous chapter discussed six primitive values: Undefined, Null, Boolean, Number, String, and Symbol. A variable that holds the original value is accessed by value because we are manipulating the actual value stored in the variable.

A reference value is an object held in memory. Unlike other languages, JavaScript does not allow direct access to memory locations, so you cannot directly manipulate the memory space in which an object resides. When you manipulate an object, you are actually manipulating a reference to the object rather than the actual object itself. To do this, the variable holding the reference value is accessed by reference.

Note that in many languages strings are represented as objects and are therefore considered reference types. ECMAScript breaks this convention.

4.1.1 Dynamic Properties

Raw and reference values are defined in a similar way, creating a variable and assigning a value to it. However, what can be done with the value after the variable holds it is quite different. For reference values, attributes and methods can be added, modified, and removed at any time. For example, look at the following example:

let person = new Object(); 
person.name = "Nicholas"; 
console.log(person.name); // "Nicholas" 
​
Copy the code

Here, you first create an object and store it in the variable Person. We then add a property named name to the object and assign the string “Nicholas” to the property. After that, the new property can be accessed until the object is destroyed or the property is explicitly deleted.

Raw values cannot have attributes, although attempts to add attributes to raw values do not generate an error. Such as:

let name = "Nicholas"; 
name.age = 27; 
console.log(name.age); // undefined 
Copy the code

Here, the code wants to define an age attribute for the string name and give it a value of 27. On the next line, the property is gone. Remember that only reference values can dynamically add attributes that can be used later.

Note that primitive types can be initialized using only the primitive literal form. If you use the new keyword, JavaScript creates an instance of type Object, but behaves like the original value. Let’s look at the differences between the two initializations:

let name1 = "Nicholas"; 
let name2 = new String("Matt"); 
name1.age = 27; 
name2.age = 26; 
console.log(name1.age); // undefined 
console.log(name2.age); // 26 
console.log(typeof name1); // string 
console.log(typeof name2); // object
Copy the code

4.1.2 duplicate values

In addition to being stored differently, the original and reference values differ when copied through variables. When assigning an original value from a variable to another variable, the original value is copied to the location of the new variable. Look at the following example:

let num1 = 5; 
let num2 = num1;
Copy the code

Here, num1 contains the value 5. When num2 is initialized to num1, num2 also gets the value 5. This value is completely separate from the 5 stored in num1 because it is a copy of that value.

These two variables can be used independently of each other. As shown below:

When a reference value is assigned from one variable to another, the value stored in the variable is copied to the location of the new variable. The difference here is that the copied value is actually a pointer to an object stored in heap memory. After the operation is complete, both variables actually refer to the same object, so changes on one object are reflected on the other, as shown in the following example:

let obj1 = new Object(); 
let obj2 = obj1; 
obj1.name = "Nicholas"; 
console.log(obj2.name); // "Nicholas"
Copy the code

In this example, the variable obj1 holds an instance of a new object. This value is then copied to obj2, where both variables point to the same object. After creating and assigning the property name to obj1, this property is also accessible through obj2, since they all point to the same object. The following figure shows the relationship between variables and objects in heap memory.

4.1.3 Passing Parameters

Arguments to all functions in ECMAScript are passed by value. This means that values outside the function are copied to arguments inside the function, just as they are copied from one variable to another. If it’s a primitive value, it’s just like copying the original value variable, if it’s a reference value, it’s just like copying the reference value variable. This can be confusing for many developers, since variables can be accessed by value and by reference, while passing parameters can only be passed by value.

When parameters are passed by value, the value is copied to a local variable (that is, a named parameter, or in ECMAScript parlance, a slot in the Arguments object). When a parameter is passed by reference, the location of the value in memory is kept in a local variable, which means that changes to local variables are reflected outside the function. (This is not possible in ECMAScript.) Consider the following example:

function addTen(num) { num += 10; return num; } let count = 20; let result = addTen(count); console.log(count); // 20, no change console.log(result); / / 30Copy the code

Here, the function addTen() takes an argument, num, which is actually a local variable. When called, the variable count is passed in as an argument. The value of count is 20, which is copied to the num argument for use inside addTen(). Inside the function, the value of num is added by 10, but this does not affect the original variable count outside the function. The argument num and the variable count do not interfere; they just happen to hold the same value. If num is passed by reference, the value of count is also changed to 30. This fact is obvious when using raw values such as values. However, if objects are passed in the variable, it is not so clear. For example, look at this example

function setName(obj) { 
 obj.name = "Nicholas"; 
} 
let person = new Object(); 
setName(person); 
console.log(person.name); // "Nicholas" 
Copy the code

This time, we create an object and store it in the variable Person. This object is then passed to the setName() method and copied into the argument obj. Inside the function, both obj and Person point to the same object. As a result, obJ accesses the object by reference even if it is passed into the function by value. When the name attribute is set inside a function to obj, objects outside the function also reflect the change, because the object obj points to is stored in global scoped heap memory. Many developers mistakenly believe that when an object is modified in a local scope and the change is reflected globally, it means that parameters are passed by reference. To prove that objects are passed by value, let’s look at the following modified example:

function setName(obj) { 
 obj.name = "Nicholas"; 
 obj = new Object(); 
 obj.name = "Greg"; 
} 
let person = new Object(); 
setName(person); 
console.log(person.name); // "Nicholas" 
​
Copy the code

The only change in this example is the addition of two lines of code in setName() to redefine obj as a new object with a different name. When person passes setName(), its name property is set to “Nicholas”. The variable obj is then set to a new object and the name property is set to “Greg”. If person is passed by reference, then person should automatically change the pointer to point to an object whose name is “Greg”. However, when we access Person.name again, it has a value of “Nicholas”, indicating that the original reference remains unchanged after the value of the parameter in the function changes. When obj is overridden inside a function, it becomes a pointer to a local object. The local object is destroyed at the end of the function execution.

Note that arguments to ECMAScript functions are local variables.

4.1.4 Determine the type

The Typeof operator mentioned in the previous chapter is best used to determine whether a variable is primitive. More specifically, it is the best way to determine whether a variable is a string, value, Boolean, or undefined. If the value is an object or NULL, then typeof returns “object”, as shown in the following example:

let s = "Nicholas"; 
let b = true; 
let i = 22; 
let u; 
let n = null; 
let o = new Object(); 
console.log(typeof s); // string 
console.log(typeof i); // number 
console.log(typeof b); // boolean 
console.log(typeof u); // undefined 
console.log(typeof n); // object 
console.log(typeof o); // object 
Copy the code

While typeof is useful for raw values, it is less useful for reference values. We usually don’t care if a value is an object, but rather what kind of object it is. To solve this problem, ECMAScript provides the instanceof operator as follows:

result = variable instanceof constructor
Copy the code

The instanceof operator returns true if the variable is an instanceof a given reference type (as determined by its stereotype chain, more on in chapter 8). Consider the following example:

console.log(person instanceof Object); // Is the person variable Object? console.log(colors instanceof Array); // Is the variable colors Array? console.log(pattern instanceof RegExp); // Is the variable pattern RegExp?Copy the code

By definition, all reference values are instances of Object, so detecting any reference values and Object constructors through the instanceof operator returns true. Similarly, if instanceof is used to detect the original value, false will always be returned because the original value is not an object.

Note that the Typeof operator also returns “function” when used to detect functions. When used to detect regular expressions in Safari (up to Safari 5) and Chrome (up to Chrome 7), Typeof also returns “function” for implementation details. Ecma-262 specifies that any object that implements an internal [[Call]] method should return “function” when typeOF is detected. Because the regular expressions in the above browser implement this method, typeof also returns “function” on regular expressions. In IE and Firefox, Typeof returns “object” for regular expressions.