首页 > 网站 > WEB开发 > 正文

第二章 : 种子模块

2024-04-27 14:08:55
字体:
来源:转载
供稿:网友

第二章 : 种子模块

种子模块也叫核心模块,是框架中最先执行的部分。即便像jQuery那样的单文件函数库,它的内部也分很多模块,必然有一些模块执行时在最前面立即执行,有一些模块只有用到才执行。有的模块可有可无,存在感比较弱,只有在特定的浏览器下才运行。

种子模块就是其中的先锋,它里边的方法不一定要求个个功能齐全,设计优良,但一定要极具扩展性,常用,稳定。

扩展性是指通过他们能给将其它模块包含进来;常用是指绝大多数的模块都能用到它们,防止做重复工作。稳定是指在版本迭代时不轻易被新方法替代。

参照许多框架和库的实现,我们认为种子模块包含如下功能对象扩展数组化类型判定简单的绑定与卸载无冲突处理模块的加载domReady.本章学习的内容以mass Framework种子模块为范本。

https://github.com/RubyLouvre/mass-Framework

1.命名空间

种子模块作为一个框架最开始的部分,负责辅建全局的基础设施外。jQuery就有一个很好的开头,使用IIFE(立即调用函数表达式).

LIFE是现代javascript框架里最主要的基础设施,它就像细胞一样包裹自身,防止变量污染。就像一个立足点,这个就是命名空间,如PRototype.js,mootools,它们让你感受不到框架的存在,它的意义深刻到Javascript、DOM、BOM等整个执行环境的每个角落,对原生的对象原型就行扩展。由于道格拉斯(JSON作者)的极力反对,新的框架都在命名空间上构建了。

我们看怎么在javascript上模拟命名空间。javascript一切基于对象,但只有符合类型的对象才符合要求,比如function 、RegExp、Object,不过最常用的是object和function。我们往一个对象上添加一个属性,而这个属性又是一个对象,这个对象我们又可以为它添加一个对象,通过这种方法,我们就可以有条不紊的构建我们的框架。用户想调用某个方法,就以xxx.yyy.zzz()的形式调用。

    if( typeof(Ten) === "undefined" ){        Ten = {};        Ten.Function = {  /*略*/ }        Ten.Array = {  /*略*/ }        Ten.Class = {  /*略*/ }        Ten.JSONP = new Ten.Class(/*略*/ )        Ten.XHR = new Ten.Class(/*略*/ )    }

纵观各大类库的实现,一开始基本都是定义一个全局变量作为命名空间,然后对它进行扩展,如Base2的Base,Ext的Ext,jQuery的jQuery,YUI的YUI,dojo的dojo,MochiKit的mochKit。从全局变量的污染程度来看,分为两类:

prototype.js和mootools与Base2归为一类,Prototype的哲学是对javascript的原生对象进行扩展。早些年,prototype差点称为事实的标准。因此没有考虑到与其它库共存的问题。基本Prototype,也发展出诸如script.aculo.us,rico,Plotr,protoChart,Script2等非常优秀的类库以一大类收费插件。而且,有些渊源的插件几乎都与Prototype有关,比如lightBox。mootools是prototype.js的升级版,更加OO,全面复制其API。Base2则是想修复IE的bug,让IE拥有标准浏览器的API,因此也把所有原生的对象污染一遍。

第二类是jQuery,YUI,EXT这些框架YUI和Ext就是对象嵌套对象的方式构建的。jQuery则另辟蹊径,它是以选择器为导向的,因此它的命名空间是一个函数,方便用户将CSS表达器的字符串传进来。然后通过选择器进行查找,最后返回一个jQuery对象实例。

jQuery最开始也像Prototype使用$作为它的命名空间,因此,它实现了很多库的共存机制,在$和jQuery中任意切换,jQuery的多库共存原理很简单,因此后来也成为许多小库的标配。首先,把命名空间保存到一个临时变量中(注意,这时候这个对象并不是自己框架的东西,可能是prototype.js或者其他的),然后再使用个noConflict放回去。

    //jQuery1.2    var _jQuery = window.jQury , _$ = window.$; //把可能存在同名变量先保存起来    jQury.extend({        noConflict : function(deep) {            window.$ = _$; //这时再放回去            if (deep)  //补充 if ( deep && window.jQuery === jQuery )                window.jQury = _jQuery;            return jQury;        }    })

参考:http://zhidao.baidu.com/question/1239712776390687219.html

但jQuery的noConflict只是对单文件的类库框架有用,像Ext就不能复制了。因此把命名空间改名后,将Ext置为null,然后通过动态加载的方法引入新的javascript文件中,该文件会以Ext调用,会导致报错。

2.对象扩展

我们需要一种机制,将新功能添加到我们的命名空间上来。这方法在javascript通常称作extend或mixin。javascript对象在属性描述符(Property Descriptor)没有诞生之前,是可以随意添加、更改、删除其成员的,因此,扩展一个对象非常便捷。一个简单的扩展方法实现是这样的。

    function extend (destination,source){        for (var property in source)            destination[property] = source[property];        return destination;    }

不过,旧版本IE在这里有个问题,它认为像Object的原型方法就是不应该被遍历出来,因此for in循环是无法遍历valueOf、toString的属性名。这导致,模拟Object.keys方法是现实时也遇到了这个问题。

    Object.keys = Object.keys || function(obj){        var a = [];        for(a[a.length] in obj);        return a;    }

在不同的框架,这个方法还有不同的实现,如Ext分别为apply与applyIf两个方法,前者会覆盖目标对象的同名属性,而后者不会。dojo允许多个对象合并在一起。jQuery还支持深拷贝。下面是mass Farmework的mix方法。支持多对象合并与选择是否覆写。

    function mix(target,source){ //如果最后参数是布尔,判定是否覆盖同名属性        var args = [].slice.call(arguments), i = 1, key,            ride = typeof args[args.length - 1] == "boolean" ? args.pop() : true;        if (args.length === 1){ //处理$.mix(hash)的情形            target = !this.window ? this : {};            i = 0;        }            while ((source = args[i++])) {            for (key in source){ //允许对象糅杂,用户保证都是对象                if (ride || !(key in target)) {                    target[key] = source[key];                }            }        }        return target;    }

3.数组化

浏览器下存在很多类数组对象,如function内的arguments,通过document.forms、form.elements,document.links、select.options、document.getElementsByName,document.getElementsByTagName、childNodes、children等方式获取的节点的结合(HTMLCollection 、NodeList)或按照某些特殊的写法自定义对象。

类数组对象是一个很好的存储结构。不过功能太弱了,为了能使用纯数组的那些便捷的方法,我们会在处理它们前都会做一下转换。

通常来说,使用[].slice.call就能转换了 ,不过功能不够用,但在旧版本的HTMLCollection、NodeList不是Object的子类,采用如上的方法会导致IE执行异常。我们看一下

jQuery:makeArray

    var makeArray = function(array) {        var ret = [] ;        if(array != null){            var i = array.length;            if(i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval)                ret[0] = array;            else                while (i)                    ret(--i) = array[i];        }        return ret;    }

mass的实现,一开始就进行区分,直接[].slice.call,IE的话自己动手实现一个slice方法

    $.slice = window.dispatchEvent ? function(nodes,start,end){        return [].slice.call(nodes,start,end);    } : function (nodes,start,end){        var ret = [],              n = nodes.length;        if (end === void 0 || typeof end === "number" && isFinite(end)){            start  = parseInt (start,0) || 0;            end = end == void 0 ? n:parseInt (end,10);            if(start < 0){                start += n;            }            if (end > n) {                end =n            };            if (end < 0) {                end += n            };            for (var i = start; i < end; ++i){                ret[i-start] = nodes[i];            }        }        return ret;    }

4.类型的判定

javascript存在两套类型系统,一套是基本的数据类型,另一套是对象类型系统。基本数据类型包括6中 。分别是undefined、string、null、boolean、function、object。基本数据类型是通过typeof来检测的。对象类型系统是以基础类型系统为基础的,通过instanceof来检测的。然而,javascript自带的这两套识别机制非常不靠谱,于是就催生了isXXX系列。就拿typeof来说,它只能粗略识别出string、number、boolearn、function、undefined、object这6种数据类型,无法识别null,RegExpArgument等细分的对象类型。

    typeof null // => "object"    typeof document.childNodes //=> safari: "function"    typeof document.creatElement('embed') //=> ff3-10 "function"    typeof document.creatElement('object') //=> ff3-10 "function"    typeof document.creatElement('object') //=> ff3-10 "function"    typeof //d/i //在实现了ecma262v4的浏览器返回"function"    typeof window.alert //ie678 "object"    var iframe = document.creatElement("iframe")    document.body.appendChild(iframe)    xArray = window.frames[window.frames.length - 1].Array;    var arr = new xArray(1,2,3) //=>[1,2,3]    arr instanceof Array ;// false    isNaN("aaa") //=> true

另外,以前人们总是以document.all来判断是否为ie,这其实是很危险的,因为,用document.all来取得页面中所有的元素是不错的注意,这个方法FF,Chrome打算使用很久了,不过人们都这样判断,就是在chrome下有这样的闹剧。

    typeof document.all //undefined    document.all //HTMLAllCollection [728] (728为元素总数)

在判定undefined、null、string、number、boolean、function这六个还算简单,前面两个可以分别与void(0)、null比较,后面4个的typeof也可以满足90%的情形。这样说是因为string、number、boolean可以包装成伪对象。

    typeof new Boolean(1); //=>"object"    typeof new Number(1); //=>"object"    typeof new String("aa"); //=> "object"

这些还不是最难的,难点在于RegExp与Array.判定RegExp的情况很少。Array则不一样。有关isArray的实现不下二十种。都是因为鸭式辨型被打破了。直到prototype.js把Object.prototype.toString发掘出来。此方法是直接输出内部的[[Class]],绝对精准。有了它,95%的陷阱被跳过了。

    function isArray(arr){        return arr instanceof Array;    }    function isArray(arr){        return !!arr && arr.constructor === Array;    }    function isArray(arr) { //prototype.js 1.6        return arr != null && typeof arr === "object" && 'splice' in arr && 'join' in arr;    }    function isArray(arr){// Douglas Crockford(JSON作者,反对原型污染)        return typeof arr.sort == "function"    }    function isArray(array){ //kriszyp        var result = false;        try{            new array.constructor (Math.pow(2,32))        } catch (e){            result = /Array/.test(e.message)        }        return result;    };    function isArray(o){//kangax        try{            Arr
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表