# 物件與函數 ## Objects and functions ### Objects and the dot 物件裡面可以包含三種東西 - Primitive (property) - Object (property) - Function (method) 核心物件會有一個記憶體的位址,可以參考到這些電腦記憶體中的屬性(property)和方法(method)的位址空間,如下圖。 ![](https://i.imgur.com/awNve2a.png) ```javascript= var person = new Object() person["firstname"] = "Tony" person["lastname"] = "Alicea" console.log( person.firstname + person["lastname"] ) // TonyAlicea var key = "firstname" console.log(person[key]) // Tony ``` ## Objects and Object Literals (物件實字) ```javascript= // * Same thing var a = new Object() var b = {} var person = { firstname: 'Tony', lastname: 'Alicea', } ``` ## JSON and Object Literals JSON => JavaScript Object Notation ```javascript= var objectLiteral = { firstname: 'Mary', IsAProgrammer: true, } console.log(JSON.stringify(objectLiteral)) // 會解析成 JSON var jsonValue = JSON.parse({ "firstname": "Mary", "isAProgrammer": true, }) // 解析成物件 ``` 補充 [XML](https://zh.wikipedia.org/zh-tw/XML) 早期傳資料的做法 ## Functions are Objects ### First Class Functions (一級函數) Everything you can do with other types you can do with functions Assign them to variables, pass them around, create them on the fly. ![](https://i.imgur.com/XAK9XZz.png) ## Function Statements and Function Expressions ### Expression A unit of code that results in a value It doesn't have save to a variable ```javascript= var anonymousGreet = function(){ console.log('hi') } anonymousGreet() // hi function log(a){ a() } log(function(){ console.log('hi') }) // hi ``` ## Conceptual Aside - By Value vs By Reference - by Value - ![](https://i.imgur.com/BClTr3p.png) - by Reference - ![](https://i.imgur.com/4t3bZU7.png) ```javascript= // by value var a = 2 var b = 3 b = a a = 3 console.log(a) // 3 console.log(b) // 2 // by reference (all objects (including functions)) var c = {greeting: 'hi'} var d d = c c.greeting = 'hello' // mutate console.log(c) // {greeting: 'hello'} console.log(d) // {greeting: 'hello'} // by reference (even as parameters) function changeGreeting(obj){ obj.greeting = 'Hola' // mutate } changeGreeting(d) console.log(c) // {greeting: 'Hola'} console.log(d) // {greeting: 'Hola'} // equals operator sets up new memory space (new address) c = {greeting: 'howdy'} console.log(c) // {greeting: 'howdy'} console.log(d) // {greeting: 'Hola'} ``` ## Mutate To change something "Immutable" means it can't be changed. ## Objects, Functions, and 'this' ![](https://i.imgur.com/0T59yVS.png) ```javascript= function a (){ console.log(this) this.newVariable = 'hello' } var b = function (){ console.log(this) } a() // window console.log(newVariable) // hello b() // window var c = { name: 'The c object', log: function(){ var self = this self.name = 'Updated c object' console.log(self) // Object of c // wrong and hardcore way var setname2 = function (newname){ this.name = newname } setname2('Updated again! The c object') console.log(this.name) // The c object // Proper way var setname1 = function (newname){ self.name = newname } setname1('Updated again! The c object') console.log(this.name) // Updated again! The c object } } ``` ## Arrays Collections of anything - 可以放任何型別到陣列 - 使用中括號裡面寫上 index (從0開始數),來呼叫陣列的其中一個 item ,eg: `arr[0]` ```javascript= var arr = [ 1, false, { name: 'Tony', address: '111 Main St.' }, function(name){ var greeting = 'Hello'; console.log(greeting + name); }, 'hello' ] console.log(arr) // 省略XD arr[3](arr[2].name) // HelloTony ``` ## `arguments` and spread - arguments => array-like - 有些瀏覽器不支援 arguments - spread parameter 多出來的參數會被包進 spread parameter 的陣列裡面 - spread 目前功能還沒有很完整 ```javascript= function greet(firstname, lastname, ...others){ console.log(firstname, lastname, others) console.log(arguments) } greet('John', 'Doe', 'en') // John Doe ['en'] ["John", "Doe", "en"] ``` ## 語法解析器 > 切記我們所撰寫的程式語言並不會直接被電腦執行,而是透過有個中介的程式在我們的程式與電腦之間轉換成電腦可以懂得指令。 這個編譯器裡面非常多功能與東西,其中一個就是我們要講的語法解析器,它會閱讀我們的程式碼是否有效,例如它會一個一個字看程式碼是否有效 return 變成 r-e-t-u-r-n,如果中間變成 r-e-t-r…語法解析器就會不認得你在講什麼,所以要記得這個規則。 否則你會很常看到這傢伙 `not defined`。 ## 自動插入分號 首先這個危險小叮嚀與 JavaScript 的語法解析器有關係也就是它的自動插入分號,我們前面瞭解那麼多都知道 JavaScript 語法解析器有多強大,它會盡可能幫助我們做一些事情,或許你已經注意到我們有沒有寫分號都沒有差異,甚至不是很必要,但為什麼這樣子?主要原因出在 JavaScript 幫我們自動插入分號,但這個動作有多危險? 一般來講語法解析器在解析語法是一個字一個字去檢查,舉例來講 r-e-t-u-r-n,解析完畢後若我們按下換行(carriage return 又稱 回車鍵、Enter)你會發生一些奇怪的事情 ,所以如果沒有分號或括號,JavaScript 就會幫我們補上它認為應該補的地方,所以我們必須理解到一件事情,並不是它並不需要分號而是 JavaScript 幫我們補上了,所以我們要盡可能避免讓 JavaScript 幫我們做決定補上分號,讓我們來看程式碼瞭解這個恐怖的問題 ↓ ## 立即呼叫的函數表示式(IIFEs) ### 前言 立即呼叫的函數表示式又稱為 IIFE,相信許多人已經知道這個東西,但實際 IIFE 是什麼,它是怎樣運作,又有什麼用,這就是本章節要探討的問題。 ### 函數陳述句與函數表示式 (function statement & function expression) 首先我們都已經知道函數陳述句與函數表示式的差異。 而這就是函數陳述句,當 JavaScript 執行完畢後只會將它放在記憶體中,並不會執行它,除非我們執行了 greet()。 ```javascript function greet() { console.log('Hello'); } greet(); ``` 然而函數表示式就像這樣,它一開始並不會被儲存到記憶體內,只有變數會被儲存至記憶體內,因為變數有提升效果,直到 JavaScript 執行這一行時就會立刻創造執行環境,這就是函數表示式。 ```javascript var greetFunction = function() { ... }; greetFunction(); ``` ### IIFE 簡單回顧了之前的函數陳述句與函數表示式後,我們可以對函數做一點特別的動作,讓它變成特別的物件,若我們希望在函數表示式後立刻呼叫它,而不是透過 greetFunction(); 呢? ```javascript var greetFunction = function() { console.log('Hi'); }(); ``` 只需要在後面加上括號就可以了,那如果我們帶入參數應該放哪?一樣在括號裡面帶入就好了。 ```javascript var greetFunction = function(name) { console.log('Hi ' + name); }('Jack'); ``` 而這觀念就是它會在函數被創造之後立刻呼叫它(執行),而這就是人家講的立即呼叫的函數表示式又稱 IIFE。 所以我們可以看看這兩者差異在哪裡。 ```javascript var greetFunctionTwo = function(name) { console.log('Hi ' + name); }; console.log(greetFunctionTwo); // IIEF var greetFunction = function(name) { console.log('Hi' + name); }(); console.log(greetFunction); ``` 我們可以發現不是使用 IIFE 會得到一整個函數,而 IIFE 直接會得到一個值,這也就是這兩者差異。 當然也有另一種撰寫方式。 ```javascript (function(name) { console.log('Hi' + name) })('Jack'); ``` 這也是人家講的匿名函數,我不需要名稱就可以直接執行的函數。 但若如果這樣呢? ```javascript function(name) { console.log('Hi' + name) } ``` 你會得到一個錯誤。 `Uncaught SyntaxError: Unexpected token` 因為當語法解析器第一行解析到 f-u…. 就會一直解析,而它就會想說 「哦!這是一個函數陳述句,所以等一下應該會有一個名稱」,結果我們並沒有給予函數名稱,所以就會得到一個錯誤。 那如果不要讓語法解析器認為這是一個函數陳述句,那就要在函數陳述句外圍包上括號,當它解析到括號,它就會知道 「哦!這是一個函數表示式」,就像前面講的範例一樣。 ```javascript (function(name) { console.log('Hi' + name) }) ``` 但是如果試著將這段程式碼貼上去執行會看到一個滿有趣的狀況。 它直接回傳一個函數,所以如果我們要正確呼叫它就在最後加上括號,而括號內也可以帶上參數。 ```javascript (function(name) { console.log('Hi' + name); })('Jack'); ``` 最後補充一個課程講的地方,這兩者 IIFE 都是一樣合理正確的,純粹只是美感問題。 ```javascript (function(name) { console.log('Hi' + name); })('Jack'); (function(name) { console.log('Hi' + name); }('Jack')); ``` ## 閉包(一) ### 前言 這章節將會開始講解一個非常恐怖又惡名昭彰的東西「閉包」(closuere),由於我們要深入瞭解這個程式語言,所以閉包就是一個非常重要的觀念,非常多人對於閉包非常感到混淆甚至討厭,因為這真的很難懂。 ### 閉包 由於一開始我們必須先撰寫一些程式碼來展示閉包的威力,然後再來瞭解它背後的機制及好用之處 ↓ ```javascript function greet(whattosay) { return function(name) { console.log(whattosay + ' ' + name); } } ``` 這時候你可能會說,我該如何呼叫放在 return 中的函數,其實只要這樣就可以了 ↓ ```javascript function greet(whattosay) { return function(name) { console.log(whattosay + ' ' + name); } } greet('Hi')('Tomy'); ``` ![](https://i.imgur.com/3ThekIe.png) 雖然內部函數沒有 whattosay 變數,但因為範圍鏈所以會參考外部環境,進而找到 whattosay,這邊目前看起來還算合理。 但是接下來這個範例就比較不太正常了 ↓ ```javascript function greet(whattosay) { return function(name) { console.log(whattosay + ' ' + name); } } var sayHi = greet('Hi'); sayHi('Tony'); ``` ![](https://i.imgur.com/jri5EvX.png) 為什麼 sayHi 還是知道 whattosay 是什麼?你一定會說我為什麼這樣講,讓我們試著直接輸出 sayHi 你就會知道為什麼了 ↓ ```javascript function greet(whattosay) { return function(name) { console.log(whattosay + ' ' + name); } } var sayHi = greet('Hi'); console.log(sayHi); sayHi('Tony'); ``` ![](https://i.imgur.com/yz6Vdih.png) 我們可以看到只有內部函數,但完全沒有看到 whattosay 的變數,那它到底是怎麼找到的? 首先當 greet 被執行時執行環境被建立,然後 whattosay 變數被傳入到環境內。 ![](https://i.imgur.com/E4WCefp.png) 接下來當 greet 執行完畢後回傳了一個物件函數,所以 greet 就離開了執行堆並留下了 whattosay 的變數。 ![](https://i.imgur.com/PUgimZc.png) 但是每一個執行環境都有屬於它自己的記憶體空間,所以若當執行環境消失了,記憶體空間會發生什麼事情呢? > 一般情況下會被清除,而這也稱之為垃圾回收 (Garbage collection)。 但是當執行環境結束時記憶體還是會存在那邊,雖然執行環境消失了,但變數一樣存在某處,然後就回到了全域環境狀況下,並開始執行 sayHi('Tony');。 ![](https://i.imgur.com/xbm7B7N.png) 那這時候的 sayHi('Tony'); 是一個匿名函數,類似像這樣 ↓ ```javascript var sayHi = function(name) { console.log(whattosay + ' ' + name); } ``` 接下來當我們執行匿名函數後都知道會出現 console.log(whattosay + ' ' + name);,但是我們會注意到裡面並沒有 whattosay 變數,所以 JavaScript 就會往外部環境去找,就是所謂的範圍鏈。 ![](https://i.imgur.com/QfmSs2H.png) 所以課程上有說明 JavaScript 為了避免我們找不到變數,所以會形成一個框框,將這一整個包住關起來,確保我們可以找的到東西。 ![](https://i.imgur.com/6Tf0V6D.png) 而這行為就是所謂的閉包。 ![](https://i.imgur.com/n0JFpW3.png) 而閉包只是 JavaScript 中的一個特色,確保我們找的到我們想要的東西而已,與我們何時呼叫函數並沒有任何直接關係。 ## 閉包(二) ### 前言 在網路上搜尋相關閉包知識時,其實都會找到類似的範例程式,但是如果沒有釐清觀念,頓時會覺得很難。 ### 閉包 所以在這一堂課算是驗證自己對於閉包的觀念是否已經清楚了,首先先準備一個函數 ↓ ```javascript function buildFunction () { var arr = []; for(var i = 0; i < 3; i++) { arr.push( function () { console.log(i); } ); } return arr; } ``` 接下來將執行 buildFunction() 存入變數 fs,所以這邊我們可以預期得到一個陣列 ↓ ```javascript function buildFunction () { var arr = []; for(var i = 0; i < 3; i++) { arr.push( function () { console.log(i); } ); } return arr; } var fs = buildFunction (); console.log(fs); ``` ![](https://i.imgur.com/wimdemD.png) 所以我們可以這樣呼叫陣列裡面的函數,那預期應該會是什麼? ```javascript function buildFunction () { var arr = []; for(var i = 0; i < 3; i++) { arr.push( function () { console.log(i); } ); } return arr; } var fs = buildFunction(); console.log(fs); fs[0]() fs[1]() fs[2]() ``` ![](https://i.imgur.com/TEgCF2a.png) 可以看到答案是 3,絕對不是我下面沒有截圖。 為什麼當它到外部參數找 i 的時候會發現它們都一樣呢?回頭想一下執行堆。 首先在執行堆中 buildFunction() 會開始執行,然後跑三次,然後匿名函數會被建立,可是它們只是建立並沒有被執行,只是將函數放進去陣列中 (arr),接下來執行完畢就回傳 arr,所而 i 在執行完畢後最終停在 3,故而被保存在記憶體中,就像課程簡報一樣。 ![](https://i.imgur.com/XHEdsav.png) 它們的環境是處於閉包,在匿名函數裡面它們找不到 i,所以會從範圍鏈去取得 i,而 i 目前為 3,這也是為什麼 i 會是 3 的原因。 ![](https://i.imgur.com/4QZC4cg.png) 但是如果我們希望是輸出 0…1…2 呢?那就這樣做。 ```javascript function buildFunction () { var arr = []; for(var i = 0; i < 3; i++) { let j = i; arr.push( function () { console.log(j); } ); } return arr; } var fs = buildFunction(); console.log(fs); fs[0]() fs[1]() fs[2]() ``` ![](https://i.imgur.com/067gtmZ.png) 這樣就可以達到我們要的效果,而這邊主要使用的是 ES6 的 let,let 的特性就是只會存活在這個區塊,所以當這個區塊結束或離開後,就會被消滅,但在這邊 j 會被儲存在不同的記憶體位置中,所以每次 for 迴圈執行一次,就會產生一個新的記憶體位置的 j。 那另一種作法呢? ```javascript function buildFunction () { var arr = []; for(var i = 0; i < 3; i++) { let j = i; arr.push( (function () { console.log(j); }) ); } return arr; } var fs = buildFunction(); console.log(fs); fs[0]() fs[1]() fs[2]() ``` ![](https://i.imgur.com/ZvTe1BU.png) 這邊所活用的方式是先期講過的其中一種方式,因為要讓變數獨一無二那就要有一個唯一的執行環境,所以就是立刻執行環境 IIFE。 但是課程的寫法是這樣。 ```javascript function buildFunction () { var arr = []; for(var i = 0; i < 3; i++) { let j = i; arr.push( (function (j) { return function () { console.log(j); } })(i) ); } return arr; } var fs = buildFunction(); console.log(fs); fs[0]() fs[1]() fs[2]() ``` ![](https://i.imgur.com/iBhPOxr.png) 基本上我覺得課程所講的方式較妥當,因為可以確保有執行環境。 ## 閉包與回呼 ### 前言 一般來講如果我們在 JavaScript 使用過 setTimeout、jQuery,這些其實都是使用了閉包,所以這章節要來聊聊閉包和回呼 (callback)。 ### 閉包與回呼 首先我們先試著建立一個簡單的函數範例 ↓ ```javascript= function sayHiLater() { var greeting = 'Hi'; setTimeout(function() { console.log(greeting); }, 3000); } ``` 這個範例我們都知道會在三秒後出現 Hi,而這個過程其實我們已經使用了函數表示式及閉包。 而 jQuery 也是一樣,例如 click 事件。 ```javascript= $('button').click(function () { ... }); ``` 另外這也是一個閉包,從上面這兩個範例我們有可能已經使用過了閉包、函數表示式及一級函數。 ![](https://i.imgur.com/7daxcOm.png) 而回呼函數是當某個函數執行完你給他的執行函數,然後那個函數再回呼你給他的函數,在白話一點就是當某個函數執行完畢要給該函數執行的函數就稱為回呼。 讓我們從範例來看 ```javascript= function tellMeWhenDone(callback) { var a = 1000; var b = 2000; //當結束時呼叫另一個函數 callback(); } tellMeWhenDone(function() { console.log('做完了!'); }) tellMeWhenDone(function() { console.log('我還在做'); }) ``` 我們可以看到呼叫了兩次 tellMeWhenDone,然後參數 function 帶入 callback,當 tellMeWhenDone 執行完之後就會呼叫剛剛帶入的函數,這就是回呼。 簡單來講就是 function 執行完畢後再執行另一個。 ## call()、apply()與 bind() ### 前言 這節課將來討論 JavaScript 中的三個方法 call()、apple()、bind(),而這三個東西與我們前面所講的東西有關係。 在探討這三個方法之前必須先瞭解什麼一級函數,一開始我們已經知道一級函數是特殊形態 - 它可以有名稱及沒有名稱 (也就是匿名函數) - 它有程式區塊,裡面可以撰寫程式碼,然後程式是可以被呼叫 - 函數就是物件,所以本身也可以有屬性及方法 - 所有函數都包含著 call()、apply() 與 bind(),而這三個都與 this 以及我們傳入的參數有關。 ![](https://i.imgur.com/sH0jFr9.png) ### call()、apply()與 bind() 接下來我們試著跟著課程範例了解。 ```javascript= var person = { fistname: 'John', lastname: 'Doe', getFullName: function() { var fullname = this.fistname + ' ' + this.lastname; return fullname; }, }; ``` 那 call()、apply()與 bind() 該怎麼使用呢?假設我們有一個 logName 匿名函數 ```javascript= var logName = function (lang1, lang2) { console.log('Logged' + this.getFullName()); } logName(); ``` 課程來看,其實我們大概都可以知道這樣會出現錯誤,因為 this 指向的是 window,而 window 下並沒有一個函數叫 getFullName。 ### bind 但是如果今天我們可以控制 this 指向誰的話呢?所以這邊就要試著使用看看 bind ```javascript= var person = { fistname: 'John', lastname: 'Doe', getFullName: function() { var fullname = this.fistname + ' ' + this.lastname; return fullname; }, }; var logName = function (lang1, lang2) { console.log('Logged' + this.getFullName()); } var logPersonName = logName.bind(person) // 把函數當作物件使用 logPersonName(); ``` bind 這個方法會回傳新的函數,當 JavaScript 看到 bind 將會重新指向 this 到傳入 bind 方法的東西。 所以 bind 創造任何我們呼叫的函數拷貝,不論我們傳入函數還是物件都可以。 ```javascript= var person = { fistname: 'John', lastname: 'Doe', getFullName: function() { var fullname = this.fistname + ' ' + this.lastname; return fullname; }, }; var logName = function (lang1, lang2) { console.log('Logged: ' + this.getFullName()); console.log('Arguments: ' + lang1 + ' ' + lang2) console.log('-----------'); } var logPersonName = logName.bind(person) // 把函數當作物件使用 logPersonName(); logPersonName('en'); ``` ### call ```javascript= var person = { fistname: 'John', lastname: 'Doe', getFullName: function() { var fullname = this.fistname + ' ' + this.lastname; return fullname; }, }; var logName = function (lang1, lang2) { console.log('Logged: ' + this.getFullName()); console.log('Arguments: ' + lang1 + ' ' + lang2) console.log('-----------'); } logName.call(person) // 也可以用這種方式呼叫。 ``` call 與 bind 一樣可以傳入要拷貝的函數,但是可以傳入參數並執行他 ```javascript= var person = { fistname: 'John', lastname: 'Doe', getFullName: function() { var fullname = this.fistname + ' ' + this.lastname; return fullname; }, }; var logName = function (lang1, lang2) { console.log('Logged: ' + this.getFullName()); console.log('Arguments: ' + lang1 + ' ' + lang2) console.log('-----------'); } logName.call(person,'en','es') ``` > bind 只是單純拷貝並沒有執行函數,但 call 可以拷貝並執行函數。 ### apply ```javascript= var person = { fistname: 'John', lastname: 'Doe', getFullName: function() { var fullname = this.fistname + ' ' + this.lastname; return fullname; }, }; var logName = function (lang1, lang2) { console.log('Logged: ' + this.getFullName()); console.log('Arguments: ' + lang1 + ' ' + lang2) console.log('-----------'); } logName.apply(person,'en','es'); ``` 但是如果我們試著跟 call 一樣傳入參數就會出現錯誤。 所以應該怎麼使用 apply?,apply 。主要接受的參數是陣列,而不是普通的字串、數值等等。 ```javascript= var person = { fistname: 'John', lastname: 'Doe', getFullName: function() { var fullname = this.fistname + ' ' + this.lastname; return fullname; }, }; var logName = function (lang1, lang2) { console.log('Logged: ' + this.getFullName()); console.log('Arguments: ' + lang1 + ' ' + lang2); console.log('-----------'); } logName.apply(person, ['en', 'es']); ``` 我們可以從上面發現 apply 非常強大,在傳入參數後會自動幫我們對應載入相對應的參數。 所以我們這邊也可以透過匿名函數 + apply or call 立刻執行呼叫。 ```javascript= var person = { fistname: 'John', lastname: 'Doe', getFullName: function() { var fullname = this.fistname + ' ' + this.lastname; return fullname; }, }; (function (lang1, lang2) { console.log('Logged: ' + this.getFullName()); console.log('Arguments: ' + lang1 + ' ' + lang2); console.log('-----------'); }).apply(person, ['en', 'es']); (function (lang1, lang2) { console.log('Logged: ' + this.getFullName()); console.log('Arguments: ' + lang1 + ' ' + lang2); console.log('-----------'); }).call(person,'en','es'); ``` ### 真實例子 #### 函數借用 (function borrowing) ```javascript= var person = { fistname: 'John', lastname: 'Doe', getFullName: function() { var fullname = this.fistname + ' ' + this.lastname; return fullname; }, }; var person2 = { fistname: 'Jack', lastname: 'Mary', }; person.getFullName.apply(person2); ``` 在這個例子我們可以將原有 person 底下的函數 (getFullName) this 轉指到 person2。 而 call 也是一樣。 ```javascript= var person = { fistname: 'John', lastname: 'Doe', getFullName: function() { var fullname = this.fistname + ' ' + this.lastname; return fullname; }, }; var person2 = { fistname: 'Jack', lastname: 'Mary', }; person.getFullName.call(person2); ``` #### function currying 這個例子與 bind 有關,在前面使用 call 及 apply 傳入參數就只是傳入參數,但是 bind 可以創造函數拷貝。 ```javascript= function multiply(a, b) { return a * b; }; var multpleByTwo = multiply.bind(this, 2); multpleByTwo(); ``` > 注意 bind 只會拷貝並不會執行,但給他的參數將會被設定成拷貝函數的永久參數值,所以當我們第一個參數放 2 代表永遠都是 2 , a 永遠都是 2。 此時你可能會問怎麼是 NaN?試著思考一下變數的預設值是什麼 undefined,當 2 * undefined 那當然會是 NaN,這並不是一個數值,所以我們可以在執行函數時傳入數字。 ```javascript= function multiply(a, b) { return a * b; }; var multpleByTwo = multiply.bind(this, 2); multpleByTwo(3); ``` ![](https://i.imgur.com/KwS0aPZ.png) ## 函數程式設計(一) ### 前言 在討論完一級函數及各種 JavaScript 之後準備來討論講師喜歡的主題【函數程式設計 (function Programmin)】 ### 函數程式設計 (function Programmin) 以往我們很多人都會以為 JavaScript 會跟 Java 有關係,甚至看起來很像 C++、C#等,但講師是說 JavaScript 和函數程式語言比較有關係(Lisp、Scheme、ML),所以我們從範例來了解什麼是 函數程式設計 (function Programmin)。 ```javascript= var arr1 = [1, 2, 3]; console.log(arr1); var arr2 = []; for (var i = 0; i< arr1.length; i++) { arr2.push(arr1[i] * 2); } console.log(arr2); ``` 通常身為程式設計師我們都會盡可能減少自己的工作量,所以都會將重複性較高的動作包裝至一個函數內,那如果在沒有一級函數的狀況下我們所做的事情是有限的,所以我們可以利用一級函數來製作函數程式設計,然後將上面這個範例做修改。 ```javascript= function mapForEach(arr, fu) { var newArr = []; for (var i = 0; i< arr1.length; i++) { newArr.push( fu(arr[i]) ); } return newArr; } var arr1 = [1, 2, 3]; var arr2 = mapForEach(arr1, function(item) { return item * 2; }); console.log(arr2) ``` 我們可以看到 函數程式設計 的強大,所以也可以拿來做比較。 ```javascript= function mapForEach(arr, fu) { var newArr = []; for (var i = 0; i< arr1.length; i++) { newArr.push( fu(arr[i]) ); } return newArr; } var arr1 = [1, 2, 3]; var arr2 = mapForEach(arr1, function(item) { return item > 2; }); console.log(arr2); ``` 這樣就可以重複不停地利用函數,而這就是 函數程式設計 的經典例子。 那如果拿來檢查是否有超過特定數值呢? ```javascript= function mapForEach(arr, fu) { var newArr = []; for (var i = 0; i< arr1.length; i++) { newArr.push( fu(arr[i]) ); } return newArr; } var arr1 = [1, 2, 3]; var checkPastLimt = function (limiter, item) { return item > limiter; } var arr2 = mapForEach(arr1, checkPastLimt.bind(this, 1)); console.log(arr2); ``` 但是有些時候 bind 很煩,所以這邊可以試著不要在 mapForEach(arr1, checkPastLimt.bind(this, 1)) 使用 bind 來限制值。 ```javascript= function mapForEach(arr, fu) { var newArr = []; for (var i = 0; i< arr1.length; i++) { newArr.push( fu(arr[i]) ); } return newArr; } var arr1 = [1, 2, 3]; var checkPastLimt = function (limiter, item) { return item > limiter; } var checkPastLimtSimplified = function (limiter) { return function (limiter, item) { return item > limiter; }.bind(this, limiter) } var arr2 = mapForEach(arr1, checkPastLimtSimplified(2)); console.log(arr2); ``` ## 函數程式設計(二) ### 前言 前面我們已經了解到 函數程式設計 的強大,所以這堂課將會花點時間來講一些資源例子。 ### underscore.js underscore.js 是一個滿有名的 JavaScript 資源庫,可以幫助我們處理陣列及物件。 講師也建議我們可以從這些開源資料來了解進而學習,但要挑戰之前我們一定要有觀念,所以這堂課也希望我們可以藉由開源的資料來幫助我們學習了解函數程式設計。 ### Lodash Lodash與 underscore 類似,但執行速度比 underscore 快。 但作者建議我們去 underscore 看一下學習了解 underscore 如何撰寫,這可以協助我們更加深了解函數程式設計。 最後我附上我找的幾個資源 [underscore.js (1.7.0) 中文版](http://www.bootcss.com/p/underscore/) [Underscore.js (1.9.1) 中文版](https://www.html.cn/doc/underscore/) [UnderscoreJS精巧而强大工具包](http://blog.fens.me/nodejs-underscore/) ###### tags: `WierdJavascript`