LEVEL: BEGINNER/INTERMEDIATE (INTERMEDIATE STUFF IN [BRACKETS])
PREREQS: VARIABLES
First off, what the heck is 'scope' (the kind that doesn't help kill the germs that cause bad breath)? Scope describes the context in which a variable can be used. For example, if a variable's scope is a certain function, then that variable can only be used in that function. If you were to try to access that variable anywhere else in your script, it would come back as undefined.
There are four (technically three) types of scope in javascript:
- window - These variables can be used anywhere in your script at any time.
- function - These variables are only available inside of a particular function.
- class (arguably same as function) - These variables are only available to members of a particular class.
- block (new in JavaScript 1.7) - These variables only exist within a particular code block (i.e., between a set of '{' and '}').
WINDOW SCOPE
Window-level variables get attached to the universal window object in your script. To declare a window-level (or "global") variable, you would use this syntax:
Expand|Select|Wrap|Line Numbers
- <script type="text/javascript">
- var myGlobal = "This is a global variable because it's not declared inside a function.";
- myGlobal = "This is also a global variable because it is not defined using the 'var' keyword. See below for more info.";
- window.myGlobal = 'This is also global for the same reason the previous statement is global.';
- </script>
So:
Expand|Select|Wrap|Line Numbers
- var myGlobal = 'This is global because my context is window, but...';
- function doSomething() {
- var myLocal = '... this is not because my context is doSomething, not window.';
- }
[INTERMEDIATE STUFF: JavaScript has a (little-used) 'with' keyword that allows you to arbitrarily define a context. When you define a context using the with keyword, you are allowed to omit the parent object when you access its members.
For example:
Expand|Select|Wrap|Line Numbers
- var myObject = {
- product: 'paper',
- quantity: 500,
- salesman: 'Fred'
- };
- with(myObject) {
- alert(salesman);
- }
Similarly, every script in JavaScript starts with the following (implied) statement:
Expand|Select|Wrap|Line Numbers
- with(window) {
- // Your code goes here.
- }
Just like the way these two statements are identical:
Expand|Select|Wrap|Line Numbers
- window.location.href = 'http://www.example.com/';
- location.href = 'http://www.example.com/';
Expand|Select|Wrap|Line Numbers
- window.myVar = 'Global variable!';
- myVar = 'Also global!';
FUNCTION / CLASS SCOPE
[INTERMEDIATE STUFF: I've lumped function and class together since classes are basically just really, really shiny functions in JavaScript.]
This is where the var keyword really becomes important. Remember from the 'window scope' section that the var keyword will bind a variable to its parent context:
Expand|Select|Wrap|Line Numbers
- <script type="text/javascript">
- var myGlobal = 'Global because my context is window.';
- function doSomething() {
- var myLocal = 'Not global because my context is doSomething, not window.';
- }
- </script>
Expand|Select|Wrap|Line Numbers
- function doSomething() {
- var myLocal = 'Not a global variable.';
- alert(myLocal);
- }
- doSomething(); // Alerts 'Not a global variable.'.
- alert(myLocal); // Alerts 'undefined'.
Expand|Select|Wrap|Line Numbers
- function doSomething() {
- myLocal = 'This is global because it gets attached to window.';
- alert(myLocal);
- }
- doSomething(); // Alerts 'This is global because it gets attached to window.'.
- alert(myLocal); // Alerts 'This is global because it gets attached to window.'.
Expand|Select|Wrap|Line Numbers
- function Class() {
- var myPrivate;
- function __construct() {
- myPrivate = 'A private variable.';
- alert(myPrivate);
- }
- __construct();
- }
- var myObject = new Class(); // Alerts 'A private variable.'.
- alert(myObject.myPrivate); // Alerts 'undefined' because myPrivate is not a member of myObject.
BLOCK SCOPE (NEW IN JAVASCRIPT 1.7)
New in JavaScript 1.7 (getting the idea?) is the let keyword. let allows you to scope a variable to a specific block of code.
For example:
Expand|Select|Wrap|Line Numbers
- let(myVar = 5) {
- alert(myVar); // Alerts 5.
- }
- alert(myVar); // Alerts 'undefined'.
For more information on JavaScript 1.7, check out this document.
SCOPE INHERITANCE
So we know that you can use scoping to limit access to a variable to a specific context. But what about functions (contexts) inside of functions or classes? How does scoping work then?
Let's look at a sample setup:
Expand|Select|Wrap|Line Numbers
- function myOuterFunction() {
- var myLocal = 'Declared locally.';
- function myInnerFunction() {
- alert(myLocal);
- }
- myInnerFunction(); // Alerts 'Declared locally.'
- }
- myOuterFunction(); // To declare myInnerFunction...
- myInnerFunction(); // Generates an error (see below).
[INTERMEDIATE STUFF: Note also that myInnerFunction is attached to myOuterFunction's context. In other words, if you try to call myInnerFunction outside of myOuterFunction, you'll get an error.
The reason for this is the same reason why the myLocal variable is undefined outside of myOuterFunction. For more information, take a look at this article.]
HANDLING COLLISIONS
What happens if you have two variables in overlapping contexts with identical names?
Expand|Select|Wrap|Line Numbers
- var myVar = 'Global';
- alert(myVar); // Alerts 'Global'.
- function myOuterFunction() {
- var myVar = 'Local';
- alert(myVar); // Alerts 'Local'.
- function myInnerFunction(myVar) {
- myVar = myVar + '!';
- alert(myVar);
- }
- myInnerFunction('Inner'); // Alerts 'Inner!'.
- }
- myOuterFunction(); // Do we modify myVar?
- alert(myVar); // Alerts 'Global'.
This is why the final alert statement in the above example outputs 'Global' instead of 'Inner!'. We're not actually changing the value of window.myVar. Instead we're setting it aside and creating a new variable named myVar inside of myInnerFunction and modifying that variable instead.
Note also that when you declare a function, you are also *declaring* its arguments. So in this line:
Expand|Select|Wrap|Line Numbers
- function myInnerFunction(myVar) {
Expand|Select|Wrap|Line Numbers
- function myInnerFunction(someOtherVar) {
So now you know the basics [and intermediates] behind variable scope in JavaScript. JavaScript variables [and functions, classes and objects] can have one of four [technically three] different scopes, depending on how -- and in what context -- they are declared:
- window (or 'global') scope - Available to any script anywhere in the window once declared.
- function scope - Available only to functions and objects inside of the function that defined the variable.
- class scope - Available only to functions and objects inside of the class that defined the variable.
- block scope - Available only to functions and objects inside of the block created by using a let statement.
That concludes today's lesson on variable scope. Live long and write prosperous code!