【jquery源码四】jQuery对象原型下的方法 【jquery源码】工具方法汇总①。

前言:通过上篇文章已经知道了,jQuery实例对象中的大量方法很多都是通过$.fn.extend()去进行扩展出来的,但是jQuery下还是有些方法写在上面的,这些方法的作用是相对重要的,而且不会经常需要修改、优化,或者删除的方法。现在来探究下这些方法的奥义。

【jquery源码】目录。

一、模拟封装jQuery对象的

<!DOCTYPE >
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>模拟jQ对象</title>
<script>
(function(window,undefined){  
	var jQ = function(selector){  
		return new jQ.fn.init(selector);  
	};  
	jQ.fn = jQ.prototype = {  
		jquery:'2.0.3',     //jquery版本号信息
		constructor: jQ,    //添加构造器属性
		length:0,			//初始length属性
		selector:'',		//初始selector属性
		init: function(selector){   
			var match, elem;
			if ( !selector ) {	
				return this;
			}
			elem = document.getElementsByTagName(selector);
			for(var i =0,len=elem.length; i<len; i++){
				this[i] = elem[i];
			}
			this.context = document;
			this.length = elem.length;
			this.selector = selector;
		},
		toArray: function(){},
		get: function(num){},
		pushStack: function(){},
		each: function(){},
		ready: function(){},
		slice: function(){},
		first: function(){},
		last: function(){},
		eq: function(){},
		map: function(){},
		end: function(){},
		push: function(){},
		sort: function(){},
		splice: function(){} 
	}; 
	jQ.fn.init.prototype = jQ.fn;  
	  
	window.$$ = jQ;    	
})( window ); 
</script>
</head>

<body>
<div>div1</div>
<div>div2</div>
<div>div3</div>
<div>div4</div>
<script>
	console.log($$('div'));
</script>
</body>
</html>

运行结果


这是我之前写的模拟jQuery对象,如有看不明白的可以查看我之前的文章。现在主要看原型下的方法。


原型下的方法并不多,除了init()初始化jQuery对象外,只有14个方法。


①、toArray();

就是将jQuery实例对象转换成数组类型。

扫描二维码关注公众号,回复: 1873903 查看本文章
toArray: function(){
			return [].slice.call(this);	
		}
<script>
console.log($$('div').toArray());
</script>
运行代码:

这里其实就是调用了数组下的slice方法,通过call()的机制,给$$对象调用。


②、get();

get()方法相信大家也用过很多了,在将jQuery对象转换成DOM对象的时候要用到。

get: function(num){
    return num == null ?
	   this.toArray() :
	   ( num < 0 ? this[ this.length + num ] : this[ num ] );
},
<script>
console.log($$('div').get());
$$('div').get(0).style.color = 'red';
$$('div').get(-3).style.color = 'blue';
</script>
运行结果

$$('div').get(); 当get()里面并没有参数的时候,其实调用的是之前创建的toArray方法。

$$('div').get(0),其实就是$$('div')[0],因为我们之前封装jQ对象的时候,是把他的键值设置为数字的,这样也就可以获取到第一个div DOM节点对象了。

$$('div').get(-3),当参数为负数时,我们将负数与$$('div')的length属性的值进行了相加,获取到了1,相当于$$('div').get(1)


③pushStack || eq();  || ⑪end();

因为第三个的pushStack方法相对特殊一点,跟eq(),end()方法来一起使用会比较好理解。

$('div').eq(0).css('color','red').end().css('borderBottom','1px solid #ccc');

运行结果(我们先改变了第一个div的颜色,然后有返回原来的$('div')中,把所有div添加了一个border-bottom)


在jQuery中使用end(),就需要用到pushStack()方法的功效了。我们知道end() 方法会结束当前链条中的最近的筛选操作,并将匹配元素集还原为之前的状态。这是怎么样去实现的呢?

在模拟源码中我们想给jQ对象添加一个方法

jQ.merge = function( first, second ) {
	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;
}

jQ.merge是用于数组的合并或者是对象与自变量的合并。

例如

var arr4 = {
	0:'c',
	1:'d',
	length: 2,
	sayHi: function(){
		console.log('Hi');	
	}	
}
var arr5 = ['a','b'];

console.log($$.merge(arr4,arr5));

输出结果:

添加pushStack, eq, end方法(freddySay方法用于稍后的测试)

pushStack: function(elems){
	var ret = jQ.merge( this.constructor(), elems );
	ret.prevObject = this;
	ret.context = this.context;
	return ret;
},
eq: function(i){
	var len = this.length,
	    j = +i + ( i < 0 ? len : 0 );
	return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );	
},
end: function(){
	return this.prevObject || this.constructor(null);	
},
freddySay: function(){
	console.log(this.length);
	return this;	
}
<body>
<div>div1</div>
<div>div2</div>
<div>div3</div>
<div>div4</div>
<script>
$$('div').eq(0).freddySay().end().freddySay();
console.log($$('div').eq(0));
</script>
</body>

运行结果


这时候效果已经实现了。

pushStack: function(elems){
	var ret = jQ.merge( this.constructor(), elems );
	ret.prevObject = this;
	ret.context = this.context;
	return ret;
},
eq: function(i){
	var len = this.length,
	j = +i + ( i < 0 ? len : 0 );
	return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );	
}

$$('div').eq(0),

可以知道var len = this.length; 这里len是为4,即j = 0 + 0 ,

然后return this.pushStack( this[0] ),也就是相当于 return this.pushStack($$('div')[0]);

在pushStack方法中:

var ret = jQ.merge( this.constructor(), $$('div')[0] );    //这里的this.constructor就是jQ对象

      ret.prevObject = this;     

      ret.context = this.context;

      return ret ;

其实就是把$$('div')[0]与jQ对象,合并成一个对象,相当于实例了一个新的jQ对象,且把原先的$$('div')保存到了,prevObect属性下了。

end: function(){
	return this.prevObject || this.constructor(null);	
},

最后end()方法 就是返回一个prevObject中的jQ对象 或者一个空的构造对象。


④、each()

each就是遍历jQ实例对象的方法了。

each: function(){
	return jQ.each( this, callback, args );	
}

其实这里调用jQ中封装的的jQ.each()方法,对jQ.each()有很明白的可以看

【jquery源码】工具方法汇总①。


⑤、ready()

ready: function(){
	jQuery.ready.promise().done( fn );

	return this;
}

ready()方法是在浏览器中DOM加载完毕之后触发的方法,涉及到的知识面太广,蛮点也会单独分出一篇文章来说它。


⑥、slice()

slice: function(){
	return this.pushStack( [].slice.apply( this, arguments ) );
}

slice()方法在数组中是对数组进行分割,而这里也是将jQ对象进行分割,并通过this.pushStack方法跟eq()那里一样,进行创建新的jQ对象,并添加prevObject

<body>
<div>div1</div>
<div>div2</div>
<div>div3</div>
<div>div4</div>
<script>
$$('div').slice(0,2).freddySay().end().freddySay();
console.log($$('div').slice(0,2));
</script>
</body>


⑦first()  || ⑧last()

了解eq是怎么实现之后,first(),last()方法也就很好解决了,直接调用eq也就行了。

first: function(){
	return this.eq( 0 );
},
last: function(){
	return this.eq( -1 );	
}


⑩map()

map()方法是对数组的整合处理,这里倒时也会跟each方法一起解析。


push() || ⑬sort() ⑭splice() 在开发中并没有用到,并且jQuery的注释中也说明了,这三个方法一般用于jQuery内部处理一些东西的时候使用。

push: [].push,
sort: [].sort,
splice: [].splice

很简单的直接引用数组的方法就对了。


汇总:这就是综上合并后的模拟jQuery的代码

(function(window,undefined){  
	var jQ = function(selector){  
		return new jQ.fn.init(selector);  
	};  
	jQ.fn = jQ.prototype = {  
		jquery:'2.0.3',     //jquery版本号信息
		constructor: jQ,    //添加构造器属性
		length:0,			//初始length属性
		selector:'',		//初始selector属性
		init: function(selector){   
			var match, elem;
			if ( !selector ) {	
				return this;
			}
			elem = document.getElementsByTagName(selector);
			for(var i =0,len=elem.length; i<len; i++){
				this[i] = elem[i];
			}
			this.context = document;
			this.length = elem.length;
			this.selector = selector;
		},
		toArray: function(){
			return [].slice.call(this);	
		},
		get: function(num){
			return num == null ?

			this.toArray() :

			( num < 0 ? this[ this.length + num ] : this[ num ] );
		},
		pushStack: function(elems){
			var ret = jQ.merge( this.constructor(), elems );
			ret.prevObject = this;
			ret.context = this.context;
			return ret;
		},
		each: function(){
			//return jQuery.each( this, callback, args );	
		},
		ready: function(){
			//jQuery.ready.promise().done( fn );

			//return this;
		},
		slice: function(){
			return this.pushStack( [].slice.apply( this, arguments ) );
		},
		first: function(){
			return this.eq( 0 );
		},
		last: function(){
			return this.eq( -1 );	
		},
		eq: function(i){
			var len = this.length,
				j = +i + ( i < 0 ? len : 0 );
			return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );	
		},
		map: function(){
			/*return this.pushStack( jQuery.map(this, function( elem, i ) {
				return callback.call( elem, i, elem );
			}));*/
		},
		end: function(){
			return this.prevObject || this.constructor(null);	
		},
		push: [].push,
		sort: [].sort,
		splice: [].splice,
		freddySay: function(){
			console.log(this.length);
			return this;	
		}
	}; 
	jQ.fn.init.prototype = jQ.fn;  
	
	jQ.merge = function( first, second ) {
		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;
	}
	  
	window.$$ = jQ;    	
})( window ); 



猜你喜欢

转载自blog.csdn.net/w390058785/article/details/80866320
今日推荐