You are previewing JavaScript Enlightenment.

JavaScript Enlightenment

Cover of JavaScript Enlightenment by Cody Lindley Published by O'Reilly Media, Inc.
  1. JavaScript Enlightenment
  2. Preface
    1. Introduction
      1. Why Did I Write This Book?
      2. Who Should Read This Book?
      3. Why JavaScript 1.5 and ECMAScript 3 Edition?
      4. Why Didn’t I Cover the Date(), Error(), and RegEx() Objects?
    2. More Code, Fewer Words
    3. Exhaustive Code and Repetition
    4. Color-Coding Conventions
    5. jsFiddle, JS Bin, and Firebug lite-dev
    6. Conventions Used in This Book
    7. Using Code Examples
    8. Safari® Books Online
    9. How to Contact Us
    10. About the Author
    11. About the Technical Editors
      1. Michael Richardson
      2. Kyle Simpson
      3. Nathan Smith
      4. Ben Nadel
      5. Ryan Florence
      6. Nathan Logan
  3. 1. JavaScript Objects
    1. Creating Objects
    2. JavaScript Constructors Construct and Return Object Instances
    3. The JavaScript Native/Built-In Object Constructors
    4. User-Defined/Non-Native Object Constructor Functions
    5. Instantiating Constructors Using the new Operator
    6. Creating Shorthand/Literal Values from Constructors
    7. Primitive (a.k.a. Simple) Values
    8. The Primitive Values null, undefined, “string”, 10, true, and false Are Not Objects
    9. How Primitive Values Are Stored/Copied in JavaScript
    10. Primitive Values Are Equal by Value
    11. The String, Number, and Boolean Primitive Values Act Like Objects When Used Like Objects
    12. Complex (a.k.a. Composite) Values
    13. How Complex Values Are Stored/Copied in JavaScript
    14. Complex Objects Are Equal by Reference
    15. Complex Objects Have Dynamic Properties
    16. The typeof Operator Used on Primitive and Complex Values
    17. Dynamic Properties Allow for Mutable Objects
    18. All Constructor Instances Have Constructor Properties that Point to Their Constructor Function
    19. Verify that an Object Is an Instance of a Particular Constructor Function
    20. An Instance Created From a Constructor Can Have Its Own Independent Properties (Instance Properties)
    21. The Semantics of “JavaScript Objects” and “Object() Objects”
  4. 2. Working with Objects and Properties
    1. Complex Objects Can Contain Most of the JavaScript Values as Properties
    2. Encapsulating Complex Objects in a Programmatically Beneficial Way
    3. Getting/Setting/Updating an Object’s Properties Using Dot Notation or Bracket Notation
    4. Deleting Object Properties
    5. How References to Object Properties Are Resolved
    6. Using hasOwnProperty, Verify That an Object Property Is Not From the Prototype Chain
    7. Checking If an Object Contains a Given Property Using the in Operator
    8. Enumerate (Loop Over) an Object’s Properties using the for in Loop
    9. Host Objects versus Native Objects
    10. Enhancing and Extending Objects with Underscore.js
  5. 3. Object()
    1. Conceptual Overview of Using Object() Objects
    2. Object() Parameters
    3. Object() Properties and Methods
    4. Object() Object Instance Properties and Methods
    5. Creating Object() Objects Using “Object Literals”
    6. All Objects Inherit From Object.prototype
  6. 4. Function()
    1. Conceptual Overview of Using Function() Objects
    2. Function() Parameters
    3. Function() Properties and Methods
    4. Function Object Instance Properties and Methods
    5. Functions Always Return a Value
    6. Functions Are First-Class Citizens (Not Just Syntax but Values)
    7. Passing Parameters to a Function
    8. this and arguments Values Available To All Functions
    9. The arguments.callee Property
    10. The Function Instance length Property and arguments.length
    11. Redefining Function Parameters
    12. Return a Function Before It Is Done (Cancel Function Execution)
    13. Defining a Function (Statement, Expression, or Constructor)
    14. Invoking a Function [Function, Method, Constructor, or call() and apply()]
    15. Anonymous Functions
    16. Self-Invoking Function Expression
    17. Self-Invoking Anonymous Function Statements
    18. Functions Can Be Nested
    19. Passing Functions to Functions and Returning Functions from Functions
    20. Invoking Function Statements Before They Are Defined (Function Hoisting)
    21. A Function Can Call Itself (Recursion)
  7. 5. The Head/Global Object
    1. Conceptual Overview of the Head Object
    2. Global Functions Contained Within the Head Object
    3. The Head Object versus Global Properties and Global Variables
    4. Referring to the Head Object
    5. The Head Object Is Implied and Typically Not Referenced Explicitly
  8. 6. The this Keyword
    1. Conceptual Overview of this and How It Refers to Objects
    2. How Is the Value of this Determined?
    3. The this Keyword Refers to the Head Object in Nested Functions
    4. Working Around the Nested Function Issue by Leveraging the Scope Chain
    5. Controlling the Value of this Using call() or apply()
    6. Using the this Keyword Inside a User-Defined Constructor Function
    7. The this Keyword Inside a Prototype Method Refers to a Constructor Instance
  9. 7. Scope and Closures
    1. Conceptual Overview of JavaScript Scope
    2. JavaScript Does Not Have Block Scope
    3. Use var Inside Functions to Declare Variables and Avoid Scope Gotchas
    4. The Scope Chain (Lexical Scoping)
    5. The Scope Chain Lookup Returns the First Found Value
    6. Scope Is Determined During Function Definition, not Invocation
    7. Closures Are Caused by the Scope Chain
  10. 8. Function Prototype Property
    1. Conceptual Overview of the Prototype Chain
    2. Why Care About the prototype Property?
    3. Prototype Is Standard on All function() Instances
    4. The Default prototype Property Is an Object() Object
    5. Instances Created From a Constructor Function are Linked to the Constructor’s prototype Property
    6. Last Stop in the prototype Chain is Object.prototype
    7. The prototype Chain Returns the First Property Match It Finds in the Chain
    8. Replacing the prototype Property with a New Object Removes the Default Constructor Property
    9. Instances That Inherit Properties from the Prototype Will Always Get the Latest Values
    10. Replacing the prototype Property with a New Object Does Not Update Former Instances
    11. User-Defined Constructors Can Leverage the Same Prototype Inheritance as Native Constructors
    12. Creating Inheritance Chains (the Original Intention)
  11. 9. Array()
    1. Conceptual Overview of Using Array() Objects
    2. Array() Parameters
    3. Array() Properties and Methods
    4. Array Object Instance Properties and Methods
    5. Creating Arrays
    6. Adding and Updating Values in Arrays
    7. Length versus Index
    8. Defining Arrays with a Predefined Length
    9. Setting Array Length can Add or Remove Values
    10. Arrays Containing Other Arrays (Multidimensional Arrays)
    11. Looping Over an Array, Backwards and Forwards
  12. 10. String()
    1. Conceptual Overview of Using the String() Object
    2. String() Parameters
    3. String() Properties and Methods
    4. String Object Instance Properties and Methods
  13. 11. Number()
    1. Conceptual Overview of Using the Number() Object
    2. Integers and Floating-Point Numbers
    3. Number() Parameters
    4. Number() Properties
    5. Number Object Instance Properties and Methods
  14. 12. Boolean()
    1. Conceptual Overview of Using the Boolean() Object
    2. Boolean() Parameters
    3. Boolean() Properties and Methods
    4. Boolean Object Instance Properties and Methods
    5. Non-Primitive False Boolean Objects Convert to true
    6. Certain Things Are false, Everything Else Is true
  15. 13. Working with Primitive String, Number, and Boolean Values
    1. Primitive/Literal Values Are Converted to Objects When Properties Are Accessed
    2. You Should Typically Use Primitive String, Number, and Boolean Values
  16. 14. Null
    1. Conceptual Overview of Using the null Value
    2. typeof Returns null Values as “object”
  17. 15. Undefined
    1. Conceptual Overview of the undefined Value
    2. JavaScript ECMAScript 3 Edition (and Later) Declares the undefined Variable in the Global Scope
  18. 16. Math Function
    1. Conceptual Overview of the Built-In Math Object
    2. Math Properties and Methods
    3. Math Is Not a Constructor Function
    4. Math Has Constants You Cannot Augment/Mutate
  19. A. Review
  20. B. Conclusion
  21. Index
  22. About the Author
  23. Colophon
  24. Copyright
O'Reilly logo

Chapter 4. Function()

Conceptual Overview of Using Function() Objects

A function is a container of code statements that can be invoked using the parentheses () operator. Parameters can be passed inside of the parentheses during invocation so that the statements in the function can access certain values when the function is invoked.

Below, we create two versions of an addNumbers function object—one using the new operator and another using the more common, literal pattern. Both are expecting two parameters. In each case, we invoke the function, passing parameters in the parentheses () operator.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

var addNumbersA = new Function('num1', 'num2', 'return num1 + num2');

console.log(addNumbersA(2, 2)); // logs 4

// could also be written the literal way, which is much more common
var addNumbersB = function(num1, num2) {return num1 + num2;};

console.log(addNumbersB(2, 2)); // logs 4

</script></body></html>

A function can be used to return a value, construct an object, or as a mechanism to simply run code. JavaScript has several uses for functions, but in its most basic form, a function is simply a unique scope of executable statements.

Function() Parameters

The Function() constructor takes an indefinite number of parameters, but the last parameter expected by the Function() constructor is a string containing statements that comprise the body of the function. Any parameters passed to the constructor before the last will be available to the function being created. It’s also possible to send multiple parameters as a comma-separated string.

Below, I contrast the usage of the Function() constructor with the more common patterns of instantiating a function object.

Live Code

<!DOCTYPE html><html lang="en"><body><script>


var addFunction = new Function('num1', 'num2', 'return num1 + num2');

/* Alternately, a single comma-separated string with arguments can be
  the first parameter of the constructor, with the function body following. */
var timesFunction = new Function('num1,num2', 'return num1 * num2');

console.log(addFunction(2,2),timesFunction(2,2)); // logs '4 4'

// versus the more common patterns for instantiating a function
var addFunction = function(num1, num2) {return num1 + num2;}; // expression form
function addFunction(num1, num2) {return num1 + num2;} // statement form


</script></body></html>

Notes

  • Directly leveraging the Function() constructor is not recommended or typically ever done because JavaScript will use eval() to parse the string containing the function’s logic. Many consider eval() to be unnecessary overhead. If it’s in use, a flaw in the design of the code is highly possible.

  • Using the Function() constructor without the new keyword has the same effect as using only the constructor to create function objects [e.g., new Function('x','return x') versus function(('x','return x')].

  • No closure is created (see Chapter 7) when invoking the Function() constructor directly.

Function() Properties and Methods

The function object has the following properties (not including inherited properties and methods):

Properties (e.g., Function.prototype;):

  • prototype

Function Object Instance Properties and Methods

Function object instances have the following properties and methods:

Instance Properties (e.g., var myFunction = function(x, y, z) {}; myFunction.length;):

  • arguments

  • constructor

  • length

Instance Methods (e.g., var myFunction = function(x, y, z) {}; myFunction.toString();):

  • apply()

  • call()

  • toString()

Functions Always Return a Value

While it’s possible to create a function simply to execute code statements, it’s also very common for a function to return a value. Below, we are returning a string from the sayHi function.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

var sayHi = function() {
   return 'Hi';
};

console.log(sayHi()); // logs "Hi"

</script></body></html>

If a function does not specify a return value, then undefined is returned. Below, we call the yelp function, which logs the string 'yelp' to the console without explicitly returning a value.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

var yelp = function() {
   console.log('I am yelping!');
   // functions return undefined even if we don't
}

/* logs true because a value is always returned, 
even if we don't specifically return one */
console.log(yelp() === undefined);

</script></body></html>

The takeaway here is that all functions return a value, even if you do not explicitly provide a value to return. If you do not specify a value to return, the value returned is undefined.

Functions Are First-Class Citizens (Not Just Syntax but Values)

In JavaScript, functions are objects. This means that a function can be stored in a variable, array, or object. Also, a function can be passed to, and returned from, a function. A function has properties because it is an object. All of these factors make functions first-class citizens in JavaScript.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

/* functions can be stored in variables (funcA), arrays (funcB), 
and objects (funcC) */
var funcA = function(){}; // called like so: funcA()
var funcB = [function(){}]; // called like so: funcB[0]()
var funcC = {method: function(){}}; // too.method() or funcC['method']()

// functions can be sent to, and sent back from, functions
var funcD = function(func){
   return func
};

var runFuncPassedToFuncD = funcD(function(){console.log('Hi');});

runFuncPassedToFuncD();

// functions are objects, which means they can have properties
var funcE = function(){};
funcE.answer = 'yup'; // instance property
console.log(funcE.answer); // logs 'yup'

</script></body></html>

It is crucial that you realize a function is an object, and thus a value. It can be passed around or augmented like any other expression in JavaScript.

Passing Parameters to a Function

Parameters are vehicles for passing values into the scope of a function when it is invoked. Below, as we invoke addFunction(), since we have predefined it to take two parameters, two added values become available within its scope.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

var addFunction = function(number1, number2) {
   var sum = number1 + number2;
   return sum;
}

console.log(addFunction(3, 3)); // logs 6

</script></body></html>

Notes

  • In contrast to some other programming languages, it is perfectly legal in JavaScript to omit parameters even if the function has been defined to accept these arguments. The missing parameters are simply given the value of undefined. Of course, by leaving out values for the parameters, the function might not work properly.

  • If you pass unexpected parameters to a function (those not defined when the function was created), no error will occur. And it’s possible to access these parameters from the arguments object, which is available to all functions.

this and arguments Values Available To All Functions

Inside the scope/body of all functions, the this and arguments values are available.

The arguments object is an array-like object containing all of the parameters being passed to the function. In the code below, even though we forgo specifying parameters when defining the function, we can rely on the arguments array passed to the function to access parameters if they are sent upon invocation.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

var add = function() {
   return arguments[0] + arguments[1];
};

console.log(add(4, 4)); // returns 8

</script></body></html>

The this keyword, passed to all functions, is a reference to the object that contains the function. As you might expect, functions contained within objects as properties (i.e., methods) can use this to gain a reference to the “parent” object. When a function is defined in the global scope, the value of this is the global object. Review the code below and make sure you understand what this is returning.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

var myObject1 = {
   name: 'myObject1',
   myMethod: function(){console.log(this);}
};

myObject1.myMethod(); // logs 'myObject1'

var myObject2 = function(){console.log(this);};

myObject2(); // logs window

</script></body></html>

The arguments.callee Property

The arguments object has a property called callee, which is a reference to the function currently executing. This property can be used to reference the function from within the scope of the function (e.g., arguments.callee)—a self-reference. In the code below, we use this property to gain a reference to the calling function.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

var foo = function foo() {
   console.log(arguments.callee); // logs foo()
   /* callee could be used to invoke recursively the foo function 
   (e.g., arguments.callee()) */
}();

</script></body></html>

This can be useful when a function needs to be called recursively.

The Function Instance length Property and arguments.length

The arguments object has a unique length property. While you might think this length property will give you the number of defined arguments, it actually gives the number of parameters sent to the function during invocation.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

var myFunction = function(z, s, d) {
   return arguments.length;
};

console.log(myFunction()); /* logs 0 because no parameters were passed 
                           to the function */

</script></body></html>

Using the length property of all Function() instances, we can actually grab the total number of parameters the function is expecting.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

var myFunction = function(z, s, d, e, r, m, q) {
   return myFunction.length;
};

console.log(myFunction()); // logs 7

</script></body></html>

Note

The arguments.length property beginning with JavaScript 1.4 is deprecated, and the number of arguments sent to a function can be accessed from the length property of the function object. So, moving forward, you can get the length value by leveraging the callee property to first gain reference to the function being invoked (i.e., arguments.callee.length).

Redefining Function Parameters

A function’s parameters can be redefined inside the function either directly, or by using the arguments array. Take a look at the code below.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

var foo = false;
var bar = false;

var myFunction = function(foo, bar) {
   arguments[0] = true;
   bar = true;
   console.log(arguments[0], bar); // logs true true
}

myFunction();

</script></body></html>

Notice that I can redefine the value of the bar parameter using the arguments index or by directly reassigning a new value to the parameter.

Return a Function Before It Is Done (Cancel Function Execution)

Functions can be cancelled at any time during invocation by using the return keyword with or without a value. Below, we are canceling the add function if the parameters are undefined or not a number.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

var add = function(x, y) {
   // If the parameters are not numbers, return error.
   if (typeof x !== 'number' || typeof y !== 'number') {return 'pass in numbers';}
   return x + y;
}
console.log(add(3,3)); // logs 6
console.log(add('2','2')); // logs 'pass in numbers'

</script></body></html>

The takeaway here is that you can cancel a function’s execution by using the return keyword at any point in the execution of the function.

Defining a Function (Statement, Expression, or Constructor)

A function can be defined in three different ways: a function constructor, a function statement, or a function expression. Below, I demonstrate each variation.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

/* function constructor: the last parameter is the function logic,
   everything before it is a parameter */
var addConstructor = new Function('x', 'y', 'return x + y');

// function statement
function addStatement(x, y) {
   return x + y;
}

// function expression
var addExpression = function(x, y) {
    return x + y;
};

console.log(addConstructor(2,2), addStatement (2,2), addExpression (2,2)); 
  // logs '4 4 4'

</script></body></html>

Note

Some have said that there is a fourth type of definition for functions, called the “named function expression.” A named function expression is simply a function expression that also contains a name (e.g., var add = function add(x, y) {return x+y}).

Invoking a Function [Function, Method, Constructor, or call() and apply()]

Functions are invoked using four different scenarios or patterns:

  • As a function

  • As a method

  • As a constructor

  • Using apply() or call()

In the code below, we examine each of these invocation patterns.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

// function pattern
var myFunction = function(){return 'foo'};
console.log(myFunction()); // log 'foo'

// method pattern
var myObject = {myFunction: function(){return 'bar';}}
console.log(myObject.myFunction()); // log 'bar'

// constructor pattern
var Cody = function(){
   this.living = true;
   this.age = 33;
   this.gender = 'male';
   this.getGender = function() {return this.gender;};
}
var cody = new Cody(); // invoke via Cody constructor
console.log(cody); // logs cody object and properties

// apply() and call() pattern
var greet = {
   runGreet: function(){
       console.log(this.name,arguments[0],arguments[1]);
   }
}

var cody = {name:'cody'};
var lisa = {name:'lisa'};

// invoke the runGreet function as if it were inside of the cody object
      greet.runGreet.call(cody,'foo','bar'); // logs 'cody foo bar'

// invoke the runGreet function as if it were inside of the lisa object
      greet.runGreet.apply(lisa, ['foo','bar']); // logs 'lisa foo bar'

/* Notice the difference between call() and apply() in how parameters are sent 
to the function being invoked */

</script></body></html>

Make sure you are aware of all four of the invocation patterns, as code you will encounter may contain any of them.

Anonymous Functions

An anonymous function is a function that is not given an identifier. Anonymous functions are mostly used for passing functions as a parameter to another function.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

// function(){console.log('hi');}; // anonymous function, but no way to invoke it

// create a function that can invoke our anonymous function
var sayHi = function(f){
   f(); // invoke anonymous function
}

// pass an anonymous function as parameter
sayHi(function(){console.log('hi');}); // log 'hi'

</script></body></html>

Self-Invoking Function Expression

A function expression (really any function except one created from the Function() constructor) can be immediately invoked after definition by using the parentheses operator. Below, we create a sayWord() function expression and then immediately invoke the function. This is considered to be a self-invoking function.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

var sayWord = function() {console.log('Word 2 yo mo!');}(); 
// logs 'Word 2 yo mo!'

</script></body></html>

Self-Invoking Anonymous Function Statements

It’s possible to create an anonymous function statement that is self-invoked. This is called a self-invoking anonymous function. Below, we create several anonymous functions that are immediately invoked.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

// most commonly used/seen in the wild
(function(msg) {
   console.log(msg);
})('Hi');

// slightly different but achieving the same thing:
(function(msg) {
   console.log(msg)
}('Hi'));

// the shortest possible solution
!function sayHi(msg) {console.log(msg);}('Hi');

// FYI, this does NOT work!
// function sayHi() {console.log('hi');}();

</script></body></html>

Note

According to the ECMAScript standard, the parentheses around the function (or anything that transforms the function into an expression) are required if the function is to be invoked immediately.

Functions Can Be Nested

Functions can be nested inside of other functions indefinitely. Below, we encapsulate the goo function inside of the bar function, which is inside of the foo function.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

var foo = function() {
   var bar = function() {
       var goo = function() {
          console.log(this); // logs reference to head window object
       }();
   }();
}();

</script></body></html>

The simple takeaway here is that functions can be nested and that there is no limit to how deep the nesting can go.

Note

Remember, the value of this for nested functions will be the head object (e.g., window object in a web browser) in JavaScript 1.5, ECMAScript 3 Edition.

Passing Functions to Functions and Returning Functions from Functions

As previously mentioned, functions are first-class citizens in JavaScript. And since a function is a value, and a function can be passed any sort of value, a function can be passed to a function. Functions that take and/or return other functions are sometimes called “higher-order functions.”

Below, we are passing an anonymous function to the foo function, which we then immediately return from the foo function. It is this anonymous function that the variable bar points to, since foo accepts and then returns the anonymous function.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

// functions can be sent to, and sent back from, functions
var foo = function(f) {
   return f;
}

var bar = foo(function() {console.log('Hi');});

bar(); // logs 'Hi'

</script></body></html>

So when bar is invoked, it invokes the anonymous function that was passed to the foo() function, which is then passed back from the foo() function and referenced from the bar variable. All this is to showcase the fact that functions can be passed around just like any other value.

Invoking Function Statements Before They Are Defined (Function Hoisting)

A function statement can be invoked during execution before its actual definition. This is a bit odd, but you should be aware of it so you can leverage it, or at least know what’s going on when you encounter it. Below, I invoke the sayYo() and sum() function statements before they are defined.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

// Example 1

var speak = function() {
    sayYo(); /* sayYo() has not been defined yet but it can still be invoked, 
             logs 'yo' */
    function sayYo() {console.log('Yo');}
}(); // invoke

// Example 2

console.log(sum(2, 2)); /* invoke sum(), which is not defined yet, 
                        but can still be invoked */
function sum(x, y) {return x + y;}

</script></body></html>

This happens because before the code runs, function statements are interpreted and added to the execution stack/context. Make sure you are aware of this as you use function statements.

Note

Functions, defined as “function expressions” are not hoisted—only “function statements” are hoisted.

A Function Can Call Itself (Recursion)

It’s perfectly legitimate for a function to call itself. In fact, this is often used in well-known coding patterns. In the code below, we kick off the countDownFrom function, which then calls itself via the function name countDownFrom. Essentially, this creates a loop that counts down from 5 to 0.

Live Code

<!DOCTYPE html><html lang="en"><body><script>

var countDownFrom = function countDownFrom(num) {
   console.log(num);
   num--; // change the parameter value
   if (num < 0){return false;} // if num < 0 return function with no recursion
   // could have also done arguments.callee(num) if it was an anonymous function
   countDownFrom(num);
};

countDownFrom(5); // kick off the function, which logs separately 5,4,3,2,1,0

</script></body></html>

You should be aware that it’s not uncommon for a function to invoke itself (a.k.a. recursion) or to do so repetitively.

The best content for your career. Discover unlimited learning on demand for around $1/day.