如果你还不了解AMD模块加载器,可以先看看我的前一篇文章,之后又在其基础上做了一点小修改。
主要是修改了检测循环依赖的函数,不在遍历对象。
第二就是加入了模块配置方法。以及将lynxcat更名为lynx。
还有就是为模块加入了dom ready机制。
var isReady = false;
var readyList = [];
var ready = function(fn){
if(isReady){
fn();
}else{
readyList.push(fn);
}
};
var fireReady = function(){
for(var i = 0,len = readyList.length; i < len; i++){
readyList[i]();
}
readyList = [];
lynx.modules.ready.state = 2;
checkLoadReady();
};
var bindReady = function(){
if(isReady){
return;
}
isReady=true;
fireReady();
if(doc.removeEventListener){
doc.removeEventListener("DOMContentLoaded",bindReady,false);
}else if(doc.attachEvent){
doc.detachEvent("onreadystatechange", bindReady);
}
};
if( doc.readyState === "complete" ) {
bindReady();
}else if(doc.addEventListener){
doc.addEventListener("DOMContentLoaded", bindReady, false);
}else if(doc.attachEvent){
doc.attachEvent("onreadystatechange", function(){
if((/loaded|complete/).test(doc.readyState)){
bindReady();
}
});
(function(){
if(isReady){
return;
}
var node = new Image();
var timer = setInterval(function(){
try{
isReady || node.doScroll('left');
node = null;
}catch(e){
return;
}
clearInterval(timer);
bindReady();
}, 16);
}());
}
return ready;
}()
关于ready方法,其实很简单。 首先是提供一个方法,把用户的ready的方法存入一个callbacks数组里。 然后在检测到dom ready的时候执行callbacks里面的数组,如果是在dom ready之后传入ready的callback就直接执行。 具体检测dom ready就需要看浏览器的支持了,如果是支持标准w3c规范的浏览器DOMContentLoaded 方法,IE则支持onreadystatechange方法。 还可以利用的一个特性就是 doScroll要求最初的文档被完全加载。否则就会出错。 这样一来我们就可以用setInterval方法来不断的检测doScroll是否出错,如果不出错。那么代表文档已经加载完成了。 总体来说ready方法还是很简单的。
然后说说如何把ready整合到模块中。 首先我想到的是,在require中判断是否已经dom ready如果没有就把当前的require传入到ready方法。 也就是说,这个方法是让页面ready完成后在加载模块。 然后这样有一个坏处就是没有充分利用dom ready的这段时间。 后来看了一下别人的实现,又自己思索了一下。 决定把ready作为一个AMD模块放入到框架中。这样一来就就可以用模块加载的原理来处理了。 如果某个模块依赖ready,就一定要在ready之后才执行它的factory方法。 具体实现的方式是将ready作为一个基准模块写入到modules中,不过它不用被加载。而是在检测dom ready之后就将modules中的ready状态改为2。并检测一下是否所有模块都被加载完毕。这样一来就可以将dom加载的这段时间用来加载模块了。
然而这样一来就又有一个新的bug在里面,就是如果用户真的想加载一个ready方法的时候就会被误认为是dom ready。 关于这点目前还没想到什么办法,不过用户可以通过写url而不是写ready这样的名字来加载。 在下一版的加载器中我会加入部模块自动加前缀来解决这个问题。还有会加入控制并发数。以及对模块合并的简单说明。
以下是全部源码。
win = win || window;
var doc = win.document || document,
head = doc.head || doc.getElementsByTagName("head")[0],
hasOwn = Object.prototype.hasOwnProperty,
slice = Array.prototype.slice,
configure = {},
basePath = (function(nodes){
var node, url;
if(configure.baseUrl){
node = nodes[nodes.length - 1];
url = (node.hasAttribute ? node.src : node.getAttribute("src", 4)).replace(/[?#].*/, "");
}else{
url = configure.baseUrl;
}
return url.slice(0, url.lastIndexOf('/') + 1);
}(doc.getElementsByTagName('script'))),
_lynx = win.lynx;
斐波那契是1,1,2,3,5,8,13,21。。。。。 即前两项之和为第三项。程序实现如下
普通版的斐波那契:
<script type="text/javascript">
function f(num)
{
if(num<=0)
{
console.log('请输入大于0的正整数');
return ;
}
else if(num<=2 && num>0)
{
return 1;
}
else
{
return f(num-2)+f(num-1);
}
}
</script>
用三目运算符优化如下:
<script type="text/javascript">
function f(num)
{
if(num<=0)
{
console.log('请输入大于0的正整数');
return ;
}
return num<=2 && num>0 ? 1 : f(num-1)+f(num-2);
}
</script>
但是这样函数内部调用了函数的名称,假设该函数要改名,那就相当不完善了,函数内部和函数名紧紧耦合在一起了。所以可以用arguments.callee来表示该函数本身
进一步优化如下
<script type="text/javascript">
function f(num)
{
if(num<=0)
{
console.log('请输入大于0的正整数');
return ;
}
return num<=2 && num>0 ? 1 : arguments.callee(num-1)+arguments.callee(num-2);
}
</script>
至此,简单的优化就做成了。
本文链接