无论什么语言,都需要先将代码加载到内存中,而后才能执行,代码量上去以后,对于大量代码的加载所照成的长时间等待的问题必须有相应的解决方案,比如java的类加载器
java的类加载器
简单来说分三个部分,第一部分引导类,程序启动的基本类加载(系统级别),比如jdk,第二部分为依赖类,具体项目启动的依赖类(项目级别),第三类为自定义类,就是你自己写的代码(功能级别),不懂java可以考虑下操作系统加载的顺序,首先加载系统级别的类库(先将操作系统打开),在加载拓展功能的类库(比如系统本身所需要实现的功能),再然后根据你所操作的命令,加载具体的应用程序,就是这么简单(这种懒加载的方式,总会配合内存卸载共同使用,以缓解内存不足的窘境,在需要更高的效率,或不在意开机速度,亦或者内存多的发烧的情况下,一次性加载完也是极好的选择)
前端加载--js与css
使用link与script,程序本身对html的修改进行监听,当页面中存在script/link时,就进行一次下载请求,并自动进行加载,页面每次变更都会进行刷新,每一次请求都会进行一次js与css的加载,对服务端的压力(下载)可以通过304进行有效的改善,但是在内存中的加载---相信我,原生的html并不支持这种方式(即没考虑到,也没这个需求)
动态加载js的需求
页面每刷新一次,就像展开一个项目,如果不进行刷新,而只进行必要的内存数据/方法的增加,那已加载过的数据就可以得到有效的应用,这就是动态加载js的需求,好吧,简单来说就是启动一次的成本太大,需要复用,如果你使用过ext(项目级别的)并且不使用他的模块处理方式,启动一次3-5min不是梦
动态加载js
使用jquery的话可以通过$.getScript甚至$.get直接进行加载(记得改成同步的)
$.getScript("js/1.js")
$.get("js/1.js")
这是我第一次使用动态加载的解决方案,简单粗暴还不带套- -(当时还没考虑单页面循环和缓存,最后加入了一个全局的cache对src进行保存,以此决定是否进行加载,自动无视跨域)
上面只是我逗逼是的解决方案,至少能解决一下问题,或者证明了加载还是很简单的,其实了解脚本化通讯的话,应该知道,script自带通讯功能,创建script也可以达到相同的功能(我第一次创建script进行通讯,仅仅是因为没有jquery使用...依赖性好大的)
document.write('<script src="js/1.js" type="text/javascript" charset="utf-8"></script>')
通过document方法进行创建,原生逗逼写法,其实跟直接贴上去没什么区别,看起来能在script中动态加载,但其实,只能在页面第一次加载domcument还没有close时进行加载,加载完后,比如运行时,点击按钮触发此事件,世界会爆炸的...
var head = document.getElementsByTagName('head').item(0); var script= document.createElement("script"); script.src="js/1.js"; head.appendChild( script);
documen添加元素的正确姿势,他能解决大多数问题(文档流已经关闭了,这个时候script的加载完全是异步的)
上面的代码我经常用,用好了可以答案装B的效果,比如有些网站使用其他公司提供的通过jquery,或者被废,或者被禁,没有翻墙怎么办,控制台打出上面的话,src换成网络可访问的jquery就可以了(成功装B好几次,如果依赖太深就算了...)
当然这些都是异步加载的,转成同步除了不需要跨域的ajax外,还可以
var head = document.getElementsByTagName('head').item(0); var script= document.createElement("script"); script.src="js/1.js" ; script.onload=function(){ //....无视兼容性 } head.appendChild( script);
稍微封装一下,一个崭新的js加载器就成功完成了
当然问题还是有的,比如
(function(){ $.fn.combobox=function(){ //..... $.fn.combo.method } })()
上面的代码,我使用动态加载,必须人为的先加载jquery,在加载$.fn.combo上的内容,显然我们的动态加载器无法记住依赖,他只有加载的功能(最多加个缓存),没有解决依赖,undefined相当容易出现,加载器拥有解决依赖的能力,这就是新的需求
模块化--java
模块化本身就是面向对象的概念,java这种生物自带模块化,通过extends解决依赖问题,此后执行内部代码就不会有任何污染甚至undefined问题,当然,java的模块化通过import进行管理,最基本的要求就是import===path路劲,了解这些以后,就可以做一下尝试
假设rt为basejs,plugin引用此类js,那么,1.js中的数据可以这么写
loadJS("rt.rt.js",function(){ //.... })
loadJS是我们之前写的加载器,他依赖rt.js,其中"rt.rt.js"即是命名空间,也是地址,对吧(不要嫌麻烦,java可就是这么干的),function是我们放到onload中的回调,但是加载这个页面时,无法记录plugin.js的名字和依赖(有依赖,却没有名称)
define("plugin.1.js",function(){ loadJS("rt.rt.js",function(){ //.... }) })
define用来记录1.js的依赖关系(java也是这么干的),当你发现每个地方都这么写的时候,你就可以尝试着换一种写法,比如
define("plugin.js","rt.rt.js",function(){ }) define("plugin.js",{ extends:"rt.rt.js", init:function(){ //自动执行的function }, //其他的配置参数 })
第二种更像java对吧,好吧,这是ext,我很喜欢这种写法
记住依赖,大致就是模块化的主要工作
AMD与CMD
模块化解决方案的2种衍生概念(自动无视软件工程)
一种大概是这样,即用记取
define(function(require, exports, module) { var a = require('./a') a.do() var b = require('./b') b.do() })
有点var,有木有,另一种则一次性依赖完,就是我们上面写的栗子
define("plugin.js",["rt.rt.js","a.js","b.js"],function(){ })
你不想用数组也可以,关于那个是那个...who cares- -~
第二个更加的像java,需要提前(编译期)知道依赖的地址,但是显然,操作起来10分蛋疼,这货可没有ctrl+1快捷键直接进行引用
完整的模块化大致解决了命名污染,动态加载,加载依赖这几个问题
有人说模块化是必然趋势...拜托,那个系统级语言没这个功能,js原生功能到底还是太嫩了