RequiereJs学习笔记——RequireJS的由来和垃圾回收机制理解的闭包

Why?为什么要使用RequireJs?

一个程序员,不管注释写的再好,总是会难以维护大型项目的代码。100行没问题,1000行没问题,3000行呢?5000行呢?那我光是浏览一遍都得花很多时间,极大的浪费了精力。

那怎么办呢?拆。

把一个3000行的项目拆成几百行几百行的小项目(按组件)。最傻的方式就是写一个Scrip

/*topbar区域代码*/
var $topbar=$('topbar')
//省略100行
$topbar.on('click',function(){
  console.log('topbar')
})
/*$banners区域代码*/
var $banners=$('banners')
//省略100行
$banners.on('click',function(){
  console.log('topbar')
})
/*slides区域代码*/
var $slides=('slides')
//省略100行
$slides.on('click',function(){
  console.log('topbar')
})

这样就成功的把代码分成了几个部分。我们十分开心的把代码们都隔开了,不会找错修改的位置了。

但是这样又产生了一个问题,如果哪个人手贱硬要在不同部分之间调用怎么办呢?我本来划分的好好的不同段代码又会变得杂乱无章,无法整理。

闭包问题:

于是有一个聪明的前端就想到了使用立即执行函数来进行隔离,让不同区域之间的代码变成函数内代码,也就是把它做成局部变量放在函数里。(顺带一提,ES6中,立即执行函数是一句废话,因为有let的存在。)

function xxx(){

var $topbar =$('#topbar')

$topbar.on('click'),function(){

console.log('topbar')

}

出了函数作用域的块,$topbar就是Undefined,这样就能做到消除全局变量了。

但是这样又产生了一个问题,我们又重新把函数xxx()变成了全局变量。这就很僵硬,我们并没有消灭掉全局变量,消灭掉一个又使用了一个。

于是,又有一个聪明的前端想到了使用!function.

这么做的优点是,我将无法越权修改模块。这就是立即执行函数的意义,可以将环境分割开,让各模块之间不互相干扰。

然而写着写着突然发现了一个新需求:既然已经隔开了,但是我现在想在其他模块上做一个功能需要调用到这个作用域内的函数,又怎么办呢?

函数作用域是不可能互相访问的,我们无法把作用域打通,但是我们可以做一个桥梁。

每当我们新生成一个作用域,它就相当于一个孤岛。

那么想让两个作用域交流怎么办呢?

前端们自然就想到了在函数内使用window.xxxyyy=xxxyyy,将函数内的xxxyyy映射到window上,这样全局不就都可以访问了吗?真是机智。

但是这样就又发生了一个问题,我们将username映射到了全局。如果一个不小心误修改啥的怎么办呢?还是不妥。

我们需要的是什么?是让username只能读,不能写。

于是:

window.userGetter={

nameGetter:function(){

return user.name

},

ageGetter:function(){

return user.age

}

}

我们使用了一次闭包,将这个问题解决了。别的作用域只能读取user,不能改user.

闭包在哪?

在这里说说我的理解。

闭包的用途就是如上:有一个独立的作用域,可以通过全局变量直接将内部的变量映射出去。但是我暴露出去就会有一定的风险,容易被人修改,于是我就不暴露一个变量,而是暴露一个函数。这个函数可以修改这个变量,但是只做有限的操作。外部调取只知道函数名而去访问它,不知道实际上自己操作的是哪个变量名,从而对变量的值进行保护,闭包就是一种作用域的特殊使用方式。

如果要个人做一个定义:

闭包:如果一个函数,它的内部使用了它外部的变量,那么这个函数和这个变量就组合成了一个闭包。

而闭包的作用机制,我认为用JS的垃圾回收机制来说明可能比较清晰明了。

如果我定义一个函数a,并在函数a内写了return b.

然后定义并调用 var fb=a();

那么fb,就是return b 闭包函数的引用。在我执行var fb后,调用了a(),作用域链为函数b→a()→window.

fb会依次往上寻找,直到找到变量为止(找不到则为undefined)

JS垃圾回收机制是不引用则销毁,而这里我们可以看到,作用域链中a()被fb引用,而b被a()引用,也就是说只要fb的引用存在,那么b就一直不会被销毁。

顺便一提,方法访问变量,变量存在内存中,这样做应该会让内存使用的更多才对。

——————————————————————————————————————————————

回到正题。这么使用似乎已经解决了所有问题。但是很快的,会搞事的前端又觉得不爽了。

代码根本没有关联,为啥要放在一个文件里?太sb了。分开吧。

于是:

拆成topbar.js,slide.js,banner.js,他们之间根本无法互相影响。完美!简单粗暴的模块化!!搞那么多弯弯绕绕干嘛!!!还不如一个切图仔想的办法好!!!

前端工程师:你呀,毕竟Naive.

那我有些功能写了一遍并不想再写一遍,比如topbar和banner有相近似的功能,我就是想用他们之中共通的部分,又怎么办呢?写个插件吧。头疼的事又来了,他们之间没法互相访问啊!我写了也没法在其他JS文件中调用。你这样模块化是不是略僵硬了?比如:

new Plugin({element:'#banners>slides.js.'})

写了这个插件之后,我就只能先调用plugin而不能调用别的了。顺序固定了。

而我们知道,两个局部作用域,如果没有window,是永远无法调用其他部分的。

但是分JS文件后,我这么做都是白费功夫,一旦我要依赖,还是要暴露到全局。

暴露到全局的变量太多,太烦了。于是聪明的前端先做一个window.app={},然后将所有东西都挂载在window.app里。那么我所有的调用都是调用app里的,只使用了一个全局变量。是不是跨时代的做法!!(旁人:兄弟,你这不是骗自己吗...该暴露的一个没少啊)

而这个方案,在前端维持了很长一段时间。最著名的库就是:

Jquery($符号)、YUI等。他们都是这么做的

直到requireJs的出现。

Jquery的做法是:分割变量不就是需要立即执行函数吗,那传给我,我帮你执行。上面的需求如果用JQ做,那么我把它挂载在JQ上:

var Plugin=$.Plugin

然后再从$上去取我要的函数。而这种方式,我们叫做命名空间(所有命名都是以同一个空间作为挂载)

requireJS:我觉得你这个方法不错,那我....我就把命名空间的名字固定一下吧。(骗star吗兄弟!!)

window.require(['./plugin.js'],function(Plugin ))

接着就只需要写main.js了,main.js声明了自己依赖了三个js,分别为A\B\C.

ABC里进行了分类,声明了依赖的顺序,全部加载完后就会执行main.js

QQ图片20170613134855

于是RequireJS就成为了一个很棒棒的库,一直到了今天.....

嗯,接下来就是学习如何使用了,这个坑慢慢填。

你可能感兴趣的:(RequiereJs学习笔记——RequireJS的由来和垃圾回收机制理解的闭包)