jq源码学习4_jQuery.extend() : 扩展一些工具方法

基于jquery-2.0.3的源码分析

//4.jQuery.extend() : 扩展一些工具方法
/*jQuery.extend({	
	expando  :  生成唯一JQ字符串(内部)
	noConflict()  :  防止冲突
	isReady  :  DOM是否加载完(内部)
	readyWait  :  等待多少文件的计数器(内部)
	holdReady()  :  推迟DOM触发
	ready()  :  准备DOM触发
	isFunction()  :  是否为函数
	isArray()  :  是否为数组
	isWindow()  :  是否为window
	isNumeric()  :  是否为数字 
	type()  :  判断数据类型
	isPlainObject()  :  是否为对象自变量
	isEmptyObject()  :  是否为空的对象
	error()  :  抛出异常
	parseHTML()  :  解析节点
	parseJSON()  :  解析JSON
	parseXML()  :  解析XML
	noop()  :  空函数
	globalEval()  :  全局解析JS
	camelCase()  :  转驼峰(内部)
	nodeName()  :  是否为指定节点名(内部)
	each()  :  遍历集合
	trim()  :  去前后空格
	makeArray()  :  类数组转真数组
	inArray()  :  数组版indexOf
	merge()  :  合并数组
	grep()  :  过滤新数组
	map()  :  映射新数组
	guid  :  唯一标识符(内部)
	proxy()  :  改this指向
	access()  :  多功能值操作(内部)
	now()  :  当前时间
	swap()  :  CSS交换(内部)
	
});
*/
jQuery.extend({
  //生成唯一的jq字符串(内部),作用:用于作为唯一映射关系
  expando:"jQuery" + (core_version + Math.random()).replace(/\D/g, ""),
  //防止冲突,当外界使用 $ 或者 jQuery 时候,jq可以自动方法这两个对外接口,通过调用 noConflict()函数,自定义对外使用接口
  noConflict:function(deep){  
    if(window.$ === jQuery){ //不写参数的时候是对$的放弃写参数的时候是对jQuery的放弃
       window.$ = _$;  //不写参数的时候放弃$
    }
    if(deep && window.jQuery === jQuery){ 
     
         window.jQuery = _jQuery;
    }
    return jQuery;
     /*noConflict()的使用<script> var $=123; var jQuery=456; </script>
      <script src="jquery-2.0.3.js"></script>
      <script>
      var emma = $.noConflict();  // 对 $ 的放弃
      emma(function() { alert($); })  // 弹出123
      var watson = $.noConflict(true); // 对 jQuery 的放弃
      watson(function() { alert(jQuery); }) // 弹出456
    </script>*/
  },
isReady: false, //dom是否加载完成(内部使用)
readyWait:1,//等待多少文件的计数器(内部)
holdReady:function(hold){ //推迟DOM触发
   if(hold){ 
     jQuery.readyWait ++;
   }else{
     jQuery.ready(true);
   }
},
ready:function(wait){ //准备DOM触发,参数和holdReady有关
  //如果readyWait不是0,或者isReady是false,那么什么也不做,继续等待!
  //readyWait为0时就不用hold了,执行后面的操作
   if(await === true ? --jQuery.readyWait : jQuery.isReady){
     return;   
   }
   jQuery.isReady = true;//默认是false
   if(wait !==true && --jQuery.readyWait >0){
     return;
   }
    //如果已经readyWait是0,那么就会执行,也就是触发事件,最终调用Deferred对象的done方法
		 //上下文是document,第二个参数是jQuery对象!这时候外层的done已经执行了,也就说这个ready中的函数可以执行了!
		 //第一个参数是指向context所以ready中第一个参数是document对象,第二个jQuery就是参数也就是给ready函数传递的参数!
   readyList.resolveWith( document, [ jQuery ] );
   if ( jQuery.fn.trigger ) {
    jQuery( document ).trigger("ready").off("ready");
  }
},
isFunction:function(obj){//是否为函数
   return jQuery.type(obj) === "function";//$.type() 函数用于确定JavaScript内置对象的类型,并返回小写形式的类型名称。
},
isArray:Array.isArray,//是否为数组
isWindow:function(obj){  //是否为window
 return obj != null && obj === obj.window;
},
isNumeric:function(obj){ //是否为数字
  //isNaN() 函数用于检查其参数是否是非数字值
  //parseFloat() 函数可解析一个字符串,并返回一个浮点数。
  //该函数指定字符串中的首个字符是否是数字。如果是,则对字符串进行解析,直到到达数字的末端为止,然后以数字返回该数字,而不是作为字符串。
  //isFinite() 函数用于检查其参数是否是无穷大。
  return !isNaN( parseFloat(obj) ) && isFinite(obj);
},
type:function(obj){  // 判断数据类型
  if(obj == null){
    return String(obj);
  }
  return typeof obj === "object" || typeof obj === "function" ?
  class2type[core_toString.call(obj)] || "object" : typeof obj;
},
isPlainObject:function(obj){ //是否为对象自变量
  if(jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow(obj)){
      return false;
  }
  try{
    if(obj.constructor && !core_hasOwn.call(obj.constructor.prototype,"isPrototypeOf")){
          return false;
    }
  }catch(e){
     return false;
  }
},
isEmptyObject:function(obj){ //判断是否为空的对象
  //for-in循环会同时枚举非继承属性和从原型对象继承的属性,如果有,则立即返回false,否则默认返回true。
   var name;
   for(name in obj){ //for...in 语句用于对数组或者对象的属性进行循环操作。
      return false;
   }
   return true;
},
error:function(msg){ //抛出异常
  throw new Error( msg ); 
},
parseHTML:function(data,context,keepScripts){  //解析节点
  if(!data || typeof data !== "string"){
     return null;
  }
  //只有两个参数的时候,第二个就是是否保存script标签,这时候context就没有传进来
  if(typeof context === "boolean"){
     keepScripts = context;
     context = false;
  }
  //如果只有两个参数那么context就是document对象!
  context = context || document;
  //如果不是单个标签那么parsed就是null,所谓的单个标签就是<div/>或者<div></div>但是<div>hello</div>不满足!
  var parsed = rsingleTag.exec( data ),
  //如果keepScripts是false,那么scripts就是
  scripts = !keepScripts && [];
  if ( parsed ) {
    //如果是单个标签就调用相应的createElement方法,默认上下文是document!
    return [ context.createElement( parsed[1] ) ];
  }
  //如果不是单个标签就调用buildFragment方法,把html字符串传入,同时上下文也传入,第三个参数就是scripts!
	//如果paseHTML的第三个参数是false,那么这里的scripts就是一个数组,传递到buildFragment中会把所有的script标签放在里面
  parsed = jQuery.buildFragment( [ data ], context, scripts );
  if(scripts){
     jQuery(scripts).remove();
  }
  //buildFragment返回的是文档碎片,所以要变成数组,调用merge方法
  return jQuery.merge( [], parsed.childNodes );
},
parseJSON: JSON.parse,//解析JSON
parseXML:function(){ //解析XML
  var xml, tmp;
  if ( !data || typeof data !== "string" ) {
    return null;
  }

  // Support: IE9
  try {
    tmp = new DOMParser();
    xml = tmp.parseFromString( data , "text/xml" );
  } catch ( e ) {
    xml = undefined;
  }

  if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
    jQuery.error( "Invalid XML: " + data );
  }
  return xml;
},
noop:function(){}, //空函数
globalEval:function(code){  //全局解析JS
  // code:待执行的JavaScript语句或表达式
  var script,
   indirect = eval;
   code = jQuery.trim(code);//去除字符串的前后空格
   if(code){
     //indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置
     //如果在数组中没找到字符串则返回 -1
        if(code.indexOf("use strict") === 1 ){ //判断是否是在严格模式下,严格模式不支持eval
          //createElement() 方法通过指定名称创建一个元素
          script = document.createElement("script");
          script.text = code; 
          document.head.appendChild( script ).parentNode.removeChild( script ); 
        }else{
          indirect(code);
        }
   }
},
camelCase:function(string){ //转驼峰(内部)
  return string.replace(rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
},
nodeName:function(elem,name){ //是否为指定节点(内部使用)
  //用法$.nodeName(document.body,'html')
  return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
},
each: function( obj, callback, args ) {//遍历集合
  var value,
    i = 0,
    length = obj.length,
    isArray = isArraylike( obj );

  if ( args ) {
    if ( isArray ) {
      for ( ; i < length; i++ ) {
        value = callback.apply( obj[ i ], args );

        if ( value === false ) {
          break;
        }
      }
    } else {
      for ( i in obj ) {
        value = callback.apply( obj[ i ], args );

        if ( value === false ) {
          break;
        }
      }
    }

  // A special, fast, case for the most common use of each
  } else {
    if ( isArray ) {
      for ( ; i < length; i++ ) {
        value = callback.call( obj[ i ], i, obj[ i ] );

        if ( value === false ) {
          break;
        }
      }
    } else {
      for ( i in obj ) {
        value = callback.call( obj[ i ], i, obj[ i ] );

        if ( value === false ) {
          break;
        }
      }
    }
  }

  return obj;
},
trim:function(text){ //去前后空格
  return text == null ? "" : core_trim.call( text );
},
makeArray:function(arr,results){ //类数组转真数组
  // makeArray可以将一个类数组转换成真正的数组。如果传入第二个参数resullts(仅在jQuery内部使用),
  //第一个参数arr中的元素将被合并入第二个参数,最后返回第二个参数,此时返回的不一定是真正的数组。
  // arr:待转换对象,可以是任何类型
  // results:仅在jQuery内部使用。如果传入参数resultes,则在该参数上添加元素。
  // 定义返回值ret。如果传入了参数result则把该参数作为返回值,否则新建一个空数组作为返回值。
  var ret = results || [];
  // 如果传入的参数arr不是null、undefined的情况
  if(arr != null){
    // 使用isArrayLike判断arr是否是数组或类数组对象。
    //如果是数组或类数组对象,调用jQuery.merge()将arr合并到返回值ret中
     if(isArraylike(Object(arr))){ //判断是不是类似数组
          jQuery.merge(ret,
            typeof arr === "string" ? [arr] :arr
          ); 
     }
     // 否则,因为不确定ret的格式。所以选择push.call()的方式合并而不是ret.push()。
     //如果只传入参数array,则返回值ret是真正的数组;如果还传入了第二个参数result,则返回值ret的类型取决于该参数的类型
     else{
       core_push.call(ret,arr);
     }
  }
  // 返回ret
  return ret;
},
//jQuery.inArray()函数用于在数组中搜索指定的值,并返回其索引值。如果数组中不存在该值,则返回 -1。
inArray:function(elem,arr,i){ // 数组版 indexOf
  return arr == null ? -1 : core_indexOf.call(arr,elem,i);
},
merge:function(first,second){ //合并数组
   // 用于合并两个数组的元素到第一个数组中,第一个参数可以是数组或类数组对象,即必须含有整数(或可以转换成整些)属性length;
   //第二个参数可以数组,类数组对象或任何含有连续属性的对象。注意:方法jQuery.merge()的合并具有破坏性,
   //将第二个参数合并到第一个参数时,会改变第一个参数。如果不希望改变第一个参数,可以在调用jQuery.merge()方法前对第一个参数进行备份。
    // 初始化变量,将second.length转换成数字,
    //并将first.length赋值给i,循环添加second的属性到first中,因为不确定first是否是数组,所以用i来修正first.length。最后返回first。
 var l = second.length,
 i = first.length,
 j = 0;
  if(typeof l ==="number"){ //判断第二个参数是数组
    for(;j<l;j++){
      first[i++] = second [j];
    }
  }else{
    while(second[j] !==undefined){
         first[i++] = second[j++];
    }
  }
  first.length = i;
  return first;
},
grep:function(elems,callback,inv){ // 用于查找数组中满足过滤函数的元素,原数组不受影响。
   // elems待遍历查找的数组
  // callback过滤每个元素的函数,执行时被传入两个参数;当前元素和它的下标。该函数应该返回一个布尔值。
  // invert如果参数是false或未传入,grep()会返回一个满足回调函数的元素数组;
  //如果invert是true,则返回一个不满足回调函数的元素数组。
  var retVal,
  ret = [],//筛选出来的数组
  i = 0;
  length = elems.length;
  //在使用grep函数的时候,如果给出了第三个参数且非null/undefined/0""/等值,
  //则inv为true,否则为false。这样做的目的就是保证inv和retVal的值都只能在true/false中取,而非其它值,为后续判断提供便利。
  inv =!!inv;
  for(;i<length;i++){
     retVal =!! callback(elems[i],i);
     if(inv !== retVal){
        ret.push(elems[i]);
     }
  }
  return ret;

},
map:function(elems,callback,args){ //映射新数组
  //args参数只在源码内部使用
 // 静态方法jQuery.map()对数组中的每个元素或对象的每个属性调用一个回调函数,
 //并将回调函数的返回值放入一个新数组中。执行回调函数时传入两个参数:数组元素或属性值,
 //元素下标或属性名。关键字this指向全局对象window。回调函数的返回值会被放入新的数组中;
 //如果返回一个数组,数组中将被扁平化后插入结果集;如果返回null或undefined,则不会放入任何元素。
 var value,
 i =0,
 length = elems.length,
  // 判断elems是数组,如果为true,将通过下标遍历
 isArray = isArraylike(elems),//是不是类数组
 ret = [];//返回的数组
 if(isArray){
    for(;i<length;i++){
       // 为每个元素执行回调函数callback,执行时依次传入三个参数:元素、下标、arg
        value = callback(elems[i],i,arg);
         // 如果回调函数的返回值不是null和undefined,则把返回值放入结果集ret
        if(value !=null){
           ret[ret.length] = value;
        }
    }
    //否则将通过属性名进行遍历
 }else{
   // 对于对象通过for..in循环遍历属性名
     for(i in elems){
        // 为每个属性值执行回调函数callback,执行时依次传入三个参数:元素、下标、arg
        var value = callback(elems[i],i,arg);
        // 如果回调函数的返回值不是null和undefined,则把返回值放入结果集ret
        if(value !=null){
           ret[ret.length] = value;
        }
     }
 }
 return core_concate.apply([],ret);//concat()方法通过合并(连接)现有数组来创建一个新数组
},
guid: 1,//唯一标识符(内部)
proxy:function(fn,context){//改this指向
  var tmp, args, proxy;
  if ( typeof context === "string" ) {
    tmp = fn[ context ];
    context = fn;
    fn = tmp;
  }
  if ( !jQuery.isFunction( fn ) ) {
    return undefined;
  }
  args = core_slice.call( arguments, 2 );
		proxy = function() {
			return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
		};

		// Set the guid of unique handler to the same of original handler, so it can be removed
		proxy.guid = fn.guid = fn.guid || jQuery.guid++;

		return proxy;
},
access: function( elems, fn, key, value, chainable, emptyGet, raw ) {//多功能值操作(内部)
  var i = 0,
    length = elems.length,
    bulk = key == null;

  // Sets many values
  if ( jQuery.type( key ) === "object" ) {
    chainable = true;
    for ( i in key ) {
      jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
    }

  // Sets one value
  } else if ( value !== undefined ) {
    chainable = true;

    if ( !jQuery.isFunction( value ) ) {
      raw = true;
    }

    if ( bulk ) {
      // Bulk operations run against the entire set
      if ( raw ) {
        fn.call( elems, value );
        fn = null;

      // ...except when executing function values
      } else {
        bulk = fn;
        fn = function( elem, key, value ) {
          return bulk.call( jQuery( elem ), value );
        };
      }
    }

    if ( fn ) {
      for ( ; i < length; i++ ) {
        fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
      }
    }
  }

  return chainable ?
    elems :

    // Gets
    bulk ?
      fn.call( elems ) :
      length ? fn( elems[0], key ) : emptyGet;
},
now: Date.now,//当前时间该函数的作用类似于 new Date().getTime()
swap: function( elem, options, callback, args ) {//CSS交换(内部)
  var ret, name,
    old = {};

  // Remember the old values, and insert the new ones
  for ( name in options ) {
    old[ name ] = elem.style[ name ];
    elem.style[ name ] = options[ name ];
  }

  ret = callback.apply( elem, args || [] );

  // Revert the old values
  for ( name in options ) {
    elem.style[ name ] = old[ name ];
  }

  return ret;
}
});
//4.jQuery.extend() : 扩展一些工具方法
发布了139 篇原创文章 · 获赞 27 · 访问量 24万+

猜你喜欢

转载自blog.csdn.net/chunchun1230/article/details/104181262