Chapter 4

Variables, Scope, and Memory

WHAT’S IN THIS CHAPTER?

  • Working with primitive and reference values in variables
  • Understanding execution context
  • Understanding garbage collection

The nature of variables in JavaScript, as defined in ECMA-262, is quite unique compared to that of other languages. Being loosely typed, a variable is literally just a name for a particular value at a particular time. Because there are no rules defining the type of data that a variable must hold, a variable’s value and data type can change during the lifetime of a script. Though this is an interesting, powerful, and problematic feature, there are many more complexities related to variables.

PRIMITIVE AND REFERENCE VALUES

ECMAScript variables may contain two different types of data: primitive values and reference values. Primitive values are simple atomic pieces of data, while reference values are objects that may be made up of multiple values.

When a value is assigned to a variable, the JavaScript engine must determine if it’s a primitive or a reference. The five primitive types were discussed in the previous chapter: Undefined, Null, Boolean, Number, and String. These variables are said to be accessed by value, because you are manipulating the actual value stored in the variable.

Reference values are objects stored in memory. Unlike other languages, JavaScript does not permit direct access of memory locations, so direct manipulation of the object’s memory space is not allowed. When you manipulate an object, you’re really working on a reference to that object rather than the actual object itself. For this reason, such values are said to be accessed by reference.

image

In many languages, strings are represented by objects and are therefore considered to be reference types. ECMAScript breaks away from this tradition.

Dynamic Properties

Primitive and reference values are defined similarly: a variable is created and assigned a value. What you can do with those values once they’re stored in a variable, however, is quite different. When you work with reference values, you can add, change, or delete properties and methods at any time. Consider this example:

image
var person = new Object();
person.name = "Nicholas";
alert(person.name);    //"Nicholas"

DynamicPropertiesExample01.htm

Here, an object is created and stored in the variable person. Next, a property called name is added and assigned the string value of "Nicholas". The new property is then accessible from that point on, until the object is destroyed or the property is explicitly removed.

Primitive values can’t have properties added to them even though attempting to do so won’t cause an error. Here’s an example:

var name = "Nicholas";
name.age = 27;
alert(name.age);    //undefined

DynamicPropertiesExample02.htm

Here, a property called age is defined on the string name and assigned a value of 27. On the very next line, however, the property is gone. Only reference values can have properties defined dynamically for later use.

Copying Values

Aside from differences in how they are stored, primitive and reference values act differently when copied from one variable to another. When a primitive value is assigned from one variable to another, the value stored on the variable object is created and copied into the location for the new variable. Consider the following example:

var num1 = 5;
var num2 = num1;

Here, num1 contains the value of 5. When num2 is initialized to num1, it also gets the value of 5. This value is completely separate from the one that is stored in num1, because it’s a copy of that value. Each of these variables can now be used separately with no side effects. This process is diagrammed in Figure 4-1.

When a reference value is assigned from one variable to another, the value stored on the variable object is also copied into the location for the new variable. The difference is that this value is actually a pointer to an object stored on the heap. Once the operation is complete, two variables point to exactly the same object, so changes to one are reflected on the other, as in the following example:

var obj1 = new Object();
var obj2 = obj1;
obj1.name = "Nicholas";
alert(obj2.name);    //"Nicholas"

In this example, the variable obj1 is filled with a new instance of an object. This value is then copied into obj2, meaning that both variables are now pointing to the same object. When the property name is set on obj1, it can later be accessed from obj2 because they both point to the same object. Figure 4-2 shows the relationship between the variables on the variable object and the object on the heap.

Argument Passing

All function arguments in ECMAScript are passed by value. This means that the value outside of the function is copied into an argument on the inside of the function the same way a value is copied from one variable to another. If the value is primitive, then it acts just like a primitive variable copy, and if the value is a reference, it acts just like a reference variable copy. This is often a point of confusion for developers, because variables are accessed both by value and by reference, but arguments are passed only by value.

When an argument is passed by value, the value is copied into a local variable (a named argument and, in ECMAScript, a slot in the arguments object). When an argument is passed by reference, the location of the value in memory is stored into a local variable, which means that changes to the local variable are reflected outside of the function. (This is not possible in ECMAScript.) Consider the following example:

image
function addTen(num) {
    num += 10;
    return num;
}
                   
var count = 20;
var result = addTen(count);
alert(count);    //20 - no change
alert(result);   //30

FunctionArgumentsExample01.htm

Here, the function addTen() has an argument num, which is essentially a local variable. When called, the variable count is passed in as an argument. This variable has a value of 20, which is copied into the argument num for use inside of addTen(). Within the function, the argument num has its value changed by adding 10, but this doesn’t change the original variable count that exists outside of the function. The argument num and the variable count do not recognize each other; they only happen to have the same value. If num had been passed by reference, then the value of count would have changed to 30 to reflect the change made inside the function. This fact is obvious when using primitive values such as numbers, but things aren’t as clear when using objects. Take this for example:

function setName(obj) {
    obj.name = "Nicholas";
}
                   
var person = new Object();
setName(person);
alert(person.name);    //"Nicholas"

FunctionArgumentsExample02.htm

In this code, an object is created and stored in the variable person. This object is then passed into the setName() method, where it is copied into obj. Inside the function, obj and person both point to the same object. The result is that obj is accessing an object by reference, even though it was passed into the function by value. When the name property is set on obj inside the function, this change is reflected outside the function, because the object that it points to exists globally on the heap. Many developers incorrectly assume that when a local change to an object is reflected globally, that means an argument was passed by reference. To prove that objects are passed by value, consider the following modified code:

function setName(obj) {
    obj.name = "Nicholas";
    obj = new Object();
    obj.name = "Greg";
}
                   
var person = new Object();
setName(person);
alert(person.name);    //"Nicholas"

The only change between this example and the previous one are two lines added to setName() that redefine obj as a new object with a different name. When person is passed into setName(), its name property is set to "Nicholas". Then the variable obj is set to be a new object and its name property is set to "Greg". If person were passed by reference, then person would automatically be changed to point to the object whose name is "Greg". However, when person.name is accessed again, its value is "Nicholas", indicating that the original reference remained intact even though the argument’s value changed inside the function. When obj is overwritten inside the function, it becomes a pointer to a local object. That local object is destroyed as soon as the function finishes executing.

image

Think of function arguments in ECMAScript as nothing more than local variables.

Determining Type

The typeof operator, introduced in the previous chapter, is the best way to determine if a variable is a primitive type. More specifically, it’s the best way to determine if a variable is a string, number, Boolean, or undefined. If the value is an object or null, then typeof returns "object", as in this example:

image
var s = "Nicholas";
var b = true;
var i = 22;
var u;
var n = null;
var o = new Object();
                   
alert(typeof s);   //string
alert(typeof i);   //number
alert(typeof b);   //boolean
alert(typeof u);   //undefined
alert(typeof n);   //object
alert(typeof o);   //object

DeterminingTypeExample01.htm

Although typeof works well for primitive values, it’s of little use for reference values. Typically, you don’t care that a value is an object — what you really want to know is what type of object it is. To aid in this identification, ECMAScript provides the instanceof operator, which is used with the following syntax:

result = variable instanceof constructor

The instanceof operator returns true if the variable is an instance of the given reference type (identified by its prototype chain, as discussed in Chapter 6). Consider this example:

alert(person instanceof Object);   //is the variable person an Object?
alert(colors instanceof Array);    //is the variable colors an Array?
alert(pattern instanceof RegExp);  //is the variable pattern a RegExp?

All reference values, by definition, are instances of Object, so the instanceof operator always returns true when used with a reference value and the Object constructor. Similarly, if instanceof is used with a primitive value, it will always return false, because primitives aren’t objects.

image

The typeof operator also returns “function” when used on a function. When used on a regular expression in Safari (through version 5) and Chrome (through version 7), typeof returns “function” because of an implementation detail. ECMA-262 specifies that any object implementing the internal [[Call]] method should return “function” from typeof. Since regular expressions implement this method in these browsers, typeof returns “function”. In Internet Explorer and Firefox, typeof returns “object” for regular expressions.

EXECUTION CONTEXT AND SCOPE

The concept of execution context, referred to as context for simplicity, is of the utmost importance in JavaScript. The execution context of a variable or function defines what other data it has access to, as well as how it should behave. Each execution context has an associated variable object upon which all of its defined variables and functions exist. This object is not accessible by code but is used behind the scenes to handle data.

The global execution context is the outermost one. Depending on the host environment for an ECMAScript implementation, the object representing this context may differ. In web browsers, the global context is said to be that of the window object (discussed in Chapter 8), so all global variables and functions are created as properties and methods on the window object. When an execution context has executed all of its code, it is destroyed, taking with it all of the variables and functions defined within it (the global context isn’t destroyed until the application exits, such as when a web page is closed or a web browser is shut down).

Each function call has its own execution context. Whenever code execution flows into a function, the function’s context is pushed onto a context stack. After the function has finished executing, the stack is popped, returning control to the previously executing context. This facility controls execution flow throughout an ECMAScript program.

When code is executed in a context, a scope chain of variable objects is created. The purpose of the scope chain is to provide ordered access to all variables and functions that an execution context has access to. The front of the scope chain is always the variable object of the context whose code is executing. If the context is a function, then the activation object is used as the variable object. An activation object starts with a single defined variable called arguments. (This doesn’t exist for the global context.) The next variable object in the chain is from the containing context, and the next after that is from the next containing context. This pattern continues until the global context is reached; the global context’s variable object is always the last of the scope chain.

Identifiers are resolved by navigating the scope chain in search of the identifier name. The search always begins at the front of the chain and proceeds to the back until the identifier is found. (If the identifier isn’t found, typically an error occurs.)

Consider the following code:

image
var color = "blue";
                   
function changeColor(){
    if (color === "blue"){
        color = "red";
    } else {
        color = "blue";
    }
}
                   
changeColor();

ExecutionContextExample01.htm

In this simple example, the function changeColor() has a scope chain with two objects in it: its own variable object (upon which the arguments object is defined) and the global context’s variable object. The variable color is therefore accessible inside the function, because it can be found in the scope chain.

Additionally, locally defined variables can be used interchangeably with global variables in a local context. Here’s an example:

var color = "blue";
                   
function changeColor(){
    var anotherColor = "red";
                   
    function swapColors(){
        var tempColor = anotherColor;
        anotherColor = color;
        color = tempColor;
        
        //color, anotherColor, and tempColor are all accessible here
    }
                   
    //color and anotherColor are accessible here, but not tempColor        
    swapColors();
}
                   
//only color is accessible here
changeColor();

There are three execution contexts in this code: global context, the local context of changeColor(), and the local context of swapColors(). The global context has one variable, color, and one function, changeColor(). The local context of changeColor() has one variable named anotherColor and one function named swapColors(), but it can also access the variable color from the global context. The local context of swapColors() has one variable, named tempColor, that is accessible only within that context. Neither the global context nor the local context of swapColors() has access to tempColor. Within swapColors(), though, the variables of the other two contexts are fully accessible, because they are parent execution contexts. Figure 4-3 represents the scope chain for the previous example.

In this figure, the rectangles represent specific execution contexts. An inner context can access everything from all outer contexts through the scope chain, but the outer contexts cannot access anything within an inner context. The connection between the contexts is linear and ordered. Each context can search up the scope chain for variables and functions, but no context can search down the scope chain into another execution context. There are three objects in the scope chain for the local context of swapColors(): the swapColors() variable object, the variable object from changeColor(), and the global variable object. The local context of swapColors() begins its search for variable and function names in its own variable object before moving along the chain. The scope chain for the changeColor() context has only two objects: its own variable object and the global variable object. This means that it cannot access the context of swapColors().

image

Function arguments are considered to be variables and follow the same access rules as any other variable in the execution context.

Scope Chain Augmentation

Even though there are only two primary types of execution contexts, global and function (the third exists inside of a call to eval()), there are other ways to augment the scope chain. Certain statements cause a temporary addition to the front of the scope chain that is later removed after code execution. There are two times when this occurs, specifically when execution enters either of the following:

  • The catch block in a try-catch statement
  • A with statement

Both of these statements add a variable object to the front of the scope chain. For the with statement, the specified object is added to the scope chain; for the catch statement, a new variable object is created and contains a declaration for the thrown error object. Consider the following:

image
function buildUrl() {
    var qs = "?debug=true";
                   
    with(location){
        var url = href + qs;        
    }
                   
    return url;
}

ExecutionContextExample03.htm

In this example, the with statement is acting on the location object, so location itself is added to the front of the scope chain. There is one variable, qs, defined in the buildUrl() function. When the variable href is referenced, it’s actually referring to location.href, which is in its own variable object. When the variable qs is referenced, it’s referring to the variable defined in buildUrl(), which is in the function context’s variable object. Inside the with statement is a variable declaration for url, which becomes part of the function’s context and can, therefore, be returned as the function value.

image

There is a deviation in the Internet Explorer implementation of JavaScript through Internet Explorer 8, where the error caught in a catch statement is added to the execution context’s variable object rather than the catch statement’s variable object, making it accessible even outside the catchblock. This was fixed in Internet Explorer 9.

No Block-Level Scopes

JavaScript’s lack of block-level scopes is a common source of confusion. In other C-like languages, code blocks enclosed by brackets have their own scope (more accurately described as their own execution context in ECMAScript), allowing conditional definition of variables. For example, the following code may not act as expected:

if (true) {
    var color = "blue";
}
                   
alert(color);    //"blue"

Here, the variable color is defined inside an if statement. In languages such as C, C++, and Java, that variable would be destroyed after the if statement is executed. In JavaScript, however, the variable declaration adds a variable into the current execution context (the global context, in this case). This is important to keep in mind when dealing with the for statement, which is typically written like this:

for (var i=0; i < 10; i++){
    doSomething(i);
}
                   
alert(i);    //10

In languages with block-level scoping, the initialization part of the for statement defines variables that exist only within the context of the loop. In JavaScript, the i variable is created by the for statement and continues to exist outside the loop after the statement executes.

Variable Declaration

When a variable is declared using var, it is automatically added to the most immediate context available. In a function, the most immediate one is the function’s local context; in a with statement, the most immediate is the function context. If a variable is initialized without first being declared, it gets added to the global context automatically, as in this example:

image
function add(num1, num2) {
    var sum = num1 + num2;
    return sum;
}
                   
var result = add(10, 20);  //30
alert(sum);                //causes an error since sum is not a valid variable

ExecutionContextExample04.htm

Here, the function add() defines a local variable named sum that contains the result of an addition operation. This value is returned as the function value, but the variable sum isn’t accessible outside the function. If the var keyword is omitted from this example, sum becomes accessible after add() has been called, as shown here:

function add(num1, num2) {
    sum = num1 + num2;
    return sum;
}
                   
var result = add(10, 20);  //30
alert(sum);                //30

ExecutionContextExample05.htm

Here, the variable sum is initialized to a value without ever having been declared using var. When add() is called, sum is created in the global context and continues to exist even after the function has completed, allowing you to access it later.

image

Initializing variables without declaring them is a very common mistake in JavaScript programming and can lead to errors. It’s advisable to always declare variables before initializing them to avoid such issues. In strict mode, initializing variables without declaration causes an error.

Identifier Lookup

When an identifier is referenced for either reading or writing within a particular context, a search must take place to determine what identifier it represents. The search starts at the front of the scope chain, looking for an identifier with the given name. If it finds that identifier name in the local context, then the search stops and the variable is set; if the search doesn’t find the variable name, it continues along the scope chain (note that objects in the scope chain also have a prototype chain, so searching may include each object’s prototype chain). This process continues until the search reaches the global context’s variable object. If the identifier isn’t found there, it hasn’t been declared.

To better illustrate how identifier lookup occurs, consider the following example:

image
var color = "blue";
                   
function getColor(){
    return color;
}
                   
alert(getColor());  //"blue"

ExecutionContextExample06.htm

When the function getColor() is called in this example, the variable color is referenced. At that point, a two-step search begins. First getColor()’s variable object is searched for an identifier named color. When it isn’t found, the search goes to the next variable object (from the global context) and then searches for an identifier named color. Because that variable object is where the variable is defined, the search ends. Figure 4-4 illustrates this search process.

Given this search process, referencing local variables automatically stops the search from going into another variable object. This means that identifiers in a parent context cannot be referenced if an identifier in the local context has the same name, as in this example:

var color = "blue";
                   
function getColor(){
    var color = "red";
    return color;
}
                   
alert(getColor());  //"red"

ExecutionContextExample07.htm

In this modified code, a local variable named color is declared inside the getColor() function. When the function is called, the variable is declared. When the second line of the function is executed, it knows that a variable named color must be used. The search begins in the local context, where it finds a variable named color with a value of "red". Because the variable was found, the search stops and the local variable is used, meaning that the function returns "red". Any lines of code appearing after the declaration of color as a local variable cannot access the global color variable without qualifying it as window.color. If one of the operands is an object and the other is not, the valueOf() method is called on the object to retrieve a primitive value to compare according to the previous rules.

image

Variable lookup doesn’t come without a price. It’s faster to access local variables than global variables because there’s no search up the scope chain. JavaScript engines are getting better at optimizing identifier lookup, though, so this difference may end up negligible in the future.

GARBAGE COLLECTION

JavaScript is a garbage-collected language, meaning that the execution environment is responsible for managing the memory required during code execution. In languages like C and C++, keeping track of memory usage is a principle concern and the source of many issues for developers. JavaScript frees developers from worrying about memory management by automatically allocating what is needed and reclaiming memory that is no longer being used. The basic idea is simple: figure out which variables aren’t going to be used and free the memory associated with them. This process is periodic, with the garbage collector running at specified intervals (or at predefined collection moments in code execution).

Consider the normal life cycle of a local variable in a function. The variable comes into existence during the execution of the function. At that time, memory is allocated on the stack (and possibly on the heap) to provide storage space for the value. The variable is used inside the function and then the function ends. At that point this variable is no longer needed, so its memory can be reclaimed for later use. In this situation, it’s obvious that the variable isn’t needed, but not all situations are as obvious. The garbage collector must keep track of which variables can and can’t be used so it can identify likely candidates for memory reclamation. The strategy for identifying the unused variables may differ on an implementation basis, though two strategies have traditionally been used in browsers.

Mark-and-Sweep

The most popular form of garbage collection for JavaScript is called mark-and-sweep. When a variable comes into context, such as when a variable is declared inside a function, it is flagged as being in context. Variables that are in context, logically, should never have their memory freed, because they may be used as long as execution continues in that context. When a variable goes out of context, it is also flagged as being out of context.

Variables can be flagged in any number of ways. There may be a specific bit that is flipped when a variable is in context, or there may be an “in-context” variable list and an “out-of-context” variable list between which variables are moved. The implementation of the flagging is unimportant; it’s really the theory that is key.

When the garbage collector runs, it marks all variables stored in memory (once again, in any number of ways). It then clears its mark off of variables that are in context and variables that are referenced by in-context variables. The variables that are marked after that are considered ready for deletion, because they can’t be reached by any in-context variables. The garbage collector then does a memory sweep, destroying each of the marked values and reclaiming the memory associated with them.

As of 2008, Internet Explorer, Firefox, Opera, Chrome, and Safari all use mark-and-sweep garbage collection (or variations thereof) in their JavaScript implementations, though the timing of garbage collection differs.

Reference Counting

A second, less-popular type of garbage collection is reference counting. The idea is that every value keeps track of how many references are made to it. When a variable is declared and a reference value is assigned, the reference count is one. If another variable is then assigned to the same value, the reference count is incremented. Likewise, if a variable with a reference to that value is overwritten with another value, then the reference count is decremented. When the reference count of a value reaches zero, there is no way to reach that value and it is safe to reclaim the associated memory. The garbage collector frees the memory for values with a reference count of zero the next time it runs.

Reference counting was initially used by Netscape Navigator 3.0 and was immediately met with a serious issue: circular references. A circular reference occurs when object A has a pointer to object B and object B has a reference to object A, such as in the following example:

function problem(){
    var objectA = new Object();
    var objectB = new Object();
                   
    objectA.someOtherObject = objectB;
    objectB.anotherObject = objectA;
}

In this example, objectA and objectB reference each other through their properties, meaning that each has a reference count of two. In a mark-and-sweep system, this wouldn’t be a problem because both objects go out of scope after the function has completed. In a reference-counting system, though, objectA and objectB will continue to exist after the function has exited, because their reference counts will never reach zero. If this function were called repeatedly, it would lead to a large amount of memory never being reclaimed. For this reason, Netscape abandoned a reference-counting garbage-collection routine in favor of a mark-and-sweep implementation in version 4.0. Unfortunately, that’s not where the reference-counting problem ended.

Not all objects in Internet Explorer 8 and earlier are native JavaScript objects. Objects in the Browser Object Model (BOM) and Document Object Model (DOM) are implemented as COM (Component Object Model) objects in C++, and COM objects use reference counting for garbage collection. So even though the Internet Explorer JavaScript engine uses a mark-and-sweep implementation, any COM objects that are accessed in JavaScript still use reference counting, meaning circular references are still a problem when COM objects are involved. The following simple example demonstrates a circular reference with a COM object:

var element = document.getElementById("some_element");
var myObject = new Object();
myObject.element = element;
element.someObject = myObject;

This example sets up a circular reference between a DOM element (element) and a native JavaScript object (myObject). The myObject variable has a property called element that points to element, and the element variable has a property called someObject that points back to myObject. Because of this circular reference, the memory for the DOM element will never be reclaimed even if it is removed from the page.

To avoid circular reference problems such as this, you should break the connection between native JavaScript objects and DOM elements when you’re finished using them. For example, the following code cleans up the circular references in the previous example:

myObject.element = null;
element.someObject = null;

Setting a variable to null effectively severs the connection between the variable and the value it previously referenced. The next time the garbage collector runs, these values will be deleted and the memory will be reclaimed.

Internet Explorer 9 remedied some of these problems by making BOM and DOM objects into true JavaScript objects, thus avoiding the problem of having two different garbage-collection algorithms and eliminating common memory leak issues.

image

There are several other patterns that may cause circular references, which will be covered throughout this book.

Performance

The garbage collector runs periodically and can potentially be an expensive process if there are a large number of variable allocations in memory, so the timing of the garbage-collection process is important. Internet Explorer was infamous for its performance issues related to how often the garbage collector ran — it ran based on the number of allocations, specifically 256 variable allocations, 4,096 object/array literals and array slots, or 64kb of strings. If any of these thresholds were reached, the garbage collector would run. The problem with this implementation is that a script with so many variables will probably continue to have that many variables throughout its lifetime, meaning the garbage collector will run quite frequently. This issue caused serious performance problems that led to changes in the garbage-collection routine in Internet Explorer 7.

With the release of Internet Explorer 7, the JavaScript engine’s garbage-collection routine was tuned to dynamically change the allocation threshold of variables, literals, and/or array slots that triggered garbage collection. The Internet Explorer 7 thresholds start out equal to those in Internet Explorer 6. If the garbage-collection routine reclaims less than 15 percent of the allocations, the threshold for variables, literals, and/or array slots doubles. If the routine ever reclaims 85 percent of the allocations, then the threshold is reset to the default. This simple change greatly improved the performance of the browser on JavaScript-heavy web pages.

image

It’s possible, though not recommended, to trigger the garbage-collection process in some browsers. In Internet Explorer, the window.CollectGarbage() method causes garbage collection to occur immediately. In Opera 7 and higher, calling window.opera.collect() initiates the garbage-collection process.

Managing Memory

In a garbage-collected programming environment, developers typically don’t have to worry about memory management. However, JavaScript runs in an environment where memory management and garbage collection operate uniquely. The amount of memory available for use in web browsers is typically much less than is available for desktop applications. This is more of a security feature than anything else, ensuring that a web page running JavaScript can’t crash the operating system by using up all the system memory. The memory limits affect not only variable allocation but also the call stack and the number of statements that can be executed in a single thread.

Keeping the amount of used memory to a minimum leads to better page performance. The best way to optimize memory usage is to ensure that you’re keeping around only data that is necessary for the execution of your code. When data is no longer necessary, it’s best to set the value to null, freeing up the reference — this is called dereferencing the value. This advice applies mostly to global values and properties of global objects. Local variables are dereferenced automatically when they go out of context, as in this example:

function createPerson(name){
    var localPerson = new Object();
    localPerson.name = name;
    return localPerson;
}
                   
var globalPerson = createPerson("Nicholas");
                   
//do something with globalPerson
                   
globalPerson = null;

In this code, the variable globalPerson is filled with a value returned from the createPerson() function. Inside createPerson(), localPerson creates an object and adds a name property to it. The variable localPerson is returned as the function value and assigned to globalPerson. Because localPerson goes out of context after createPerson() has finished executing, it doesn’t need to be dereferenced explicitly. Because globalPerson is a global variable, it should be dereferenced when it’s no longer needed, which is what happens in the last line.

Keep in mind that dereferencing a value doesn’t automatically reclaim the memory associated with it. The point of dereferencing is to make sure the value is out of context and will be reclaimed the next time garbage collection occurs.

SUMMARY

Two types of values can be stored in JavaScript variables: primitive values and reference values. Primitive values have one of the five primitive data types: Undefined, Null, Boolean, Number, and String. Primitive and reference values have the following characteristics:

  • Primitive values are of a fixed size and so are stored in memory on the stack.
  • Copying primitive values from one variable to another creates a second copy of the value.
  • Reference values are objects and are stored in memory on the heap.
  • A variable containing a reference value actually contains just a pointer to the object, not the object itself.
  • Copying a reference value to another variable copies just the pointer, so both variables end up referencing the same object.
  • The typeof operator determines a value’s primitive type, whereas the instanceof operator is used to determine the reference type of a value.

All variables, primitive and reference, exist within an execution context (also called a scope) that determines the lifetime of the variable and which parts of the code can access it. Execution context can be summarized as follows:

  • Execution contexts exist globally (called the global context) and within functions.
  • Each time a new execution context is entered, it creates a scope chain to search for variables and functions.
  • Contexts that are local to a function have access not only to variables in that scope but also to variables in any containing contexts and the global context.
  • The global context has access only to variables and functions in the global context and cannot directly access any data inside local contexts.
  • The execution context of variables helps to determine when memory will be freed.

JavaScript is a garbage-collected programming environment where the developer need not be concerned with memory allocation or reclamation. JavaScript’s garbage-collection routine can be summarized as follows:

  • Values that go out of scope will automatically be marked for reclamation and will be deleted during the garbage-collection process.
  • The predominant garbage-collection algorithm is called mark-and-sweep, which marks values that aren’t currently being used and then goes back to reclaim that memory.
  • Another algorithm is reference counting, which keeps track of how many references there are to a particular value. JavaScript engines no longer use this algorithm, but it still affects Internet Explorer because of nonnative JavaScript objects (such as DOM elements) being accessed in JavaScript.
  • Reference counting causes problems when circular references exist in code.
  • Dereferencing variables helps not only with circular references but also with garbage collection in general. To aid in memory reclamation, global objects, properties on global objects, and circular references should all be dereferenced when no longer needed.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.60.62