当前位置: 代码迷 >> Web前端 >> jQuery兑现原理浅析――一个调试类的实现
  详细解决方案

jQuery兑现原理浅析――一个调试类的实现

热度:85   发布时间:2012-11-16 14:12:14.0
jQuery实现原理浅析――一个调试类的实现
调试js代码时经常需要用到alert(),每次都要听到刺耳的‘当…’,于是就自己编写了一个简短的调试信息输出的函数debug。最近又在jQuery框架,于是就仿照其编写风格改进了一下。下面就把笔者在这个过程中的一些心得说出来,供大家参考。
一、函数实现的自定义调试函数debug
平常大家所用到的alert(msg),作用是把msg打印到警告框,虽然在编写代码时比较方面省事,但缺点也是很明显的,那就是每次都要点击,并且还有扰人的警告声。笔者所实现的debug函数,基本功能也跟alert(msg)类似,不同的是不需要点击。其把信息打印到一个个具有类名为debug_output的div里,并加上合适的样式,醒目而且不扰人。实现的代码如下,用到了jQuery库,而且我把jQuery.noConflict()打开,为的是能在不同的库共存(我所参与的项目要在Discuz!中加入jQuery库,而前者的common.js中也实现了$(),并且与jQuery不同):
function debug(s) {
	//向文档(DOM)中添加一个存放调试信息的容器,以及通过调用puts方法将调试信息加入到文档中并显示出来。
	//debug_container采用绝对定位布局在文档中,保证当有输出时总是可见
	if(!jQuery('#debug_container').length)
		jQuery('<div id="debug_container"></div>').appendTo('body')
			.css({
				position: 'absolute',
				top: '300px',
				width: '100%',
				index: 99
				});
	//向#debug_container中添加带有类debug_output的div
	jQuery('<div class="debug_output"><em style="margin-right:5px;">Output:</em>
</div>').appendTo('#debug_container');
	//搜索指定类名的div,并向其中加入要输出的调试信息,以及为了突出显示和美观而添加的样式。因为要把调试信息加入到最后一个拥有指定类名的div(原因大家可想象一下),所以用到了:last选择器
	var thisAlert = jQuery('div.debug_output:last');
	thisAlert.append('<em class="content">'+ s +'</em>').css
		({
			border: '1px solid #f00',
			'font-family': '"Courier New", monospace',
			background: '#fcc',
			width: '100%',
			'margin-top': '5px'
		});
}

在页面中插入以下代码作为测试:
//Output: testing
debug('testing');

本文笔者将以此函数为例,将一个普通的函数改造成构造类,进而在此基础上探讨jQuery式的封装。不过在开始之前,先将本文中涉及到的javascript的类和封装基本知识概述一下,有基础的读者可选择跳过。
必要的知识准备:javascript类和封装
现行的编写javascript的方法共有三种,即工厂模式、构造函数模式和原型模式,而用得最多的不是其中的哪一种,而是后两者的混合方式。面向对象编程(Object Oriented Programming,简称OOP)领域的类的概念通常包含属性(Attributes)和方法(Methods),但是在javascript里并没有明确的类的概念,所以我们以特定的方法编写函数让其实现类的功能。考虑以下的代码(混合模式):
//注:这段代码仅供演示概念之用,在实际编程中这种无意义的代码是没有任何用处的
function OO(s) {
	//在构造函数中规定类的属性
this.test = s;
}
OO.prototype = {
	//在原型(prototype)中绑定方法,则每一个类的实例都将拥有这样的一个方法的副本
	init: function(s) {
		this.trace();
	},
	trace: function() {
		alert(this.test);
	}
};
//在使用类之前必须进行实例化,下面的代码将产生一个错误:对象不支持此属性或方法
OO.init();
//应用以下的方式初始化一个OO类的实例
var o = new OO(‘This is a testing!’);
//调用类的实例方法
o.init();//弹出警告框:This is a testing!
o.trace();//弹出警告框:This is a testing!

如果读者对javascript类和封装不甚明了,或者有兴趣继续研究的话,参考这篇文章。
二、jQuery式的自定义对象Debug
不忙写代码,先把jQurey中的选择器$的实现原理简要分析一下。笔者摘录了jQurey v1.3.2的部分代码,这些都是实现$的关键和核心,方便讨论。
jQuery $选择器的核心实现代码
//第一点要说的就是(function() {})(),闭包。更多关于闭包的信息请浏览[url=http://www.cnblogs.com/zhangle/archive/2010/07/02/1770206.html这篇文章[/url]
(function(){
var
	//涉及到作用域的问题,window.$( window.jQuery)是为了能在闭包之外访问到$
 window = this,
jQuery = window.jQuery = window.$ = function(selector, context){
	//使用了new运算符,在变量$(jQuery)创建时即被实例化,而不是只声明了一个构造类
		return new jQuery.fn.init(selector, context);
	};
jQuery.fn = jQuery.prototype = {
	//这个是$(或jQuery)被创建时自动执行的函数
init: function( selector, context ) {
		//实现代码略
	}
}
//最后,把debug的原型赋值给debug的方法init,关于这点请读者认真思考:因为创建debug对象时是采用返回函数的形式(return new debug.fn.init(s);),所以debug现在的prototype属性实际上指向debug.fn.init.prototype。如果不在最后反赋值的话,debug.fn.init中是没有办法访问到this.puts()的,这时浏览器报错,不存在该属性或方法
jQuery.fn.init.prototype = jQuery.prototype;
})();

为了更好地理解jQuery的实现原理,请考虑下面这个小程序,同样注意它纯为演示功能所写,在实际编程实践中是没有意义的。
(function() {
var OO = window.OO = function(s) {
	this.test;
	return OO.fn.init(s);
};
OO.fn = OO.prototype = {
	init: function(s) {
		this.test = s;
		this.testing();
	},
	testing: function() {
		alert(this.test);
	}
};
OO.fn.init.prototype = OO.prototype; 
})();

通过下面的代码调用OO,注意此时OO在创建时就已经被初始化了,因此不需要new关键字。
//弹出警告框:testing
OO('testing');

写到这里,结合前述调试函数,一个自定义的调试类已经呼之欲出了。只要把函数的代码稍作修改,加入到jQuery式的框架内即可。代码如下:
(function() {
var
//	window = this,
	Debug = window.Debug = function(s) {
			return new Debug.fn.init(s);
};
Debug.fn = Debug.prototype = {
	init: function(s) {
		if(!jQuery('#debug_container').length)
			jQuery('<div id="debug_container"></div>').appendTo('body')
				.css({
					position: 'absolute',
					top: '300px',
					width: '100%'
					});
		this.puts(s);
	},
	puts: function(s) {
		//向#debug_container中添加带有类debug_output的div
		jQuery('<div class="debug_output"><em style="margin-right:5px;">Output:</em>
</div>').appendTo('#debug_container');
		var thisAlert = jQuery('div.debug_output:last');
		thisAlert.append('<em class="content">'+ s +'</em>').css
		  ({
			border: '1px solid #f00',
			'font-family': '"Courier New", monospace',
			background: '#fcc',
			width: '100%',
			'margin-top': '5px'
		  });
	}
};
Debug.fn.init.prototype = Debug.prototype;
})();

将这段代码加入到文档中,在任何需要查看输出信息的地方添加如下代码,就像原来的alert一样方便,但是体验却更加美好。
//Output: testing
Debug('testing');

三、文中涉及到的知识
(1)jQuery的基本使用技巧,点此深入了解。
(2)闭包,点此深入了解。
(3)对象的原型prototype,点此深入了解。
(4)javascript类和对象的概念,点此深入了解。
  相关解决方案