jQuery源码学习

jQuery使用上的几大特点

1、使用$()函数方式直接生成jQuery对象

2、链式调用(在函数的结尾返回this,this的值是根据函数运行的时候确定的)

 

无new函数创建对象:把 jQuery.prototype.init.prototype = jQuery.prototype

jQuery.fn,它实际上是 prototype 的一个引用,指向 jQuery.prototype 。好处:书写简洁

var jQuery = function(){
  return new jQuery.fn.init();
}
jQuery.fn = jQuery.prototype = {
  constructor: jQuery,
  init: function(){
    this.jquery = 3.0;
    return this;
  },
  each: function(){
    console.log('each');
    return this;
  }
}
jQuery.fn.init.prototype = jQuery.fn;
jQuery().each().each();
// 'each'
// 'each'

 

1、$.globalEval( code )-----全局性的执行一段代码

var name = "全局变量";
$(function () { 
    function test(){
        var name = "局部变量";
        alert(name); // 局部变量
        eval( "alert(name);" ); // 局部变量
        $.globalEval( "alert(name);" );  // 全局变量
    }
    test();
})

大致思路:首先创建script元素;然后将传进来的参数赋给script.text属性;最后采用链式写法,将创建的script对象添加到head对象里面(这时候就执行JS代码),然后再将添加的script对象删除。

// 部分源码
globalEval: function(a) {
	p(a)
}

function p(a, b) {
	b = b || d;    //b没有传进来*(只传了一个参数),则b=document
	var c = b.createElement("script");
	c.text = a,
	b.head.appendChild(c).parentNode.removeChild(c)
}

 

2、$.extend()---用于将一个或多个对象的内容合并到目标对象(给jQuery扩展静态属性和方法)-----默认是浅拷贝,返回目标对象

1)扩展到对象层面--jQuery.extend(),扩展的是静态方法,可以直接使用$调用;

2)扩展到选择器函数对象层面----jQuery.fn.extend(),扩展的方法是实例的方法,由$('#id名')或者$('.类名')调用

具体作用:

1)将两个或更多对象的内容合并到第一个对象(如果只有一个参数对象,则将其扩展到jQuery对象上)---实现默认字段的覆盖

jQuery.extend( target [, object1 ] [, objectN ] )
function getOpt(target, obj1, obj2, obj3){
 $.extend(target, obj1, obj2, obj3);
 return target;
}
 
var _default = {
 name : 'wenzi',
 age : '25',
 sex : 'male'
}
var obj1 = {
 name : 'obj1'
}
var obj2 = {
 name : 'obj2',
 age : '36'
}
var obj3 = {
 age : '67',
 sex : {'error':'sorry, I dont\'t kown'}
}
getOpt(_default, obj1, obj2, obj3); // {name: "obj2", age: "67", sex: {error: "sorry, I dont't kown"}}

2)为jQuery或者jQuery原型扩展方法或属性----如果extend()函数只传一个对象参数,jQuery对象本身被默认为目标对象,浅拷贝

$.extend({
 _name : 'wenzi',
 _getName : function(){
    return this._name;
 }
})
 
$._name; // wenzi
$._getName(); // wenzi

3)深度拷贝和浅度拷贝----若第一个参数是boolean类型,且值是true(深拷贝)或者false(浅拷贝),把第二个参数作为目标参数进行合并

                jQuery.extend( [deep ], target, object1 [, objectN ] )
                //深复制
                var obj = {name:'wenzi', score:80};
                var obj1 = {score:{english:80, math:90}}
                $.extend(true, obj, obj1);
                obj.score.english = 10;
                console.log(obj.score.english); // 10
                console.log(obj1.score.english); // 80
 
                //浅复制
                var obj = {name:'wenzi', score:80};
                var obj1 = {score:{english:80, math:90}}
                $.extend(false, obj, obj1);
                obj.score.english = 10;
                console.log(obj.score.english); // undefined
                console.log(obj1.score.english); // 80

jQuery中的extend()实现原理

大致过程:对后一个参数进行循环,然后把后面参数上所有的字段都给了第一个字段,若第一个参数里有相同的字段,则进行覆盖操作,否则就添加一个新的字段

1)对第一个参数做判断,如果不是Boolean类型,且只有一个参数,那么就把jQuery作为target,然后把第一个参数上的字段都赋给target,最后返回target(浅拷贝);如果有多于一个参数,那么第一个参数就是target,然后把后面参数的字段都赋给target,最后返回target(浅拷贝)

2)第一个参数是boolean类型,表示深浅拷贝,true表示深拷贝,false表示浅拷贝。只有两个参数,那么就把jQuery作为target,把第二个参数的字段赋给target,然后返回target。多于两个参数,把第二个参数作为target,然后把后面的参数的字段赋给target,最后面返回target.

// 为与源码的下标对应上,我们把第一个参数称为`第0个参数`,依次类推
jQuery.extend = jQuery.fn.extend = function() {
	var options, 
	 	name, 
	 	src, 
	 	copy, 
	 	copyIsArray, 
	 	clone,
	    target = arguments[0] || {}, // 默认第0个参数为目标参数
	 	i = 1, // i表示从第几个参数开始将目标参数与其进行合并,默认从第1个参数开始向第0个参数进行合并
	 	length = arguments.length,
		deep = false; // 默认为浅拷贝
	 
	// 1、判断第0个参数的类型,若第0个参数是boolean类型,则获取其为true还是false
	// 同时将第1个参数作为目标参数,i从当前目标参数的下一个
	// Handle a deep copy situation
	if ( typeof target === "boolean" ) {
	    deep = target;
 
 		// Skip the boolean and the target
 		// 如果第一个参数是Boolean类型
 		target = arguments[ i ] || {};
		i++;
	}
 
	///2、判断目标参数的类型,若目标参数既不是object类型,也不是function类型,则为目标参数重新赋值空对象
	// Handle case when target is a string or something (possible in deep copy)
	if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
	 	target = {};
	}
 
	///3、若目标参数后面没有参数了,如$.extend({_name:'wenzi'}), $.extend(true, {_name:'wenzi'})
	// 则目标参数即为jQuery本身,而target表示的参数不再为目标参数
	// Extend jQuery itself if only one argument is passed
	if ( i === length ) {
		target = this;
		i--;
	}
	
	// 4、从第i个参数开始遍历
	for ( ; i < length; i++ ) {
		// 获取第i个参数,且该参数不为null和undefind,在js中null和undefined,如果不区分类型,是相等的,null==undefined为true,
		// 因此可以用null来同时过滤掉null和undefind
		// 比如$.extend(target, {}, null);中的第2个参数null是不参与合并的
		// Only deal with non-null/undefined values
		if ( (options = arguments[ i ]) != null ) {
	  
			///5、使用for~in获取该参数中所有可枚举的属性
			// Extend the base object
			for ( name in options ) {
				src = target[ name ]; // 目标参数中name字段的值
				copy = options[ name ]; // 当前参数中name字段的值,有可能是值,Object,Array
		 
				///6、若参数中属性的值就是目标参数,停止赋值,进行下一个字段的赋值
				// 这是为了防止无限的循环嵌套,我们把这个称为,在下面进行比较详细的讲解
				// Prevent never-ending loop
				if ( target === copy ) {
				   continue;
				}
		 
				///7、若deep为true,且当前参数中name字段的值存在且为object类型或Array类型,则进行深度赋值
				// Recurse if we're merging plain objects or arrays
				// jQuery.isPlainObject()返回值为Boolean类型,如果指定的参数是纯粹的对象,则返回true,否则返回false
				if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
					// 若当前参数中name字段的值为Array类型
					// 判断目标参数中name字段的值是否存在,若存在则使用原来的,否则进行初始化
					if ( copyIsArray ) {
						//被复制的属性的值类型为Array
					   copyIsArray = false;
					   //判断该字段在target对象是否存在,存在且为Array,则直接使用原对象,否则创建空的Array
					   clone = src && jQuery.isArray(src) ? src : [];
					 
					}else{
						// 被复制的属性的值得类型为Object
					   //判断该字段在target对象是否存在,若存在且为Object则直接使用,否则创建空的Object
					   clone = src && jQuery.isPlainObject(src) ? src : {};
					}
		 
					// 递归处理,此处为2.2
					// Never move original objects, clone them  
					target[ name ] = jQuery.extend( deep, clone, copy );
		 
					// deep为false,则表示浅度拷贝,直接进行赋值
				// Don't bring in undefined values
				}else if ( copy !== undefined ) {			
					// 若copy是简单的类型且存在值,则直接进行赋值
				    // 若原对象存在name属性,则直接覆盖掉;若不存在,则创建新的属性
				    target[ name ] = copy;
				}
		    }
	 	}
	}
	 
	// 返回修改后的目标参数
	// Return the modified object
	return target;
};

 

3、jquery中type()-----$.type(任意类型对象)
作用---------确定JavaScript内置对象的类型,并返回小写形式的字符串类型名称

               //1、对于对象是undefined  null类型 ,如果是对象上没有的属性,则返回'undefined'    结果都为true
                console.log($.type( undefined ) === "undefined");
                console.log($.type() === "undefined");
                console.log($.type( window.notDefined ) === "undefined");
                console.log($.type( null ) === "null");

                // 2、js中内置的对象
                console.log( jQuery.type( true ) === "boolean" );
                console.log( jQuery.type( new Boolean() ) === "boolean" );
                console.log( jQuery.type( 3 ) === "number");
                console.log( jQuery.type( "test" ) === "string" );
                console.log( jQuery.type( function(){} ) === "function");
                console.log( jQuery.type( [] ) === "array" );
                console.log( jQuery.type( new Date() ) === "date");
                console.log( jQuery.type( new Error() ) === "error" );
                console.log( jQuery.type( Symbol() ) === "symbol" );
                console.log( jQuery.type( /test/ ) === "regexp");
                console.log( jQuery.type( {} ) === "object");
//因为 jQuery 用的是 toString 方法,所以需要有一个 class2type 的对象用来转换
var class2type = {
    "[object Boolean]": "boolean",
    "[object Number]": "number",
    "[object String]": "string",
    "[object Function]": "function",
    "[object Array]": "array",
    "[object Date]": "date",
    "[object RegExp]": "regexp",
    "[object Object]": "object",
    "[object Error]": "error",
    "[object Symbol]": "symbol",
    "[object Set]": "set",
    "[object Map]": "map"
}
var toString = Object.prototype.toString;

jQuery.type = function (obj) {
    if (obj == null) {
        return obj + "";
    }
    return 
      typeof obj === "object" || typeof obj === "function" ? 
        class2type[toString.call(obj)] || "object" : 
        typeof obj;
}

原理:对于null或undefined类型直接返回,如果是object或function类型则调用Object.prototype.toString.call()函数来判断参数的类型

 

4、jquery中的isPlainObject(object)----判断指定参数是否是一个纯粹的对象

//object ---任意类型
$.isPlainObject( object )

var getProto = Object.getPrototypeOf;     //获取父对象---原型对象
var hasOwn = class2type.hasOwnProperty;   //自身是否具有指定的属性,不包括原型上的属性,
var fnToString = hasOwn.toString;
var ObjectFunctionString = fnToString.call( Object );
jQuery.isPlainObject = function (obj) {
    var proto, Ctor;

    // 排除 underfined、null 和非 object 情况
    if (!obj || toString.call(obj) !== "[object Object]") {
        return false;
    }

    // 获取原型对象
    proto = getProto(obj); 

    // Objects with no prototype (e.g., `Object.create( null )`) are plain
    if (!proto) {
        return true;
    }

    // Objects with prototype are plain iff they were constructed by a global Object function
    Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
    return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
}

 

5、$.isArray = Array.isArray( obj )-----判断参数是否是数组

实现原理:Object.prototype.toString.call(obj) === '[object Array]'

if (!Array.isArray) {
  Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === '[object Array]';
  };
}

 

6、$.fn.css()-----设置或返回被选元素的一个或多个样式属性

例子:
//1、返回指定的 CSS 属性的值
css("propertyname");
$("p").css("background-color");

// 设置 CSS 属性
//2、单个css属性
css("propertyname","value");
$("p").css("background-color","yellow");

//3、设置多个 CSS 属性
css({"propertyname":"value","propertyname":"value",...});
$("p").css({"background-color":"yellow","font-size":"200%"});
//css()只有一个参数,参数为字符串则通过getComputedStyle(obj)['attr']返回该属性的值;为
// 对象则再次调用该函数css
//若 css 有两个参数,则为元素的style属性赋值
css : function(attr,val){
    for(var i = 0; i < this.length; i++){
      if(val == undefined){
        if(Object.prototype.toString.call(attr) === '[object Object]'){
          for(var key in attr){
            this.css(key, attr[key]);
          }
        }else if(typeof attr === 'string'){
          return getComputedStyle(this[i])[attr];
        }
      }else{
        this[i].style[attr] = val;
      }
    }
}

 

7、jquery中的merge()----合并两个数组内容到第一个数组

// first---Array类型 第一个用于合并的数组,合并后将包含第二个数组的内容
// Array类型 第二个用于合并的数组,该数组不会被修改
$.merge( first, second )

var arr1 = [ 0, 1, 2 ];
var arr2 = [ 2, 3, 4 ];

$.merge( arr1, arr2);
console.log(arr1);  //[0, 1, 2, 2, 3, 4]
console.log(arr2);  //[2, 3, 4]

$.merge( $.merge([], arr1), arr2);  //这样就不会修改原来的数组对象了
源码:对第二个参数数组进行遍历,添加到第一个参数数组里面,并修改第一个参数数组的length属性
 merge: function( first, second ) {
        var len = +second.length,
            j = 0,
            i = first.length;

        for ( ; j < len; j++ ) {
            first[ i++ ] = second[ j ];
        }

        first.length = i;

        return first;
    } 

8、jquery中的isArrayLike(obj)----判断参数是否为类数组

function isArrayLike(obj) {

  // Support: real iOS 8.2 only (not reproducible in simulator)
  // `in` check used to prevent JIT error (gh-2145)
  // hasOwn isn't used here due to false negatives
  // regarding Nodelist length in IE
  var length = !!obj && "length" in obj && obj.length,
      type = jQuery.type(obj);

  if (type === "function" || jQuery.isWindow(obj)) {
      return false;
  }

  return type === "array" || length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj;
}

 

9、jquery中的makeArray()----将一个类似数组的对象转换为真正的数组对象

$.makeArray( object )
$(function () { 
    var elems = document.getElementsByTagName("div"); // 返回一个节点列表
    var arr = jQuery.makeArray(elems);
    arr.reverse(); //对列表的元素使用一个数组方法
    $(arr).appendTo(document.body);
})
var push = [].push;
jQuery.makeArray = function (arr, results) {
  var ret = results || [];

  if (arr != null) {
    if (isArrayLike(Object(arr))) {
        jQuery.merge(ret, typeof arr === "string" ? [arr] : arr);
    } else {
        push.call(ret, arr);
    }
  }

  return ret;
}

 

 

8、jquery中的each()----$.each()和$(选择器).each----each的实现采用迭代器模式

1)$(选择器).each( callback(index,val)):为每个匹配元素规定运行的函数
$(selector).each(function(index,element))

//例子
$("button").click(function(){
  $("li").each(function(){
    alert($(this).text())
  });
});

2)$.each(arr | obj,callback(index,item)):----对数组或对象遍历

// 遍历数组
var arr = [{name:"limeng",email:"xfjylimeng"},{name:"hehe",email:"xfjylimeng"}];
$.each(arr,function(index,item){
	console.log("索引:" + index + "对应值为:" + item.name); //参数index为遍历索引值,item为当前的遍历对象,可用this指代
});


var arr1 = [ "one", "two", "three", "four", "five" ];
$.each(arr1, function(index,val){  //this指向当前的遍历对象
	console.log(val);   //one   two  three  four   five
});


var arr2 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
$.each(arr2, function(index, val){
	console.log(val[0]);    //1   4   7
});

// 遍历对象
var obj = { one:1, two:2, three:3, four:4, five:5 };
$.each(obj, function(key, val) {
	console.log(obj[key]);    //1   2  3  4  5
});

jQuery.each()和jQuery.fn.each()的源码是一样的

原理:通过w(a)函数判断参数是否为类数组,是则通过for循环对类数组中的每个元素调用一个callback,通过call改变callback的上下文环境为当前元素;否则为对象,则通过for...in对对象的每个元素调用一个callback,通过call改变上下文环境

//返回Boolean类型的值
//判断数组是否是类数组----具有length属性和可通过index属性访问成员
//通过type()函数返回参数的类型---Object.prototype.toString.call()
function w(a) {
	var b = !!a && "length" in a && a.length,
	// 返回参数的类型
	c = r.type(a);
	return "function" !== c && !r.isWindow(a) && ("array" === c || 0 === b || "number" == typeof b && b > 0 && b - 1 in a)
}




each: function(a, b) {
	//a---array || object
	//b----callback(index,val)
	var c, d = 0;
	//是类数组
	if (w(a)) {
		//----array
		for (c = a.length; d < c; d++){
			//b----callback
			if(b.call(a[d], d, a[d]) === !1){
				break;
			} 
		} 
	} else{
		//object
		for(d in a){
			if (b.call(a[d], d, a[d]) === !1) {
				break;
			}
		} 
	} 
	return a
}

 

9、jquery中的trim(str)----去除字符串首尾的空白字符

原理:利用String的replace()和正则表达式以及三目运算符

s = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
trim: function(a) {
	return null == a ? "": (a + "").replace(s, "");
}

 

10、jquery中的makeArray()-----将一个类数组对象转换为真正的数组对象

类数组对象----具有length属性且不为负值;可通过数字下标访问元素。没有数组原型对象上内置的方法。jquery对象($('p')),arguments,NodeList对象(document.getElementsByNodeName('p'))
返回值----数组对象

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值