Why?為什么要使用RequireJs?
一個程序員桥嗤,不管注釋寫的再好毯辅,總是會難以維護(hù)大型項目的代碼埂伦。100行沒問題,1000行沒問題思恐,3000行呢沾谜?5000行呢?那我光是瀏覽一遍都得花很多時間胀莹,極大的浪費了精力基跑。
那怎么辦呢?拆描焰。
把一個3000行的項目拆成幾百行幾百行的小項目(按組件)媳否。最傻的方式就是寫一個Scrip
/*topbar區(qū)域代碼*/
var $topbar=$('topbar')
//省略100行
$topbar.on('click',function(){
console.log('topbar')
})
/*$banners區(qū)域代碼*/
var $banners=$('banners')
//省略100行
$banners.on('click',function(){
console.log('topbar')
})
/*slides區(qū)域代碼*/
var $slides=('slides')
//省略100行
$slides.on('click',function(){
console.log('topbar')
})
這樣就成功的把代碼分成了幾個部分。我們十分開心的把代碼們都隔開了荆秦,不會找錯修改的位置了篱竭。
但是這樣又產(chǎn)生了一個問題,如果哪個人手賤硬要在不同部分之間調(diào)用怎么辦呢步绸?我本來劃分的好好的不同段代碼又會變得雜亂無章掺逼,無法整理。
閉包問題:
于是有一個聰明的前端就想到了使用立即執(zhí)行函數(shù)來進(jìn)行隔離靡努,讓不同區(qū)域之間的代碼變成函數(shù)內(nèi)代碼坪圾,也就是把它做成局部變量放在函數(shù)里晓折。(順帶一提惑朦,ES6中兽泄,立即執(zhí)行函數(shù)是一句廢話,因為有l(wèi)et的存在漾月。)
function xxx(){
var $topbar =$('#topbar')
$topbar.on('click'),function(){
console.log('topbar')
}
出了函數(shù)作用域的塊病梢,$topbar就是Undefined,這樣就能做到消除全局變量了。
但是這樣又產(chǎn)生了一個問題梁肿,我們又重新把函數(shù)xxx()變成了全局變量蜓陌。這就很僵硬,我們并沒有消滅掉全局變量吩蔑,消滅掉一個又使用了一個钮热。
于是,又有一個聰明的前端想到了使用!function.
這么做的優(yōu)點是烛芬,我將無法越權(quán)修改模塊隧期。這就是立即執(zhí)行函數(shù)的意義,可以將環(huán)境分割開赘娄,讓各模塊之間不互相干擾仆潮。
然而寫著寫著突然發(fā)現(xiàn)了一個新需求:既然已經(jīng)隔開了,但是我現(xiàn)在想在其他模塊上做一個功能需要調(diào)用到這個作用域內(nèi)的函數(shù)遣臼,又怎么辦呢性置?
函數(shù)作用域是不可能互相訪問的,我們無法把作用域打通揍堰,但是我們可以做一個橋梁鹏浅。
每當(dāng)我們新生成一個作用域,它就相當(dāng)于一個孤島屏歹。
那么想讓兩個作用域交流怎么辦呢隐砸?
前端們自然就想到了在函數(shù)內(nèi)使用window.xxxyyy=xxxyyy,將函數(shù)內(nèi)的xxxyyy映射到window上西采,這樣全局不就都可以訪問了嗎凰萨?真是機(jī)智。
但是這樣就又發(fā)生了一個問題械馆,我們將username映射到了全局胖眷。如果一個不小心誤修改啥的怎么辦呢?還是不妥。
我們需要的是什么霹崎?是讓username只能讀珊搀,不能寫。
于是:
window.userGetter={
nameGetter:function(){
return user.name
},
ageGetter:function(){
return user.age
}
}
我們使用了一次閉包尾菇,將這個問題解決了境析。別的作用域只能讀取user,不能改user.
閉包在哪囚枪?
在這里說說我的理解。
閉包的用途就是如上:有一個獨立的作用域劳淆,可以通過全局變量直接將內(nèi)部的變量映射出去链沼。但是我暴露出去就會有一定的風(fēng)險,容易被人修改沛鸵,于是我就不暴露一個變量括勺,而是暴露一個函數(shù)。這個函數(shù)可以修改這個變量曲掰,但是只做有限的操作疾捍。外部調(diào)取只知道函數(shù)名而去訪問它,不知道實際上自己操作的是哪個變量名栏妖,從而對變量的值進(jìn)行保護(hù)乱豆,閉包就是一種作用域的特殊使用方式。
如果要個人做一個定義:
閉包:如果一個函數(shù)吊趾,它的內(nèi)部使用了它外部的變量宛裕,那么這個函數(shù)和這個變量就組合成了一個閉包。
而閉包的作用機(jī)制趾徽,我認(rèn)為用JS的垃圾回收機(jī)制來說明可能比較清晰明了续滋。
如果我定義一個函數(shù)a,并在函數(shù)a內(nèi)寫了return b.
然后定義并調(diào)用 var fb=a();
那么fb,就是return b 閉包函數(shù)的引用孵奶。在我執(zhí)行var fb后疲酌,調(diào)用了a(),作用域鏈為函數(shù)b→a()→window.
fb會依次往上尋找,直到找到變量為止(找不到則為undefined)
JS垃圾回收機(jī)制是不引用則銷毀了袁,而這里我們可以看到朗恳,作用域鏈中a()被fb引用,而b被a()引用载绿,也就是說只要fb的引用存在粥诫,那么b就一直不會被銷毀。
順便一提崭庸,方法訪問變量怀浆,變量存在內(nèi)存中,這樣做應(yīng)該會讓內(nèi)存使用的更多才對怕享。
——————————————————————————————————————————————
回到正題执赡。這么使用似乎已經(jīng)解決了所有問題。但是很快的函筋,會搞事的前端又覺得不爽了沙合。
代碼根本沒有關(guān)聯(lián),為啥要放在一個文件里跌帐?太sb了首懈。分開吧绊率。
于是:
拆成topbar.js,slide.js,banner.js,他們之間根本無法互相影響。完美究履!簡單粗暴的模塊化B朔瘛!搞那么多彎彎繞繞干嘛?嫱唷M缒簟肥惭!還不如一個切圖仔想的辦法好6⒁恰!蜜葱!
前端工程師:你呀全景,畢竟Naive.
那我有些功能寫了一遍并不想再寫一遍,比如topbar和banner有相近似的功能牵囤,我就是想用他們之中共通的部分爸黄,又怎么辦呢?寫個插件吧。頭疼的事又來了揭鳞,他們之間沒法互相訪問翱还蟆!我寫了也沒法在其他JS文件中調(diào)用野崇。你這樣模塊化是不是略僵硬了称开?比如:
new Plugin({element:'#banners>slides.js.'})
寫了這個插件之后,我就只能先調(diào)用plugin而不能調(diào)用別的了乓梨。順序固定了鳖轰。
而我們知道,兩個局部作用域扶镀,如果沒有window蕴侣,是永遠(yuǎn)無法調(diào)用其他部分的。
但是分JS文件后臭觉,我這么做都是白費功夫昆雀,一旦我要依賴,還是要暴露到全局蝠筑。
暴露到全局的變量太多狞膘,太煩了。于是聰明的前端先做一個window.app={}菱肖,然后將所有東西都掛載在window.app里客冈。那么我所有的調(diào)用都是調(diào)用app里的,只使用了一個全局變量稳强。是不是跨時代的做法3≈佟:驮谩(旁人:兄弟,你這不是騙自己嗎...該暴露的一個沒少扒啤)
而這個方案鸽素,在前端維持了很長一段時間。最著名的庫就是:
Jquery($符號)亦鳞、YUI等馍忽。他們都是這么做的
直到requireJs的出現(xiàn)。
Jquery的做法是:分割變量不就是需要立即執(zhí)行函數(shù)嗎燕差,那傳給我遭笋,我?guī)湍銏?zhí)行。上面的需求如果用JQ做徒探,那么我把它掛載在JQ上:
var Plugin=$.Plugin
然后再從$上去取我要的函數(shù)瓦呼。而這種方式,我們叫做命名空間(所有命名都是以同一個空間作為掛載)
requireJS:我覺得你這個方法不錯测暗,那我....我就把命名空間的名字固定一下吧央串。(騙star嗎兄弟!M胱摹)
window.require(['./plugin.js'],function(Plugin ))
接著就只需要寫main.js了质和,main.js聲明了自己依賴了三個js,分別為A\B\C.
ABC里進(jìn)行了分類,聲明了依賴的順序稚字,全部加載完后就會執(zhí)行main.js

于是RequireJS就成為了一個很棒棒的庫饲宿,一直到了今天.....
嗯,接下來就是學(xué)習(xí)如何使用了尉共,這個坑慢慢填褒傅。