// 函式使用外部變數,會使用最新的值嗎?
let name = "John";
function sayHi() {
alert("Hi, " + name);
}
name = "Pete";
sayHi(); // what will it show: "John" or "Pete"?
// 函式會使用內部變數還是外部變數?
function makeWorker() {
let name = "Pete";
return function() {
alert(name);
};
}
let name = "John";
// create a function
let work = makeWorker();
// call it
work(); // what will it show? "Pete" (name where created) or "John" (name where called)?
// 回答第 1 個問題,變數會使用最新的值,
let name = "John";
function sayHi() {
alert("Hi, " + name);
}
name = "Pete"; // (*)
sayHi(); // Pete
Nested functions
function sayHiBye(firstName, lastName) {
// helper nested function to use below
function getFullName() {
return firstName + " " + lastName;
}
alert( "Hello, " + getFullName() );
alert( "Bye, " + getFullName() );
}
// 巢狀函式可以被返回作為新物件的屬性或直接作為結果。
// 巢狀函式作為新物件屬性
// constructor function returns a new object
function User(name) {
// the object method is created as a nested function
this.sayHi = function() {
alert(name);
};
}
let user = new User("John");
user.sayHi(); // the method "sayHi" code has access to the outer "name"
// 巢狀函式作為結果
function makeCounter() {
let count = 0;
return function() {
return count++; // has access to the outer "count"
};
}
let counter = makeCounter();
alert( counter() ); // 0
alert( counter() ); // 1
alert( counter() ); // 2
for (let i = 0; i < 10; i++) {
// Each loop has its own Lexical Environment
// {i: value}
}
alert(i); // Error, no such variable
Code blocks
// 當 2 個腳本有相同痊癒變數會出現問題,用 {...} 可以解決這樣的問題。
{
// do some job with local variables that should not be seen outside
let message = "Hello";
alert(message); // Hello
}
alert(message); // Error: message is not defined
IIFE
// 在過去沒有 lexical environment,因此發明 immediately-invoked function expressions,
// 函式有區域變數並立即執行,
(function() {
let message = "Hello";
alert(message); // Hello
})();
// 沒有函式名稱
// Try to declare and immediately call a function
function() { // <-- Error: Unexpected token (
let message = "Hello";
alert(message); // Hello
}();
// 不能宣告同時執行
// syntax error because of parentheses below
function go() {
}(); // <-- can't call Function Declaration immediately
// Ways to create IIFE
(function() {
alert("Parentheses around the function");
})();
(function() {
alert("Parentheses around the whole thing");
}());
!function() {
alert("Bitwise NOT operator starts the expression");
}();
+function() {
alert("Unary plus starts the expression");
}();
Garbage collection
// 函式執行完,會被記憶體清除
function f() {
let value1 = 123;
let value2 = 456;
}
f();
// 用閉包的特性,函式執行完仍在記憶體內,[[Environment] 指向函式。
function f() {
let value = 123;
function g() { alert(value); }
return g;
}
let g = f(); // g is reachable, and keeps the outer lexical environment in memory
// 函式被呼叫多次,相對應的 lexical environment,也會被儲存在記憶體。
function f() {
let value = Math.random();
return function() { alert(value); };
}
// 3 functions in array, every one of them links to Lexical Environment (LE for short)
// from the corresponding f() run
// LE LE LE
let arr = [f(), f(), f()];
// 當全域變數被清除,函式消失
function f() {
let value = 123;
function g() { alert(value); }
return g;
}
let g = f(); // while g is alive
// there corresponding Lexical Environment lives
g = null; // ...and now the memory is cleaned up
Real-life optimizations
// 實際狀況 JavaScript 引擎會進行優化,未作用的變數會被清除。
// v8 在 debug 時無法使用區域變數
function f() {
let value = Math.random();
function g() {
debugger; // in console: type alert( value ); No such variable!
}
return g;
}
let g = f();
g();
// 會返回全域變數
let value = "Surprise!";
function f() {
let value = "the closest value";
function g() {
debugger; // in console: type alert( value ); Surprise!
}
return g;
}
let g = f();
g();