【D1n910】學(xué)習(xí)《JavaScript 設(shè)計(jì)模式》(1[1/2]/6)
正常操作,正常分析,大家好,我是D1n910。
本系列【D1n910】學(xué)習(xí)《JavaScript 設(shè)計(jì)模式》(1/6)正式開坑了!
這本書是我很久以前買的,現(xiàn)在在看了《你不知道的JavaScript》等系列以后,再看這些書,就感覺好了很多。思想層面上提高了一點(diǎn)嘛。
我本來想隆重介紹下作者的,但是看到作者現(xiàn)在在開培訓(xùn)班了,我個(gè)人之前參加過UI設(shè)計(jì)的培訓(xùn)班,結(jié)果不是很好,所以這邊就不介紹了。
這些紛紛擾擾,我等不便參與,還是沉浸在學(xué)習(xí)的海洋里吧!

本期我們學(xué)的是 第一篇 面向?qū)ο缶幊?,?1 章 靈活的語言 —— JavaScript


github倉庫地址
https://github.com/D1N910/JavaScript-design-patterns

第一篇 面向?qū)ο缶幊?/span>
第 1 章 靈活的語言 —— JavaScript
1.1、入職第一天
剛剛畢業(yè)的小新入職了大廠前端。

在廠里小新是用原生 JavaScript 開發(fā)代碼的。
今天產(chǎn)品經(jīng)理給小新分配了一個(gè)任務(wù),讓小新去做一個(gè)驗(yàn)證表單功能的任務(wù)。
小新是有2年大學(xué)打代碼任務(wù)的老前端了好吧。
這個(gè)任務(wù)要驗(yàn)證用戶名稱,小新一看,啪,很簡單啊。
馬上用 JavaScript 閃電編寫了下面的內(nèi)容:
function checkName() {
? ? // 檢查用戶名稱
}
function?checkEmail()?{
? ? // 檢查用戶郵箱
}
……
心滿意足地提交了一個(gè) pr (合并代碼申請(qǐng))到團(tuán)隊(duì)項(xiàng)目里。
這時(shí)候負(fù)責(zé)走讀代碼
(走讀代碼:看看代碼有沒有問題,沒問題就合并代碼,有問題就不合并代碼并進(jìn)行討論溝通解決問題)
的阿蛋看了這個(gè) PR。
“寶貝,這個(gè)有問題,你要改一改。你創(chuàng)建了好多個(gè)全局變量。要不得?!?/p>
“啊,這?這是變量,這不是函數(shù)嗎?”
“函數(shù)難道不是變量嗎?”
阿蛋的反問,讓小新?lián)项^了:“函數(shù)怎么會(huì)是變量呢?”
1.2、函數(shù)的另一種形式
阿蛋噼里啪啦地打了這些內(nèi)容
var?checkName =? function ()?{
? ? // 檢查用戶名稱
}
var checkEmail =?function ()?{
? ? // 檢查用戶郵箱
}
……
“你看這樣和你之前的效果是不是一樣的。”
小新?lián)狭藫项^,“是一樣的,但這樣……”
“這樣就要在用的時(shí)候提前說明, 但是這樣就很明顯地發(fā)現(xiàn)你創(chuàng)建了3個(gè)變量,保存了函數(shù)來實(shí)現(xiàn)你的功能,和之前你用 function 定義是一樣的。所以說你也定義了3個(gè)全局變量。
這些用 function 定義的變量和 var 定義的全局變量一樣,也是直接掛在到 window 對(duì)象下的”
阿蛋打開控制臺(tái)給小新看了下面這張圖。小新果然發(fā)現(xiàn)確實(shí)是掛在到 window 對(duì)象下了。

“那這樣會(huì)有啥問題嗎?”
“從功能上來說當(dāng)然是沒問題的。你是一個(gè)人開發(fā)的時(shí)候這么寫沒問題,但是我們是一個(gè)團(tuán)隊(duì) ,你這么寫可能會(huì)影響到別人,別人寫了同樣名稱的變量也會(huì)覆蓋你的功能。如果你寫了大量的方法,那么這類的問題是比較難發(fā)現(xiàn)的?!?/p>
“那如何才能避免呢?”
“你可以把這些方法放到一個(gè)變量里保存,這樣就減少了覆蓋別人或者被覆蓋的風(fēng)險(xiǎn),而且一旦出現(xiàn)這個(gè)問題,也能更快地發(fā)現(xiàn)!”
1.3、用對(duì)象收編變量
對(duì)象,小新知道這個(gè)東西,就是 "{}" 嘛。
“是這么寫嗎”
小新寫了下面的代碼,把變量放到了 CheckObject 對(duì)象里。
var CheckObject =?{
????checkName:??function ()?{
? ? ????// 檢查用戶名稱
????},
????checkEmail:?function ()?{
? ? ????// 檢查用戶郵箱
????}
????……
}
“好啊,妙啊,你還是蠻懂的,這樣我們所有的函數(shù)變成了 CheckObject 對(duì)象下的方法了,用點(diǎn)語法就可以調(diào)用方法啦!比如檢測姓名 CheckObject.checkName() 。只要在原來的調(diào)用方法的基礎(chǔ)上,加上對(duì)象名稱就可以了。”
1.4、對(duì)象的另一種形式
我們也可以用點(diǎn)語法來在對(duì)象里創(chuàng)建方法。不過要先定義 CheckObject 為一個(gè)對(duì)象,在 JavaScript 中,function 也是對(duì)象,所以可以這么做。
var CheckObject = function() {}
CheckObject.checkName = function () {
? ? // 檢查用戶名稱
}
新創(chuàng)建的方法使用方式和之前是一樣的。但是這樣不太方便,別人使用的時(shí)候不能夠復(fù)制一份。
因?yàn)樵谠蹅冞@邊用的話,是希望能夠拷貝來用的,但是目前這個(gè)對(duì)象類在new來復(fù)制的時(shí)候,不能夠復(fù)制上剛剛點(diǎn)語法定義的函數(shù)。
就像這樣

* 實(shí)際上這里感覺蠻怪的,想要做復(fù)制拷貝的話,可以直接用 `{}` ,對(duì)象拷貝下也能用,不過這里是為了引出類的復(fù)制,也 OK,下面就說了這種對(duì)象拷貝的。
1.5、真假對(duì)象
如果只是想簡單復(fù)制,可以放到一個(gè)對(duì)象里返回
var checkObjectAnotherCopy = function () {
? ?return {
? ? ? ?checkObjectAnotherObjctCheckNameAnother: function () {
? ? ? ? ? ?console.log('1.5 真假對(duì)象 function checkObjectAnotherObjctCheckNameAnother already running');
????????}
????}
}
每次調(diào)用這個(gè)方法的時(shí)候,就會(huì)把新對(duì)象返回來的,所以明面上?checkObjectAnotherCopy,實(shí)際上用的都是新的對(duì)象,這樣每個(gè)人在使用時(shí)就互不影響了。
// 返回新對(duì)象
// 使用時(shí)候不會(huì)相互影響
var a = checkObjectAnotherCopy();
a.checkObjectAnotherObjctCheckNameAnother();
1.6、類也可以
上面這種方式創(chuàng)建出來的對(duì)象和原來的對(duì)象checkObjectAnotherCopy沒啥關(guān)系,我們希望創(chuàng)建真正的類。
var CheckObject = function ()?{
? ? this.checkName =?function ()?{
? ? ????// 檢查用戶名稱
????},
? ? this.checkEmail =?function ()?{
? ? ????// 檢查用戶郵箱
????}
}
像上面這樣寫,就創(chuàng)建了一個(gè)構(gòu)建函數(shù),也可以看作是一個(gè)類,可以這個(gè)類來創(chuàng)建對(duì)象。記得要用 new 關(guān)鍵字。
var c = new CheckObject();
c.checkName();
c.checkEmail();
這樣我們都可以對(duì)類實(shí)例話(用類創(chuàng)建對(duì)象),每個(gè)人都會(huì)有一套屬于自己的方法。
1.7、節(jié)約資源,共用同一個(gè)方法
這里的類創(chuàng)建有一個(gè)小的問題,就是每次創(chuàng)建的時(shí)候,內(nèi)部this上掛載的方法都要被重新定義一次,會(huì)造成一定的性能、資源消耗,可以把這些方法掛載到類的原型上。
var?CheckObject?= function () {}
checkObjectp.prototype.checkName=function(){
????console.log('1.7一個(gè)檢測類 function checkNameAnother already running');
}
// 也可以直接在 prototype 配置
checkObjectp.prototype = {
????checkName1: function () {
? ? ? ?????console.log('1.7 一個(gè)檢測類 function checkName1 already running');
? ?????},
????checkName2: function () {
? ????? ? ?console.log('1.7 一個(gè)檢測類 function checkName2 already running');
? ?????},
????checkName3: function () {
? ? ? ?????console.log('1.7 一個(gè)檢測類 function checkName3 already running');
? ?????}
}
這樣的話,后繼定義的對(duì)象都用的同一個(gè)方法了。
原因是因?yàn)檫@里存在 JavaScript 的原型鏈概念,用點(diǎn)語法訪問JS對(duì)象的屬性,如果直接找不到的話,會(huì)去到這個(gè)對(duì)象的原型 prototype 上去找。
這部分的內(nèi)容,可以參考我下面寫的這篇文章。

1.8、鏈?zhǔn)秸{(diào)用方法
我們使用過 JQuery,里面有好騷的操作,就是可以用點(diǎn)語法連續(xù)調(diào)用使用 ??
$('#id').html('hello').animate(...)
這里的原理是因?yàn)槊總€(gè)方法的調(diào)用都會(huì)返回 JQuery 對(duì)象,就完成了這樣的操作,在對(duì)象中的每個(gè)函數(shù)都會(huì)返回一個(gè) this,也就是對(duì)象本身 ??
var checkObjectCdy = {
? ? checkName1 : function () {
? ? ? ? console.log('1.8 方法還可以這樣用 function checkName1 already running');
? ? ? ? return this;//代表對(duì)象本身
? ? },
? ? checkName2 : function () {
? ? ? ? console.log('1.8 方法還可以這樣用? function checkName2 already running');
? ? ? ? return this;? ? ? ??
? ? },
? ? checkName3 : function () {
? ? ? ? console.log('1.8 方法還可以這樣用? function checkName3 already running');
? ? ? ? return this;? ? ? ??
? ? }
}
checkObjectCdy.checkName1().checkName2().checkName3();
我們當(dāng)然也可以用在之前學(xué)的類里來進(jìn)行使用。
var checkObjectCdyP=function(){}
checkObjectCdyP.prototype={
checkName1 : function () {
? ? ? ?console.log('1.8 方法還可以這樣用 原型 function checkName1 already running');
? ? ? ?return this;//代表對(duì)象本身
? ?},
checkName2 : function () {
? ? ? ?console.log('1.8 方法還可以這樣用 原型 function checkName2 already running');
? ? ? ?return this; ? ? ? ?
? ?},
checkName3 : function () {
? ? ? ?console.log('1.8 方法還可以這樣用 原型 function checkName3 already running');
? ? ? ?return this; ? ? ? ?
? ?}
}
//使用的時(shí)候也要聲明
var d = new checkObjectCdyP();
d.checkName1().checkName2().checkName3();
1.9、函數(shù)的祖先
要知道,函數(shù)本身是對(duì)象,它不是孤立的,它實(shí)際上是繼承自 Function 的,所以我們可以通過修改 Function 的 prototype 的方式,讓所有的函數(shù)擁有某個(gè)屬性,像下面這樣

這里我們可以用 instanceof 關(guān)鍵字驗(yàn)證確實(shí)上面的 c 是 Function 的實(shí)例,所以就能用 Function 上的原型方法。

再列舉一種添加函數(shù)內(nèi)自己的統(tǒng)一方法的案例
Function.prototype.addMethod=function(name,fn){
???? this[name]=fn;
}
var method = function(){};
method.addMethod('checkName',function(){
????console.log('1.9 函數(shù)的祖先 抽象出一個(gè)統(tǒng)一添加方法的功能方法 function checkName already running');
})
method.checkName();
不過不推薦用這種方式,因?yàn)檫@回污染 Window 里的原生對(duì)象。
1.10、鏈?zhǔn)教砑雍瘮?shù)方法
使用上面的內(nèi)容可以實(shí)現(xiàn)在給函數(shù)添加方法
//抽象出一個(gè)統(tǒng)一添加方法的功能方法
Function.prototype.addMethod=function(name,fn){
? ?this.prototype[name]=fn;
? ?return this;
}
var method = function(){};
method.addMethod('checkName',function(){
? ?console.log('1.111 鏈?zhǔn)教砑?function checkName already running');
? ?return this;
});
method.checkName().addMethod('checkName2', function() {}).checkName2();
1.11 換一種方式使用方法
我們也可以使用類的形式來調(diào)用方法。像這樣 ??
var m = new method(); // 這個(gè)?method 類是 1.10 的內(nèi)容
m.checkName();
End

通過本章節(jié)的學(xué)習(xí),我們 Javascript 是一門非常靈活的語言,知道了函數(shù)是 Javascript 里的一等公民。
這里給兩個(gè)課后練習(xí):
(1)試著定義一個(gè)可以為函數(shù)添加多個(gè)方法的 addMethod 方法;
(2)試著定義一個(gè)既可以為函數(shù)原型添加方法又可以為其自身添加方法的 addMethod 方法