前言
前一篇筆記,我們完成的需求有:
是一個可重複使用的 library/framework,也就是說,每一個安裝此 framework 的人可以直接使用,不會和它原本程式碼有所衝突。和 jQuery 只需要輸入。$( )
一樣,我們可以使用G$()
來建立物件
剩下的需求有:
當我們告訴它我們的姓(lastname)名(firstname)還有選擇的語言(language)時,它可以用正式(formal)和非正式(informal)的方式和我們打招呼。
支援英文(English)和中文(Chinese)兩種語言。
支援 jQuery,可以把 greetr 產生的訊息直接顯示於HTML中。
新增一些變數 & 方法
建立全域環境無法取用的變數
在開始建立方法(method)前,我想要先建立一些變數是我之後可以在方法中使用的,但這些變數又不會和外層的 global environment 有所衝突也不能被外層所取用,要如何達到呢?
參考:
[筆記] JavaScript中Scope Chain和outer environment的概念
[筆記] 談談JavaScript中closure的概念 – Part 1
1 | ;(function(global, $){ |
我們不用放在 prototype 或 function constructor 裡面,這樣會佔據額外的記憶體位置。
直接將變數建立在 IIFE裡 就可以了,通過 scope chain的特性,外部環境 (這邊相對於IIFE的外部環境就是window) 是參照不到這個 IIFE 內的變數的。
透過以上程式碼其實我們已經產生一個閉包 Closure。透過這種方法,我們可以讓使用 framework 的人沒有辦法去改這些變數的值,但是在使用 method
的時候,仍然可以參考到這些變數。
建立更多變數
1 | ;(function(global, $){ |
開始建立方法(method)
方法該寫在哪 ?
有兩個地方可以放置我們想要的方法,分別是如下圖的(1)和 (2)。但是如果放在這個 function constructor 中(2),變成每一個所建立的物件都會直接帶有這個方法,如此會佔據相當多的記憶體空間;所以比較好的方式是利用原型的概念,把根據這個 function constructor 所建立的物件,都可以使用到的方法,放到(1) 的 prototype 當中。
參考:
JS的原型繼承(方法1) - 函數建構子 與「new」 - 我們不該把方法放在 function constructor 中
開始撰寫程式碼
接著,我們要在 prototype 中開始建立一些 framework 裡面可以使用的方法,就像是jQuery的 animate()
或 attr()
這類的自訂函數。
我們先來看一下建立完方法後完整的程式碼長什麼樣子,這些程式碼都是放在Greetr.prototype
內,接著我們再來分別解釋每個方法的意義
1 | /* 屬性 */ |
方法解析
fullName() - 全名字串組合
1 | fullName: function() { |
這個比較單純,就是印出物件實體的名字屬性。
validate() - 驗證語言
1 | validate: function () { |
用來檢測使用者所輸入的語言有沒有支援,我們剛剛新增了一個陣列supportedLangs = ['en', 'ch']
,我們可以透過 indexOf
來檢測該物件的語言this.language
,如果沒有與陣列中任何一個值吻合,indexOf
會回傳 -1
,並透過 throw
在 console 裡丟出錯誤訊息。
indexOf
: 如果有吻合會回傳對應的陣列位置,例如輸入supportedLangs.indexOf('en')
,因為吻合supportedLangs = ['en', 'ch']
該陣列的第0個物件,所以會回傳0
。
greeting() - 非正式招呼
1 | greeting: function() { |
這樣寫的目的是要取得 該物件的語言
,並對應到剛剛宣告的物件 greetings = {en: 'Hello',ch: '你好'}
,把值給抓出來,如果 該物件的語言
的語言設定成 en
就抓出Hello
;ch
就抓出你好
。
formalGreetings() - 正式招呼
1 | formalGreetings: function() { |
基本上和上一個非正式招呼一樣,值得注意的是名字的部分它呼叫了this.fullName()
,這邊記得要加上this
,因為這個方法存在於這個物件的原型裡,而不是環境中。
greet() - 合併正式與非正式招呼。
1 | greet: function(formal) { |
透過 greet 這個方法,我可以直接控制我要使用的是 greeting()
或 formalGreeting()
,而不用打出這兩個方法。
- 如果我們給予的參數
formal
存在的話( if會型轉為true
,然後執行程式碼 ),那麼執行formalGreeting()
,否則使用greeting()
。 - 為了避免有些IE版本不支援 console 這個物件,我們用
if(console)
,意思是如果有 console 這個物件的話,再幫我輸出。 - 最重要的是
return this
,這是在模仿jQuery的鏈式 methods chainning,而這裡的this
指的也就是我們的物件,因此透過這個return this
,它可以先針對物件進行欲要進行的方法後,最後再次將它回傳成一個物件,於是,它就可以繼續在接著下一個方法,形成一個方法鍊的作法。
(方法鏈)鏈式簡易範例:
1 | var person = { |
person.method()
執行後 person.name
會被改為 Eva
,然後透過return this
回傳一個改名後的新物件,再接著執行新物件.method2()
。
log() - 針對不同語言,在console印出登入訊息+使用者名字
1 | log: function() { |
使用的技巧和上面重複。
setLans() - 更改所設定的語言
1 | setLang: function(lang) { |
設定新語言,呼叫上面寫過的 validate()
驗證語言是否支援,且一樣使用鏈式的 return this
。
測試功能
接著在 app.js 中,我們可以試著來測試一下所建立的 framework 。
1 | var person = G$('Dylan', 'Liu'); // language 預設是 ch |
我就可以得到以下的結果。因為我們有使用了鏈式的技巧,所以我可以一個接著一個方法的使用,當中我又用了 setLang()
這個方法,把預設的語言改成英文:
結果
1 | var person = G$('Eva', 'Lin'); // language的預設是ch |
首先會回傳登入的訊息,接著因為我把語言設成 “jp” ,但框架並不支援日語,所以回拋出錯誤的訊息。
完整程式碼
1 | ;(function (global, $) { |