javascript框架设计之种子模块

javascript框架设计之种子模块

本文给大家介绍的是司徒正美的javascript框架设计的第二章种子模块的相关内容,算是一个小小的读后感,小伙伴们可以参考下。

种子模块也叫核心模块,是框架中最先执行的部分。即便像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()的形式调用。

?
1
2
3
4
5
6
7
8
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放回去。

?
1
2
3
4
5
6
7
8
9
10
//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)没有诞生之前,是可以随意添加、更改、删除其成员的,因此,扩展一个对象非常便捷。一个简单的扩展方法实现是这样的。

?
1
2
3
4
5
function extend (destination,source){
   for ( var property in source)
     destination[property] = source[property];
   return destination;
}

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

?
1
2
3
4
5
Object.keys = Object.keys || function (obj){
   var a = [];
   for (a[a.length] in obj);
   return a;
}

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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执行异常。我们看一下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$.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等细分的对象类型。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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下有这样的闹剧。

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

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

?
1
2
3
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%的陷阱被跳过了。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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 {
     Array.prototype.toString.call(o);
     return true ;
   } catch (e) {
   }
   return false ;
}
 
function isArray(o){ //kangax
   if (o && typeof o == 'object' && typeof o.length == 'number' && isFinite(o.length))
   {
     var _origLength = o.length;
     o[o.length] = '__test__' ;
     var _newLength = o.length;
     o.length = _origLength;
     return _newLength == _origLength + 1;
   }
   return false
}

至于null 、 undefined 、NaN直接这样写

?
1
2
3
4
5
6
7
8
9
10
11
function isNaN(obj) {
   return obj !== obj
}
 
function isNull(obj) {
   return obj === null ;
}
 
function isUndefined(obj){
   return obj === void 0;
}

最后要判定的是对象是window,由于ECMA是不规范的Host对象,window对象属于host.所以也就没有被约定。就算Object.prototype.toString也对它没办法

?
1
2
3
4
5
6
7
8
[object Object] IE6
[object Object] IE7
[object Object] IE8
[object Window] IE9
[object Window] ff3.6
[object Window] opera10
[object DOMWindow] safari 4.04
[object global] chrome5.0

不过根据window.window和window.setInterval去判定更加不靠谱,用一个技巧就可以完美识别ie6 ie7 ie8的window对象,其它还用toString,这个神奇的hack就是,window与document互相比较,如果顺序不一样,结果也是不一样的!

剩下的就是一些经典方法:

在prototype.js中,拥有isElement,isArray,isHash,isFunctoion,isString,isNumber,isDate,isUndefined。

mootools有一个typeOf判定基本类型,instanceOf判定自定义“类”

RightJS 有isFunction , isHash , isString , isNumber , isArray ,isElement, isNode.

Ext有比较全面的判断,isEmpty,isArray,isDate,isObject,isSimpleObject,isPrimitive,isFunction,isNumber,isMumeric,isString,isBoolean,isElement,isTextNode,isDefined,isIterable,应有尽有。最后,还有typeOf判断基本类型。

Underscore.js有isElement,isEmpty,isArray,isArgument,isObject,isFunction,isString,isNumber,isFinite,isNaN,isBoolean,isDate,isRegExp,isNull,isUndefined.

jQuery就不与其它框架一样了,在jQuery1.4中只有isFunction,isArray,isPlainObject,isEmptyObject。isFunction,isArray用户肯定用的较多,isPlainObject是用来判断是否是纯净的js对象。既不是DOM,BOM对象,也不是自定义的“类”的实例对象,制造它的目的最初是为了深拷贝,避开像window那样自己引用自己的对象。isEmptyObject是用于数据缓存的系统,当此对象为空时,就可以删除它。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//jQuery2.0纯净数组的判定思路
jQuery.isPlainObject = function (obj){
   //首先排除基础类型不为Object的类型,然后是DOM节点与window对象
   if (jQuery.type(obj) !== "object" || object.nodeType || jQuery.isWindow(obj)){
     return false ;
   }
 
   //然后回溯它的最近的原型对象是否有isPrototypeOf.
   //旧版本的IE一些原生的对象没有暴露constructor、prototype。因此在这里过滤掉
   try {
     if (obj.constructor && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf" )){
       return false ;
     }
   } case (e) {
     return false ;
   }
   return true ;
}

avalon.mobile中有一个更精简的版本,只支持最新的浏览器,可以大胆的使用ecma262v5新API

?
1
2
3
avalon.isPlainObject = function (obj){
   return obj && typeof obj === "object" && Object.getPrototypeOf(obj) === Object.prototype
}

isArrayLike也是一个常用的方法,但是判定一个类数组太难了,唯一的识别方法就是判断有一个大于0或等于0的length属性,此外,还有一些共识,如window与函数和元素节点,如(form元素),不算类数组,虽然它们都满足前面的条件。因此,至今jQuery都没有把它暴露出来。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//jQuery2.0
function isArrayLike(obj){
   var length = obj.length , type = jQuery.type(obj);
   if (jQuery.isWindow(obj)){
     return false ;
   }
   if (obj.nodeType === 1 && length){
     return true
   }
   return type === "array" || type !== "function" && (length === 0 || typeof length === "number" && length > 0 && (length -1) in obj);
}
 
// avalonjs
function isArrayLike (obj) {
   if (obj && typeof obj === "object" ){
     var n = obj.length
     if (+n === n && !( n % 1) && n >= 0){ //检测length是否为非负整数
       try {
         if ({}.prototypeIsEnumerable.call(obj, 'length' ) === false ){
         return Array.isArray(obj) || /^\s? function /.test(obj.item || obj.callee)
        }
       return true ;
      } catch (e) { //IE的NodeList直接报错
       return true ;
      }
     }
   }
return false
}

在Prototype.js1.3版本中的研究成果(Object.prototype.toString.call)就应用于jQuery,在jQuery1.2中,判断一个变量是否为函数非常复杂。

复制代码 代码如下:

    isFunction : function(fn){
        return !!fn && typeof fn != "string" && !fn.nodeName && fn.constructor != Array && /^[\s[]?function/.test(fn + "");
    }

jQuery1.43中引入isWindow来处理makeArray中对window的判定,引入isNaN用于确保样式赋值的安全。同时引入type代替typeof关键字,用于获取基本数据的基本类型。

?
1
2
3
4
5
6
7
8
class2type = {};
jQuery.each( "Boolean Number String Function Array Date RegExpObject" .split( " " ), function ( i , name ){
   class2type[ "[object " + name + "]" ] = name.toLowerCase();
});
 
jQuery.type = function (obj){
   return obj == null ? String(obj) : class2type[toString.call(obj)] || "object" ;
}

jQuery1.7中添加isNumeric代替isNaN。这是个不同于其它框架的isNumber,它可以是个字符串,只要外观上像数字就可以了。但jQuery1.7还做了一个违背之前提到稳定性的事情。冒然去掉jQuery.isNaN,因此,基于旧版jQuery的有大批插件失效。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//jQuery.1.43-1.64
jQuery.isNaN = function ( obj ) {
   return obj == null || !rdigit.test( obj ) || isNaN( obj );
}
 
//jQuery1.7就是isNaN的去反版
jQuery.isNumeric = function ( obj ) {
   return obj != null && rdigit.test( obj ) && !isNaN( obj );
}
 
//jQuery.1.71 - 1.72
jQuery.isNumeric = function ( obj ) {
   return !isNaN( parseFloat(obj) ) && isFinite( obj );
}
 
//jQuery2.1
jQuery.isMumeric = function ( obj ) {
   return obj - parseFloat(obj) >= 0;
}

massFarmeWork的思路与jQuery一致,尽量减少isXXX系列的代码,把is Window,isNaN,nodeName等方法做了整合。代码较长,既可以获取类型,也可以传入第二参数进行类型比较。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
var class2type = {
   "[objectHTMLDocument]" : "Document" ,
   "[objectHTMLCollection]" : "NodeList" ,
   "[objectStaticNodeList]" : "NodeList" ,
   "[objectIXMLDOMNodeList]" : "NodeList" ,
   "[objectDOMWindow]" : "window" ,
   "[object global]" : "window" ,
   "Null" : "Null" ,
   "undefined" : "undefined"
},
toString = class2type.toString;
"Boolean,Number,String,Function,Array,Date,RegExp,Window,Document,Arguments,NodeList" .replace($.rword, function ( name ) {
   class2type[ "[object" + name + "]" ] = name;
});
 
//class2type这个映射几乎把常用的判定对象一网打尽
mass.type = function ( obj , str ){
   var result = class2type[ (obj == null || obj !== obj) ? obj : toString.call(obj) ] || obj.nodeName || "#" ;
   if (result.charAt(0) === "#" ) { //兼容旧版浏览器的个别情况,如window.opera
     //利用IE678 window === document为true,document === window为false
     if ( obj == obj.document && obj.document != obj ) {
       result = "window" ; //返回构造器名称
     } else if ( obj.nodeType === 9 ) {
       result = "Document" ;
     } else if ( obj.callee) {
       result = "Arguments" ;
     } else if ( isFinite(obj.length) && obj.item ) {
       result = "NodeList" //处理节点集合
     } else {
       result = toString.call(obj).slice(8,-1);
     }
   }
   if (str){
     result str === result;
   }
   return result;
}

然后type方法就十分轻松了,用toSring.call(obj)得出值的左键,直接从映射中取得。IE678,我们才费一些周折处理window,document,argument,nodeList等对象。

百度的七巧板基于实用主义,判定也十分严谨。与EXT一样,能想到的写上,并且判定十分严谨。

目前版本2.0.2.5 http://tangram.baidu.com/api#baidu.type()

?
1
2
3
4
5
6
baidu.isDate = function ( unknow ) {
return baidu.type(unknow) == "date" && unknow.toString() != 'Invalid Date' && !isNaN(unknow);
};
baidu.isNumber = function ( unknow ) {
return baidu.type(unknow) == "number" && isFinite( unknow );
};

 5.主流框架的引入机制-domReady

domReady其实是一种名为"DOMContentLoaded"事件的别称,不过由于框架的需要,它与真正的DOMContentLoaded有一点区别,在很多新手和旧的书中,很多人将其写在window.onload回调中,防止dom树还没有建完就开始对节点操作。而对于框架来说,越早越介入dom就越好,如要进行特征侦测之类的。domready还可以满足用户提前绑定事件需求,因为有时页面图片过多等,window.onload事件迟迟不能触发,这时用户操作都没有效果,因此,主流的框架都引入了domReady机制,并且费了很大周折才兼容所有浏览器。具体的策略如下:

对于支持DOMContentLoaded事件使用DOMcontentLoaded事件
旧版本IE使用Diego perini发现的著名Hack

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//by Diego Perini 2007.10.5
  function IEContentLoaded (w, fn) {
  var d = w.document, done = false ,
  // 只执行一次用户的回调函数init()
  init = function () {
   if (!done) {
     done = true ;
     fn();
   }
  };
( function () {
   try {
     // DOM树未创建完之前调用doScroll会抛出错误
     d.documentElement.doScroll( 'left' );
   } catch (e) {
     //延迟再试一次~
     setTimeout(arguments.callee, 50);
     return ;
   }
   // 没有错误就表示DOM树创建完毕,然后立马执行用户回调
   init();
        })();
//监听document的加载状态
d.onreadystatechange = function () {
   // 如果用户是在domReady之后绑定的函数,就立马执行
   if (d.readyState == 'complete' ) {
     d.onreadystatechange = null ;
     init();
   }
      };
}

此外,IE还可以通过script defer hack进行判定

?
1
2
3
4
5
6
document.write( "<script id=__ie_onload defer src=//0 mce_src=http://0></scr" + "ipt>" );
   script = document.getElementById( "__ie_onload" );
   script.onreadystatechange = function () { //IE即使是死链也能触发事件
         if ( this .readyState == "complete" )
   init(); // 指定了defer的script在dom树建完才触发
};

不过还有个问题,如果我们的种子模块是动态加载的,在它插入dom树时,DOM树是否已经建完呢?这该怎么触发ready回调?jQuery的方案是,连onload也监听了 ,但如果连onload也没赶上,就判定document.readyState等于complete。(完美)可惜ff3.6之前没有这属性,看mass的方案

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
var readyList = [];
mess.ready = function ( fn ) {
   if ( readyList ) {
     fn.push( fn );
   } else {
     fn();
   }
}
var readyFn ,ready = W3C ? "DOMContentLoaded" : "readyStatechange" ;
function fireReady() {
   for ( var i = 0 , fn; fn = readyList[i++]) {
     fn();
   }
   readyList = null ;
   fireReady = $.noop; //惰性函数,防止IE9调用_checkDeps
}
function doScrollCheck() {
   try { //IE下通过doScrollCheck检测DOM树是否建设完
     html.doScroll( "left" );
     fireReady();
   } catch (e){
     setTimeout(doScrollCheck);
   }
}
 
//FF3.6前,没有readyState属性
if (!document.readyState){
   var readyState = document.readyState = document.body ? "complete" : "loading" ;
   if (document.readyState === "complete" ) {
     fireReady(); //如果在domReay之外加载
   } else {
     $.bind(document,ready,readyFn = function (){
       if (W3C || document.readyState === "complete" ) {
         fireReady();
         if (readyState){ //IE下不能该项document.readyState
           document.readyState = "complete" ;
         }
       }
     });
     if (html.doScroll){
       try { //如果跨域就报错,证明有两个窗口
         if (self.eval === parent.eval) {
           doScrollCheck();
         }
       } catch (e) {
         doScrollCheck();
       }
     }
   }
 
}

6.无冲突处理

无冲突处理也叫多库共存,$是这个重要的函数名,以至于大家都爱拿它来做自己的命名空间,当jQuery开始发展时,Prototype是主流,jQuery发明了noConflict函数

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var window = this ,
undefined,
_jQuery = window.jQuery,
_$ = window.$,
//将window存入闭包中的同名变量,方便内部函数调用windows不用太麻烦查找它。
//_jQuery与_$用于以后重写
jQuery = window.jQuery = window.$ = function (selector, context){
   //用于返回一个jQuery对象
   return new jQuery.fn.init(selector,context);
}
 
jQuery.extend({
   noConflict : function (deep) {
     //引入jQuery类库后,闭包外边的window.$与window.jQuery都储存着一个函数
     //它是用来生成jQuery对象或在domReady后执行里面的函数
     //回顾最上面的代码,在还没有把function赋值给它们时,_jQuery和_$已经被赋值了,因此,它们两的值必然是undefined
     //因此,这种放弃控制权的技术很简单,就是用undefined把window.$里边jQuery函数清除掉。
     //这时,prototype或mootools的$就被拿走了
     window.$ = _$; //相当于window.$ = undefined,如果你有一个叫jQuery的库,也能大方的过渡出去。
     //这时,需要给noConflict添加一个布尔值,true
   
   if (deep)
     //但我们必须使用一个东西接纳jQuery与jQuey的入口函数
     //闭包里边的东西除非被window等宿主引用,否则是不可见的
     //因此,我们把闭包里的jQuery return出去,外面用一个变量接纳就可以
     window.jQuery = _jQuery; //相当window.jQuery = undefined
   return jQuery;
   }
})

使用时,先引入别人的库,然后引入jQuery,使用调用$.noConflict()进行改名,这样就不影响别人的$运行了。

mass的操作方式是在script标签上定义一个nick属性,那么释放出来的命名空间就是你的那个属性值。里面实现了类似jQuery的机制。

?
1
2
3
4
<script nike= "aaa" src= "mass.js" ></script>
<script>
   aaa.log( "xxxxxx" )
</script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值