javascript之模仿jQuery实现框架雏形

JQuery是如此的强大,所以我决定模仿jQuery造一个轮子,边造轮子边学习jQuery是如何利用各种技巧实现那些非常强大的功能的。既然是模仿jQuery,我决定将新的框架命名为jqc,jQuery copy之意。那么接下来让我们从零实现一个框架的雏形吧,如有谬误有劳告知。

沙箱模式

在一切的开始,我们需要定义一个沙箱来将我们的所有代码放在里面,只留部分接口供外部调用。在沙箱内的所有变量都属于局部变量,不会污染全局变量环境。

(function(window,undefined){
	var jqc = function(selector){
		//.....
	}
	//.......
	window.jqc = jqc;
})(window)

  

在该自调用函数中有两个形参window和undefined,并传入一个window当做实参。此时在内部使用window对象时直接在函数内部就能查询到window对象,不需要在作用域链中一层一层的往上查找。而由于没有传入第二个参数,所以在函数内使用undefined变量时,值为undefined。这是为了防止低版本浏览器没有undefined关键字,在使用undefined时会因为没有undefined变量而导致浏览器报错。
在最后,将jqc挂在window对象上,对外提供一个接口来操作,而其他变量将被隐藏保护起来。

 

构造函数

之后,我们需要一个构造函数,用来返回对象。在jQuery中,new jQuery()与jQuery等价。是因为使用了构造函数调用模式的特性,当使用new关键字声明的函数return的是对象时,返回的是return对象。

(function(window,undefined){
	var jqc = function(selector) {
		return new F(selector);
	}
	var F = function(selector){};
	//一些工具方法可以直接挂在jqc对象上面
	jqc.push = function(){};
	jqc.each = function(){};
	jqc.isString = function(){};
	jqc.isDom = function(){};
	//.....

	//jqc对象继承的方法添加到F原型对象上
	F.prototype = {
		constructor:F,
		appendTo:function(){},
		push:function(){},
		each:function(){}
		//.....
	}
	window.jqc = jqc;
})(window)

 

也就是说在以上代码中,new jqc()和jqc()是等价的。此时和jQuery的效果大致相同了,但是有一点需要注意的是,现在暴露在外的接口仅仅是jqc,而最重要的构造函数F没有对外公开,也就是说外界无法修改F的原型对象来为jqc对象添加新的方法,也就无法为jqc写插件。
在jQuery中,将构造函数与jQuery函数联系起来,仅仅需要暴露jQuery就可以对构造函数进行扩展,也就是为jQuery写插件。

var jQuery = function(selector, context) {
	return new jQuery.fn.init(selector, context, rootjQuery);
}
jQuery.fn = jQuery.prototype = {
	init:function(selector , context, rootjQuery){
		//........
	}
}
jQuery.fn.init.prototype = jQuery.fn;

  


可以看到,在jQuery中,调用jQuery函数将实例化一个init构造函数返回,这里的init就相当于上面我写的F构造函数。jQuery将init构造函数放入jQuery函数的原型中,并且将jQuery的原型对象赋值给init原型对象,这样修改jQuery的原型对象就相当于修改构造函数init的原型对象。所以就可以实现只提供一个接口的前提下对jQuery的原型进行扩展,也就是可以为jQuery实现插件。
而且,源码里还为jQuery函数的原型对象提供了一个简写fn,这样访问原型对象时就不需要写"prototype"这么长的单词,仅仅使用"fn"就可以了。

(function(window,undefined){
	var jqc = function(selector) {
		return new jqc.fn.init(selector);
	}
	jqc.fn = jqc.prototype = {
		constructor:jqc,
		init:function(){},
		//实例可继承的方法在这里添加
		appendTo:function(){},
		push:function(){},
		each:function(){}
	}
	//为jqc添加方法则可以直接jqc.XXX = function(){}
	jqc.push = function(){};
	jqc.each = function(){};
	jqc.isLikeArr = function(){};
	jqc.isString = function(){};
	jqc.isFunc = function(){};
	jqc.isDom = function(){};

	jqc.fn.init.prototype = jqc.fn;
	window.jqc = jqc;
})(window)

  

修改代码如上后,jqc框架的骨架大致成型。接下来就是不断的添加方法了。但是还缺了点东西,看注释,如果不断的在原型对象里添加方法则会显得十分臃肿,各种功能的方法堆积在一起。为jqc添加方法也是一样,不断的重复写jqc.XXX = function(){}。所以我们需要一个让所有方法分组的办法。

 

扩展与分模块

来让我们看看jQuery是如何解决上面的问题吧

jQuery.extend = jQuery.fn.extend = function() {
	//实现继承的方法
}

  

在jQuery中为jQuery函数以及jQuery的原型对象实现了一个名为extend的方法,该该方法的作用是将传入的对象所有属性赋值给this。由于extend的算法特别庞大,让我们来实现一个简单的extend方法。

jqc.extend = jqc.fn.extend = function(obj){
	var k;
	for( k in obj){
		this[k] = obj[k];
	}
}

  这样我们就能通过调用jqc.extend为jqc添加新的属性。通过jqc.fn.extend为jqc的原型对象添加方法。现在jqc框架算是初具雏形了,接下来让我们重新组织上面的方法吧。由于JQuery的大部分方法的算法实在是复杂,所以我不打算照搬jQuery的各种方法,牺牲一些兼容性来用简便的方法实现一个大致能用的框架。

(function(window,undefined){

var jqc = function(selector) {
	return new jqc.fn.init(selector);
}
jqc.fn = jqc.prototrype = {
	constructor:jqc,
	length:0,
	//初始化方法
	init:function ( selector , context ){
		var context = context || document;
		//判断传入的是否是  null  ''  undefined  0
		if (!selector) return;
		//判断selector是否是字符串
		if (jqc.isString(selector)) {
			if (selector.charAt(0) === "<") {
				//当为html标签时将调用parseHTML方法获取dom数组
				//将获取到的数组调用pushArr方法添加到自身
				this.push( parseHTML(selector) );
			}else{
				//否则调用select方法获取dom数组
				//将获取到的数组调用pushArr方法添加到自身
				this.push( select(selector,context) );
			}
		//判断selector是否是dom节点
		} else if(jqc.isDom(selector)) {
			//当传入的是dom元素时将dom元素添加到自身
			this.push( [selector] );
		//判断传入的是否是dom数组
		} else if(jqc.isLikeArr(selector) && jqc.isDom(selector[9]) ){
			this.push(selector);
		//判断传入的是否是jqc对象
		} else if( jqc.isJqc(selector) ){
			return this;
		}
	},
	push:function (){}
}

jqc.fn.init.prototype = jqc.fn;

//工具类方法模块
jqc.extend({
	//实现push方法
	push:[].push,
	//循环遍历方法
	each:function (){}
});

//判断类型模块
jqc.extend({
	//判断是否是数组或者伪数组
	isLikeArr:function(obj){},
	isString:function(obj){},
	isFunc:function(obj){},
	//是否是dom对象
	isDom:function(obj){},
	//是否是jqc对象
	isJqc:function(obj){}
});

//dom操作方法
jqc.fn.extend({
	appendTo:function(selector){}
})

//html转换dom对象并返回数组对象
var parseHTML = function(html){}

//查询dom元素并返回数组对象
var select = function ( selector ) {};

window.jqc = jqc;

})(window)

  

以上代码删除了所有方法的方法体仅仅保留了init构造函数的方法体,完整代码请看GitHub地址:https://github.com/XLandMine/jqc。通过上面的伪代码就可以看到jqc框架已经大致成型,今后扩展就可以使用extend对jqc或者jqc的原型对象添加新的方法,而且还可以通过不同的extend代码块来对方法按功能分组,而且在沙箱之外,也可以通过extend来为jqc框架添加新的方法来写jqc插件。
jqc框架模拟了jQuery框架的使用方法,如果补全上面的代码,那么可以通过jqc("<div>1</div>").appendTo("body")来创建一个div元素并添加到body下。

posted @ 2016-04-14 21:44  地雷  阅读(451)  评论(0编辑  收藏  举报