321 lines
6.4 KiB
Markdown
321 lines
6.4 KiB
Markdown
---
|
|
title: Variable Scope
|
|
subtitle: Not An Oral Hygeine Product
|
|
author: Levi Pearson
|
|
...
|
|
|
|
# Introduction
|
|
|
|
## Goals
|
|
|
|
+ Understand How Variable Naming Works
|
|
+ Learn What "Scope" Really Means
|
|
+ See How Different Languages Handle Scope
|
|
+ Identify Tricky Bits in Javascript Scope Rules
|
|
|
|
# Variables, Names, and Scope
|
|
|
|
## On the difficulty of names
|
|
|
|
> There are only two hard things in Computer Science:
|
|
> **cache invalidation** and **naming things**.
|
|
>
|
|
> -- Phil Karlton
|
|
|
|
## Some definitions
|
|
|
|
+ A **variable** is a **name** bound to a **storage location** within an
|
|
**environment**
|
|
|
|
+ An **environment** holds all variable bindings visible at some point in a
|
|
program.
|
|
|
|
+ Sometimes an environment is called a **context**
|
|
|
|
## What about scope?
|
|
|
|
+ The **scope** of a variable refers to the region of the program where its
|
|
binding is visible in the environment
|
|
|
|
*or*
|
|
|
|
+ **Scope** is the *property of a variable* that defines what part of the
|
|
program its name can be used in to identify the variable
|
|
|
|
## Static vs. dynamic
|
|
|
|
+ **Static** or **lexical**: Scope determined just by text of program
|
|
|
|
+ **Dynamic**: Scope affected by bindings in the run-time stack
|
|
|
|
## Early vs. late
|
|
|
|
+ Lexical scope can be determined at compile-time; sometimes called **early
|
|
binding**
|
|
|
|
+ Dynamic scope relies on run-time information; sometimes called **late
|
|
binding**
|
|
|
|
## Shadowing
|
|
|
|
+ Scopes of variables with the same name can *overlap*
|
|
|
|
+ Rules determine which scope "wins" at any point
|
|
|
|
+ The *losers* are said to be **shadowed** by the *winner*
|
|
|
|
+ Rule is usually a variation on "most recent declaration wins"
|
|
|
|
# Levels of scope
|
|
|
|
## Global scope
|
|
|
|
+ **Global** scope extends through the whole program
|
|
|
|
+ Sometimes restricted to just after the declaration.
|
|
|
|
## Function scope
|
|
|
|
+ **Function** scope extends through the whole function body
|
|
|
|
+ Sometimes restricted to just after the declaration, e.g. C
|
|
|
|
+ When not restricted, it is sometimes called **hoisting** the declaration
|
|
|
|
+ Hoisting affects scope, but not initialization point
|
|
|
|
* * *
|
|
|
|
~~~{.javascript}
|
|
// Shadowing and Hoisting
|
|
|
|
var x = 1;
|
|
|
|
function foo() {
|
|
console.log(x);
|
|
var x = 2;
|
|
console.log(x);
|
|
}
|
|
|
|
console.log(x);
|
|
~~~
|
|
|
|
* * *
|
|
|
|
~~~{.javascript}
|
|
// Shadowing and Hoisting
|
|
|
|
var x = 1;
|
|
|
|
function foo() {
|
|
console.log(x); // x is undefined!
|
|
var x = 2;
|
|
console.log(x); // prints 2
|
|
}
|
|
|
|
console.log(x); // prints 1
|
|
~~~
|
|
|
|
## Block scope
|
|
|
|
+ **Block** scope extends through a compound statement block
|
|
|
|
+ Variables declared in control statements (like `for`) are scoped to the block
|
|
|
|
+ Javascript (pre-ES6) and Python don't have block scopes!
|
|
|
|
+ ES6 keeps function scope for `var`, adds `let` and `const` for block scope
|
|
|
|
* * *
|
|
|
|
~~~{.javascript}
|
|
// Block Scope
|
|
var y = 2;
|
|
function foo() {
|
|
let x = 5;
|
|
for (let x = 0; x < 3; x++) {
|
|
let y = x + 1;
|
|
console.log(y);
|
|
}
|
|
console.log(x);
|
|
console.log(y);
|
|
let y = 3;
|
|
console.log(y);
|
|
}
|
|
console.log(y);
|
|
~~~
|
|
|
|
* * *
|
|
|
|
~~~{.javascript}
|
|
// Block Scope
|
|
var y = 2;
|
|
function foo() {
|
|
let x = 5;
|
|
for (let x = 0; x < 3; x++) {
|
|
let y = x + 1;
|
|
console.log(y); // prints 1, 2, 3
|
|
}
|
|
console.log(x); // prints 5
|
|
console.log(y); // ReferenceError
|
|
let y = 3;
|
|
console.log(y); // prints 3
|
|
}
|
|
console.log(y); // prints 2
|
|
~~~
|
|
|
|
## Other scopes
|
|
|
|
+ In C and C++, there is a **file** scope
|
|
|
|
+ Functional languages often provide **expression** scope
|
|
|
|
+ Languages with module systems provide **module** scope
|
|
|
|
+ Python has a module system and module scope; ES6 does too
|
|
|
|
## Dynamic scope
|
|
|
|
+ Dynamic scope refers to time periods instead of text regions
|
|
|
|
+ Dynamic global scope refers to the whole program execution
|
|
|
|
+ Dynamic function scope
|
|
+ starts when execution enters the function body
|
|
+ extends through any function calls in the body
|
|
+ ends when the function returns
|
|
|
|
+ Dynamic scope is default in Bourne-style shells, PowerShell, Emacs Lisp
|
|
|
|
+ Dynamic scope is optional in Perl, Common Lisp, and others
|
|
|
|
* * *
|
|
|
|
~~~{.javascript}
|
|
// In a hypothetical dynamically-scoped Javascript
|
|
var x = 1;
|
|
|
|
function foo() {
|
|
var x = 2;
|
|
bar();
|
|
}
|
|
|
|
function bar() {
|
|
console.log(x);
|
|
}
|
|
|
|
foo();
|
|
bar();
|
|
~~~
|
|
|
|
* * *
|
|
|
|
~~~{.javascript}
|
|
// In a hypothetical dynamically-scoped Javascript
|
|
var x = 1;
|
|
|
|
function foo() {
|
|
var x = 2;
|
|
bar();
|
|
}
|
|
|
|
function bar() {
|
|
console.log(x);
|
|
}
|
|
|
|
foo(); // prints 2
|
|
bar(); // prints 1
|
|
~~~
|
|
|
|
# Tricky Bits
|
|
|
|
## Assignment
|
|
|
|
+ What should be the scope of a variable created by assignment?
|
|
|
|
+ Local?
|
|
+ Can't assign to a variable in enclosing environment
|
|
+ Will create a new variable shadowing the one you wanted to change
|
|
+ Python works this way
|
|
|
|
+ Global?
|
|
+ Might accidentally change existing global instead of making a new one
|
|
+ Can work around it by declaring all your variables
|
|
+ Javascript works this way
|
|
|
|
+ Just make declaration of variables mandatory!
|
|
|
|
## Closures
|
|
|
|
+ A variable is **free** within a function body if:
|
|
+ it is referenced in the body
|
|
+ it is not declared in the body
|
|
+ it is not a parameter to the function
|
|
|
|
+ A function *closes over* free variables bound in an enclosing environment
|
|
|
|
+ The variables are found in its **closure**
|
|
|
|
+ We call a function returned from its enclosing environment a **closure** too
|
|
|
|
+ Bindings in a closure keep the scope from their *definition* point, even if
|
|
the function is invoked in a different environment
|
|
|
|
* * *
|
|
|
|
~~~{.javascript}
|
|
// Fun with closures!
|
|
function a() {
|
|
var x = 0;
|
|
return function() {
|
|
x++; console.log(x);
|
|
}
|
|
}
|
|
function b(g) {
|
|
var x = 0;
|
|
g(); console.log(x);
|
|
}
|
|
var c1 = a(), c2 = a();
|
|
b(c1);
|
|
b(c2);
|
|
b(c1);
|
|
~~~
|
|
|
|
* * *
|
|
|
|
~~~{.javascript}
|
|
// Fun with closures!
|
|
function a() {
|
|
var x = 0;
|
|
return function() {
|
|
x++; console.log(x);
|
|
}
|
|
}
|
|
function b(g) {
|
|
var x = 0;
|
|
g(); console.log(x);
|
|
}
|
|
var c1 = a(), c2 = a();
|
|
b(c1); // prints 1, 0
|
|
b(c2); // prints 1, 0
|
|
b(c1); // prints 2, 0
|
|
~~~
|
|
|
|
## This
|
|
|
|
+ It is dynamically scoped, late-bound
|
|
|
|
+ Rules for `this`:
|
|
1. invoked as a function, it is the global object
|
|
2. invoked with `new`, it is the object the constructor will return
|
|
3. invoked as a method, it is the object before the `.`
|
|
4. invoked with `call` or `apply`, it's what you asked it to be
|
|
5. wrapped by `Function.prototype.bind`, it's `bind`'s argument
|
|
6. invoked as a DOM Event handler, it's the element the event fired from
|
|
|
|
+ For closure creation, `this` is not a free variable
|
|
|
|
+ Except for ES6 arrow functions!
|
|
|
|
# Thanks for listening!
|