zoukankan      html  css  js  c++  java
  • 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下。

  • 相关阅读:
    正则表达式大全
    List.FindAll查找实例
    list的ForEach跟FindAll
    利用C#实现数据同步功能
    数据集和数据库的同步-DataAdapter的使用
    OpenCV
    关于android toolchain
    每天一点Linux 进入终端模式
    源码下编译APK,却是总是提示,找不到符号:SystemProperties 。。。
    使用Android NDK中的独立toolchain来开发C/C++程序
  • 原文地址:https://www.cnblogs.com/LandMine/p/5392909.html
Copyright © 2011-2022 走看看