Immediately Invoked Function Expressions (IIFE) 立即呼叫的函數表示式。
// function expression
var greetFunc = function(name){
console.log("hello"+name);
};
greetFunc("John");
// results:
// hello John
// using IIFE
var greeting = function(name){
return "hello" + name;
}("John");
console.log(greeting);
console.log(greeting());
// results:
// hello John
// error, greeting is a string not a function.
3;
"This a string";
{
"a": "A",
"b": "B"
};
// 執行以上程式都不會出錯,
// 但若是 function 的話:
funtion(name){
// do something
};
// results:
//
// error
//
// 因為當 function 在該行一開始就出現時,
// JavaScript 會預期你是要寫 function statement,
// 所以它預期的東西是 function functionName(para),
// 所以會出錯。
要立即執行一個 function extrpeesion, 必須將 funtion 放進 () 中, 並且在最後用 () invoke 它。
var firstName = "Newt";
(function(name){
var greeting = "hello";
console.log(greeting + " " + name);
}(firstName));
// result:
//
// hello Newt
//
// 上面是一個典型的 IIFE
function 中的變數與全域環境隔絕。
// import a.js
var greeting = "hola";
// import b.js
(function(name){
var greeting = "hello";
console.log(greeting + " " + name);
}("John"));
// results
//
// hello John
a.js 與 b.js 彼此之間不會互相影響、衝突。
如果利用 IIFE 建立的 lib 想要使用全域物件, 則將 window 傳入:
var greeting = "hola";
(function(global, name){
var greeting = "hello";
global.greeting = "hello";
console.log(greeting + " " + name);
}(window, "John"));
console.log(greeting);
// results
//
// hello John
// hello
這也可以用來讓 lib 的某些功能影響全域物件, 可以要露出的東西寫入 global 中。
example
function greet(whatToSay){
return function(name){
;console.log(whatToSay + " " + name)
}
}
var sayHi = greet("Hi");
sayHi("Tony");
// results:
//
// Hi Tony
運作過程:
一開始,call greet() 創造出 execution context, 同時變數環境裡有 whatToSay => "Hi"。
在回傳一個 function 後, greet() 的 execution context 就結束了, 也就是離開 execution stack 了。
正常而言,execution context 結束後, 它所留下的記憶體空間會被(garbage collection)清除, 但在 closure 中的因為還被仍存在的 function 使用, 所以會留下來。
當執行 sayHi() 時, js engine 會順著 scope chain 向外尋找 whattosay 並順利找到當初在執行 greet("Hi") 時所留下的 whattosay。
function buildFunctions(){
var arr =[];
for(var i = 0; i < 3; i++){
arr.push(function(){
console.log(i);
});
}
return arr;
}
var fs = buildFunctions();
fs[0]();
fs[1]();
fs[2]();
// results:
//
// 3
// 3
// 3
invoke buildFunction() 會創造 execution context, 一路執行到最後要 return arr 時, i 等於 3, 而 arr 中存有 3 個獨立的 function。
然後 buildFunction() 結束, 記憶體空間留下。
所以之後 fs[] functions 執行時, 都會參照到 i = 3。
承上所述, 若是我們今天是想要輸出 1, 2, 3 的話, 則有兩種解法:
function buildFunctions(){
var arr =[];
for(var i = 0; i < 3; i++){
let j = i;
arr.push(function(){
console.log(j);
});
}
return arr;
}
var fs = buildFunctions();
fs[0]();
fs[1]();
fs[2]();
function buildFunctions() {
var arr = [];
for (var i = 0; i < 3; i++) {
arr.push(
(function(j) {
return function() {
console.log(j);
}
}(i))
);
}
return arr;
}
var fs = buildFunctions();
fs[0]();
fs[1]();
fs[2]();
function makeGreeting(language) {
return function(fristname, lastname) {
if (language === "en") {
console.log("hello " + firstname + " " + lastname);
}
if (language === "es") {
console.log("holai " + firstname + " " + lastname);
}
}
}
var greetEnglish = makeGreeting("en");
var greetSpanish = makeGreeting("es");
greetEnglish("John", "Doe");
greetSpanish("John", "Doe");
執行過程:
第一次 call makeGreeting()時, 會創造一個獨立 execution context 與 memory space, 裡面包含 language = "en"。
執行結束後, execution context 關閉, 但 memory space 留下。
第二次 call makeGreeting(); 再一次創造另外一個獨立的 execution context。
執行結束後, exexution context 關閉, 但 memory space 同樣會留著。
執行 greetEnglish("John", "Doe");
執行 greetSpanish("John", "Doe");
function sayHiLater(){
var greeting = "Hi!";
setTimeout(function(){
console.log(greeting);
}, 3000);
}
sayHiLater();
// results:
//
// (after 3 seconds)
// Hi!
用來操控 this 為何。
function 是一種特殊的物件, 因為是物件所以可以含有方法, call(), apply() 和 bind() 便是 function 都擁有的方法。
this 錯的狀況:
var person = {
firtname: "John",
lastname: "Doe",
getFullName: function() {
// 這裡的 this 會指向整個 person 物件,
// 因為它是由 person 的 method 所 invoke
var fullName = this.firtname + this.lastname;
return fullName;
}
}
var logName = function(lang1, lang2) {
console.log("Logged:" + this.getFullName());
}
logName();
// results:
//
// error
// 因為 logName 中的 this 是指向全域物件 window。
所以將上面的 code 改成:
var person = {
firtname: "John",
lastname: "Doe",
getFullName: function() {
var fullName = this.firtname + this.lastname;
return fullName;
}
}
var logName = function(lang1, lang2) {
console.log("Logged:" + this.getFullName());
}
// 用一個變數去接新的 function
var logPersonName = logName.bind(person);
logPersonName();
// results:
//
// Logged John Doe
所以將上面的 code 改成:
var person = {
firtname: "John",
lastname: "Doe",
getFullName: function() {
var fullName = this.firtname + this.lastname;
return fullName;
}
}
var logName = function(lang1, lang2) {
console.log(lang1 + lang2 + this.getFullName());
}.bind(person);
logName();
logName("en ", "es ")
// results:
//
// undefined undefined John Doe
// en es John Doe
不同於 bind() 是回傳一個新函式, call() 跟 apply() 會直接執行該函式, 兩者的差別在於傳參數的方式。
延續上面的例子:
var person = {
firtname: "John",
lastname: "Doe",
getFullName: function() {
var fullName = this.firtname + this.lastname;
return fullName;
}
}
var logName = function(lang1, lang2) {
console.log(lang1 + lang2 + this.getFullName());
};
logName.call(person, "en ", "es ");
logName.apply(person, ["en ", "es "]);
// results
//
// en es JohnDoe
// en es JohnDoe
using .call() or .apply()
利用改變 execution context 的 this 來讓 object 可以借用其他 object 的 method。
var person2 = {
firstname: "Jane",
lastname: "Doe"
};
console.log(person.getFullName.apply(person2));
// results:
//
// JaneDoe
using .bind(),
拷貝一個 function,並可以限制它的參數。
function multiply(a, b){
return a*b;
}
// 設定第一個參數永遠是 2
var multipleByTwo = multiply.bind(this, 2);
console.log(multipleByTwo(3));
// result:
//
// 6