顯示廣告
隱藏 ✕
看板 KnucklesNote
作者 Knuckles (站長 那克斯)
標題 [JS] 克服JS的奇怪部分 ch9 來打造一個框架/資源庫!
時間 2016-12-12 Mon. 00:58:57


Udemy課程: JavaScript全攻略:克服JS的奇怪部分
https://www.udemy.com/javascriptjs/learn/v4/overview
上完第九章的心得筆記

章節9 來打造一個框架/資源庫!

73. 需求

框架命名: Greetr

輸入姓名,選擇性輸入語言
產生正式和非正式的問候

語言支援 English 和 Spanish

必需能重覆使用,不會跟其他JS程式衝突

可用簡寫 G$() 來執行

支援 jQuery


74. 打造安全的程式

先建立一個 HTML 檔,會載入 jQuery、Greetr.js、app.js 三個JS檔
<html>
    <head>

    </head>
    <body>
        <script src="jquery-1.11.2.js"></script>
        <script src="Greetr.js"></script>
        <script src="app.js"></script>
    </body>
</html>
我們要打造的框架會寫在 Greetr.js

在 Greetr.js 中,我們首先要用立即執行函數
將我們的框架程式全部包起來,讓他有一個安全的執行環境
避免與全域環境衝突

在這個執行環境產生時,要傳入全域物件 window,以及 jQuery 物件
(function(global, $){

	
// 框架程式內容

})(window, jQuery);


75. 我們的物件與其原型

我們希望在建立 Greetr 物件時,可以像 jQuery 那樣執行一個函數即可
不需要加上 new 運算子,像這樣
var g = G$(firstName, lastName, language);

所以在 Greetr.js 中,我們要將 Greetr 建立為一個一般函數
然後在 Greetr 函數中再使用函數建構子來產生一個物件

函數建構子要將能輸入三個參數 firstName, lastName, language
若沒輸入時要有預設值

在函數建構子中將這三個輸入值設定為建立物件的屬性

試著依照 jQuery 的模式寫寫看,結果會像這樣
(function(global, $){

	
var Greetr = function(firstName, lastName, language){

	
	
return new Greetr.init(firstName, lastName, language);
	
}

	
Greetr.init = function(firstName, lastName, language){

	
	
var self = this;
	
	
self.firstName = firstName || '';
	
	
self.lastName = lastName || '';
	
	
self.language = language || 'en';

	
}

})(window, jQuery);
將 Greetr 建立為一般函數,會輸入三個參數
在函數裡用函數建構子 Greetr.init 建立物件

接著建立函數建構子 Greetr.init
先將產生物件的 this 存成 self
然後將三個輸入值存成物件的屬性


再來我們想要在一般函數 Greetr 的屬性 prototype 中加上一些函數
	
Greetr.prototype = {
	
	
// ...
	
}
讓產生的物件可以從原型呼叫這些函數來用
但物件實際上是由函數建構子 Greetr.init 產生的
物件的原型會指向 Greetr.init.prototype

所以要將 Greetr.init.prototype 改成指向 Greetr.prototype
	
Greetr.init.prototype = Greetr.prototype;

然後將 Greetr 函數存成全域變數 Greetr 與 G$
	
global.Greetr = global.G$ = Greetr;

加進程式後變成像這樣
(function(global, $){

	
var Greetr = function(firstName, lastName, language){

	
	
return new Greetr.init(firstName, lastName, language);
	
}

	
Greetr.prototype = {
	
	
// ...
	
}

	
Greetr.init = function(firstName, lastName, language){

	
	
var self = this;
	
	
self.firstName = firstName || '';
	
	
self.lastName = lastName || '';
	
	
self.language = language || 'en';

	
}

	
Greetr.init.prototype = Greetr.prototype;

	
global.Greetr = global.G$ = Greetr;

})(window, jQuery);

現在我們可以在 app.js 中使用 G$ 建立 Greetr 物件了
var g = G$('John', 'Doe');
console.log(g);
執行結果
[圖]



76. 屬性與可鍵結方法

接下來幫 Greetr 加上一些功能

之前我們在函數建構子 Greetr.init 中,使用 self 建立的三個物件屬性,
這些屬性在每個新建立的物件都會另外存一份,
每個產生的物件儲存的屬性值可以不相同

為了節省記憶體空間,如果我們想要有些屬性或成員函數是所有產生的物件共享的,
那應該要放在哪裡呢

如果有某些變數我只想在框架程式中使用,不想被外面的程式取用,
建立物件時,也不會變成物件的一部份,
要放在哪邊呢

參考以下程式
(function(global, $) {

	
var Greetr = function(firstName, lastName, language) {
	
	
return new Greetr.init(firstName, lastName, language);   
	
}

	
//只有在框架中使用,不讓外面程式取用的變數:

	
var supportedLangs = ['en', 'es']; //可使用的語言

	
var greetings = { //不同語的招呼語
	
	
en: 'Hello',
	
	
es: 'Hola'
	
};

	
var formalGreetings = { //不同語言的正式招呼語
	
	
en: 'Greetings',
	
	
es: 'Saludos'
	
};

	
var logMessages = { //不同語言的log訊息
	
	
en: 'Logged in',
	
	
es: 'Inició sesión'
	
};

	
Greetr.prototype = {

	
	
//建立的物件會共用的成員函數:

	
	
fullName: function() {
	
	
	
return this.firstName + ' ' + this.lastName;   
	
	
},

	
	
validate: function() {
	
	
	
 
if (supportedLangs.indexOf(this.language)  === -1) {
	
	
	
	
throw "Invalid language";   
	
	
	
 
}
	
	
},

	
	
greeting: function() {
	
	
	
return greetings[this.language] + ' ' + this.firstName + '!';
	
	
},

	
	
formalGreeting: function() {
	
	
	
return formalGreetings[this.language] + ', ' + this.fullName(); 
	
	
},

	
	
greet: function(formal) {
	
	
	
var msg;

	
	
	
// if undefined or null it will be coerced to 'false'
	
	
	
if (formal) {
	
	
	
	
msg = this.formalGreeting(); 
	
	
	
}
	
	
	
else {
	
	
	
	
msg = this.greeting(); 
	
	
	
}

	
	
	
if (console) {
	
	
	
	
console.log(msg);
	
	
	
}

	
	
	
// 'this' refers to the calling object at execution time
	
	
	
// makes the method chainable
	
	
	
return this;
	
	
},

	
	
log: function() {
	
	
	
if (console) {
	
	
	
	
console.log(logMessages[this.language] + ': ' + this.fullName()); 
	
	
	
}

	
	
	
return this;
	
	
},

	
	
setLang: function(lang) { //改變要使用的語言
	
	
	
this.language = lang;

	
	
	
this.validate();

	
	
	
return this;
	
	
}

	
};

	
Greetr.init = function(firstName, lastName, language) {

	
	
var self = this;
	
	
self.firstName = firstName || '';
	
	
self.lastName = lastName || '';
	
	
self.language = language || 'en';

	
}

	
Greetr.init.prototype = Greetr.prototype;

	
global.Greetr = global.G$ = Greetr;

}(window, jQuery));
在建立完 Greetr 函數後,接著建立一些只有在框架中可使用的變數

supportedLangs 使用陣列存了兩個可以使用的語言
	
var supportedLangs = ['en', 'es'];

greetings 為了要使用不同語言做為索引,所以使用物件來存兩種招呼語
	
var greetings = {
	
	
en: 'Hello',
	
	
es: 'Hola'
	
};

接著的 formalGreetings 與 logMessages 也是用物件來存兩種語言的字串

這幾個變數只有在整個立即執行函數的範圍裡可以存取
外面的程式無法直接取用這幾個變數


接下來在 Greetr.prototype = { ... } 中,
加上要讓建立的物件可以共用的成員函數

因為 Greetr.prototype 之後會覆寫到函數建構子 Greetr.init 的原型上
	
Greetr.init.prototype = Greetr.prototype;
所以用函數建構子 Greetr.init 建立的物件
可透過原型鍵使用 Greetr.prototype 中建立的函數

例如建立一個函數 fullName,用來取得屬性 firstName 與 lastName
	
fullName: function() {
	
	
return this.firstName + ' ' + this.lastName;  
	
},
使用 this 可以讀取到建立物件時存進去的屬性

建立一個函數 validate,用來檢查建立物件時輸入的語言是否支援
	
validate: function() {
	
	
 if (supportedLangs.indexOf(this.language)  === -1) {
	
	
	
throw "Invalid language";  
	
	
 }
	
},
這邊可以取用到上面建立的 supportedLangs = ['en', 'es'];
是因為閉包的關係,變數會保留著,等到呼叫時還是可以取用

後面幾個函數 greet, log, setLang 最後會 return this;
是為了能使用方法鍵連續呼叫成員函數


在 Greetr.js 加上這一堆功能後,在 app.js 使用看看
var g = G$('John', 'Doe');
g.greet(); //顯示 Hello John!

//可以在執行完一個成員函數後,再直接執行另一個
g.greet()      //顯示 Hello John!
 .greet(true); //顯示 Greetings, John Doe

g.greet()       //顯示 Hello John!
 .setLang('es') //設定要顯示的語言
 .greet(true);  //顯示 Saludos, John Doe

//設定為不支援的語言
g.setLang('fr') //顯示錯誤 Uncaught Invalid language
 .greet(); //這行不會執行


77. 增加 jQuery 支援

之前寫的 greet() 是將招呼語顯示在開放者工具的 Console 上
在這一節我們利用 jQuery 將招呼語顯示在網頁上

在網頁上加上要用來顯示招呼語的元件,例如
<h1 id="greeting"></h1>

我們希望在 app.js 中,執行 Greetr 物件的 HTMLGreeting()
將招呼語顯示在網頁的 <h1 id="greeting"></h1> 元件上
像這樣
var g = G$('John', 'Doe');
g.HTMLGreeting('#greeting', true); //在網頁的<h1>顯示 Greetings, John Doe

修改 Greetr.js 在 Greetr.prototype = { ... } 中新增一個函數
        HTMLGreeting: function(selector, formal) {
            if (!$) {
                throw 'jQuery not loaded';   
            }
           
            if (!selector) {
                throw 'Missing jQuery selector';   
            }
           
            var msg;
            if (formal) {
                msg = this.formalGreeting();   
            }
            else {
                msg = this.greeting();   
            }
           
            $(selector).html(msg);
           
            return this;
        }
函數 HTMLGreeting 有兩個輸入值
第一個輸入值 selector 是給 jQuery 用來找出網頁元件的選擇器字串
例如我們要找出 id="greeting" 的元件,就傳入 "#greeting"

第二個輸入值 formal 是個布林值代表要用正式還是非正式的招呼語

先檢查 jQuery 物件 $ 有沒有值,輸入值 selector 有沒有值

接著依照 formal 的值選擇要執行 formalGreeting() 還是 greeting()
來取得招呼語 msg

取得招呼語 msg 後執行 $(selector).html(msg);
先用 $(selector) 取得網頁元件轉為 jQuery 物件後
使用 jQuery 的函數 html() 將 msg 填入網頁的元件中

最後加上 return this; 讓這個函數可使用方法鏈


79. 來使用我們的框架吧

使用 jQuery 以及 Greetr 來實作一個簡單的登入網頁
http://knuckles.disp.cc/test/jsWeirdPart/ch9/79/

在網頁上有一個選擇框,可以選擇 English 或 Spanish
有一個按鈕 Login,點擊按鈕後會隱藏選擇框和按鈕
然後顯示招呼語 (假設已經知道使用者的名稱了)

網頁的HTML為
<html>
    <head>

    </head>
    <body>
        <div id="logindiv">
            <select id="lang">
                <option value="en">English</option>
                <option value="es">Spanish</option>
            </select>
            <input type="button" value="Login" id="login" />
        </div>
        <h1 id='greeting'></h1>
        <script src="jquery-1.11.2.js"></script>
        <script src="Greetr.js"></script>
        <script src="app.js"></script>
    </body>
</html>

在 app.js 中實作我們想要的功能
// 使用 jQuery 在 Login 按鈕的點擊事件執行一個匿名函數
$('#login').click(function() {
   
    //建立一個 Greetr 物件 (假設我們已取得使用者的名字了)
    var loginGrtr = G$('John', 'Doe');
   
    //隱藏選擇框和按鈕
    $('#logindiv').hide();
   
    //執行 loginGrtr 物件的成員函數
    loginGrtr.setLang($('#lang').val()) //取得選擇框的值,傳入 setLang()
        .HTMLGreeting('#greeting', true) //在<h1>顯示招呼語
        .log(); //在Console顯示執行記錄

});






--
※ 作者: Knuckles 時間: 2016-12-12 00:58:57
※ 編輯: Knuckles 時間: 2016-12-12 23:19:59
※ 看板: KnucklesNote 文章推薦值: 0 目前人氣: 0 累積人氣: 1399 
分享網址: 複製 已複製
r)回覆 e)編輯 d)刪除 M)收藏 ^x)轉錄 同主題: =)首篇 [)上篇 ])下篇