看了这个才发现jQuery源代码不是这就是说晦涩

看了这个才发现jQuery源代码不是那么晦涩

 

很多人觉得jquery、ext等一些开源js源代码 十分的晦涩,读不懂,遇到问题需要调试也很费劲。其实我个人感觉主要是有几个方面的原因:

1、对一些js不常用的语法、操作符不熟悉

2、某个function中又嵌套了一些内部的function,使得整个代码的层次结构不像java代码那么清晰。

3、js中允许变量先使用后定义,会造成我们看代码时候忽然冒出来一个变量、function,却找不到是在哪里定义的。

 

那么今天给大家分享一下我的经验,扫清你的障碍。

一、一些晦涩的操作符:

1、(function(){})();

几乎所有的开源js代码开篇都是这样(function(……){……})(……);

下面是Jquery的部分源码:

 

(function( window, undefined ) {
	var jQuery = function( selector, context ) {
		// The jQuery object is actually just the init constructor 'enhanced'
		return new jQuery.fn.init( selector, context );
	},

	// Map over jQuery in case of overwrite
	_jQuery = window.jQuery,

	// Map over the $ in case of overwrite
	_$ = window.$,
	……
	indexOf = Array.prototype.indexOf;

	// Expose jQuery to the global object
	window.jQuery = window.$ = jQuery;
})(window);

 

那么这个操作符(function(){})();到底是什么意思呢?

(function(){})中的定义了一个function,紧接着的()表示立即执行这个function。

我们看到Jquery源码第一个()中是定义了一个匿名function( window, undefined ) {};接着末尾有个(window),就表示执行这个匿名function,并传入参数window。

 

在匿名function( window, undefined ) {}中,定义了一个局部变量jQuery;然后在末尾我们看到Jquery末尾有一句 window.jQuery = window.$ = jQuery; 这句代码就表示,将此前定义的jQuery导出到window对象。这也是为什么我们可以在代码任何地方直接使用$、jQuery对象,因为在这里已经将$、jQuery对象挂载到window下去了,而window.$、window.jQuery与直接使用$、jQuery是没有区别的。

(注意,这个window对象是传入的参数window,而不是浏览器window对象!!一个形参、一个实参。我们可以在定义function的时候,将参数window取名为其他字符。所以我们看到jquery.min.js中这个匿名function变成了(function(E,B){})(window);)

 

通常(function(){})()用来封装一些私有成员或者公共成员的导出。

 

2、令人迷惑的","

我们知道“,”一般用于一次定义多个变量、定义多个参数等。像上面的jQuery源码中在var jQuery后面,使用“,”一次定义了很多个变量。

但是,像下面的代码,可能大家就不一定看得懂了:

 

//html:<input type="hidden" value="king" id="nameHide"/>
jQuery(document).ready(function() {
	var showName=function(){
		var value,nameInput=$("#nameHide");
		return nameInput.show(),value=nameInput.val();
	};
	alert(showName());
});
//结果:弹出king

 这里的“nameInput.show(),value=nameInput.val()”中的“,”运算符的作用是返回","右侧表达式的值。所以,return 后面如果有多个表达式,且表达式之间由","隔开,整个return表达式返回的是最后一个","右侧的表达式的值。

 

“,”在开源代码中常常被用于return表达式中,以及跟下面我们要讲到的"()"运算符一起使用。

 

3、“()”广义上的代码包装

我们遇到复杂的逻辑表达式时,我们通常会把需要一起运算的表达式用“()”包起来:(a||b)&&(c||d)

其实,我们可以这样理解:"()"运算符将一个表达式包裹起来作为一个整体进行运算,然后返回这个整体的值。

那么上面的(function(){})()中左侧定义function的()也是这个作用,将这个function给包裹起来,然后返回这个function。我们调用方法一般是a();那么(function(){})的作用就是返回这个function对象,然后(function(){})()右侧的()表示调用这个function。

 

我们再来看其他的用法:

 

 

//html:<input value="kings" id="name"/><div id="nameErrorTip">输入错误!</div>
jQuery(document).ready(function() {
	var nameValidate=function(){
		var value,nameInput=$("#name"),nameErrorTip=$("#nameErrorTip"); 
		return (value=nameInput.val(),value=="king")?(nameErrorTip.hide(),"对了,输入为king!"):(nameErrorTip.show(),"请输入king!");
	};
	alert(nameValidate());
});

//结果 nameErrorTip显示,弹出"请输入king!"



//html:<input value="king" id="name"/><div id="nameErrorTip">输入错误!</div>
//结果 nameErrorTip隐藏,弹出"对了,输入为king!"
 

 

这里“ (value=nameInput.val(),value=="king")”中"()"将里面的表达式作为一个整体进行运算,而里面的表达式又是由","构成的多个表达式组,所以执行的时候会把这多个表达式都执行一次,并且返回最后一个表达式的值!

所以 (value=nameInput.val(),value=="king")执行时,先运算value的值,再判断是否为"king"。如果为king,会执行(nameErrorTip.hide(),"对了,输入为king!")。这个表达式又先将nameErrorTip隐藏,再返回一个"对了,输入为king!"字符串作为 整个return的值。

 

 

4、||、&&、if()逻辑让人头晕

 

||、&&两侧参与运算的是逻辑表达式,if()中也是。但是我们在很多开源代码中看到的||、&&参与运算的表达式看起来却好像不是逻辑表达式……

 

下面节选一段jQuery.tool中的一段源码:

 

e.circular || (f.onBeforeSeek(function(a, b) {
					setTimeout(function() {
								a.isDefaultPrevented()
										|| (n.toggleClass(e.disabledClass,
												b <= 0), o.toggleClass(
												e.disabledClass, b >= f
														.getSize()
														- 1))
							}, 1)
				}), e.initialIndex || n.addClass(e.disabledClass)), f.getSize() < 2
				&& n.add(o).addClass(e.disabledClass), e.mousewheel
				&& a.fn.mousewheel && b.mousewheel(function(a, b) {
							if (e.mousewheel) {
								f.move(b < 0 ? 1 : -1, e.wheelSpeed || 50);
								return !1
							}
						});

 

 这里有多处||、&&。但与运算的表达式却是调用某个函数的返回值。

 

其实,js中的逻辑表达式是按照真值、假值来分的。true是真值;1是真值;一个对象也是真值;false是假值;""、0是假值。

 

在js中&&、||不一定都是用来判断一个表达式的逻辑值是true、false,更多的是用来依据真值或者假值执行相应操作!

 

我们知道,||运算的时候,会先运算左侧的表达式的值,如果为真值,那么真个表达式就为真值,而同时右侧表达式是真值、假值都不重要,因为右侧表达式都不再继续参与运算了。又如果左侧为假值,则继续运算右侧表达式。

&&则先运算左侧表达式,两侧表达式,一个为假值,则整个表达式为假值。

 

这里关键是这个真值或者假值的运算过程中,我们可以使用上面介绍的","、"()"将一组表达式串起来执行。也就是说,这个表达式可能会很长很长,我甚至可以定义一个function在里面。这些表达式在执行过程中,有可以进行某些附加操作。比如我们希望这个表达式为真值的时候我们做什么,假值的时候做什么,把这些操作用"()"、","串起来作为一个整体运算。

于是就有了上面的复杂代码。

 

另外,大家注意:下面的写法是等价的:

 

if(a){
	b
}
//等价于
a&&(b)
//b可以是一个function调用表达式,或者是多个语句用","串起来
 

 

我们来看个实例吧。是上面例子的升级版。我们加入一个nameInput是否存在的判断:

 

jQuery(document).ready(function() {
	var nameValidate=function(){
		var value,nameInput=$("#name"),nameErrorTip=$("#nameErrorTip"),msg;
		msg=(value=nameInput.val(),value=="king")?(nameErrorTip.hide(),"对了,输入为king!"):(nameErrorTip.show(),"请输入king!");
		return (nameInput.length&&nameInput.val()&&nameErrorTip.length&&msg)||"没有找到name输入框或者输入框没有值!";
	};
	alert(nameValidate());
});
 

测试:

 

//html:<input value="king" id="myName"/>
//结果:弹出“没有找到name输入框或者输入框没有值!”

//<input value="king" id="name"/><div id="nameErrorTip">输入错误!</div>
//结果:弹出“对了,输入为king!”,nameErrorTip被隐藏
 

 

return表示中 nameInput.length&&nameInput.val()&&nameErrorTip.length&&msg会先运算 nameInput.length的值,如果length为0则表达式为假值,如果为1则为真值。val()操作也是如此,如果val()结果为""则表达式也是假值。几个表达式之间为&&运算,则表示依次运算几个表达式的值,如果都未真值则返回最后一个表达式的值,由于整个表达式与

"没有找到name输入框或者输入框没有值!"

表达式之间是||运算,所以前面的表达式其中一个表达式为假值则返回||右侧的表达式的值,也就是整个“没有找到name输入框或者输入框没有值!”字符串。

 

说到这里,我之前写过一篇文章专门说到了&&、||的真值、假值问题。有兴趣的可以去看看。http://my249645546.iteye.com/blog/1553202

 

 

 

谈了这些难以理解的运算符后,大家可能会觉得,这个javascript为什么要搞这些晦涩的运算符呢?

我的理解是因为javascript通常在客户端运行,那么从服务器端将js代码传输到客户端肯定需要耗时。上面的这些运算符都是为了减少代码量。再加上使用压缩工具去掉空格,替换变量名,就可以使用压缩率达到最好。

 

再这里,我也告诉大家,其实我也非常反对在实际应用中采用这种写法的,因为会对初学者造成阅读障碍。我写这篇文章的目的不是为了让大家以后就这样用,而是告诉大家可以这样用,在一些开源代码中遇到了能看懂。不可为了炫耀而故意写一些晦涩的代码。

 

最后,为了帮助我们更快的找到变量定义、理清代码整体结构,给大家推荐一个eclipse的js插件:Spket,支持jQuery代码提示哦!

 

 

 

2 楼 suene 前天  
是啊,其实很多人都没有很系统的学过js,(本人也是), 跟java语法差不太多,弱语言,这*的写起来一点都不弱啊...

刚接触js时开始看见 if(Object)  , 碉堡了.

顶一个.

看了这个才发现jQuery源代码不是这就是说晦涩

3 楼 white_crucifix 前天  
Extjs的源码有一部分可能还好读懂,因为ext的源码分两层,一层是像楼主说的底层的东西,另一层是上层一点的,其实和我们自己写的代码同性质的,是extjs帮我们实现好了的控件效果。
4 楼 xiarilian12 前天  
清楚多了,3Q
5 楼 svenzeng.bak 昨天  
var value,nameInput是声明语句。
return也是语句。。
6 楼 x87628 昨天  
有个疑问

最后的实例中,有这样的代码:
nameInput.length&&nameInput.val()&&nameErrorTip.length&&msg

如果nameInput.length为假值,会继续运算nameInput.val(),nameErrorTip.length以及msg么
7 楼 my249645546 昨天  
x87628 写道
有个疑问

最后的实例中,有这样的代码:
nameInput.length&&nameInput.val()&&nameErrorTip.length&&msg

如果nameInput.length为假值,会继续运算nameInput.val(),nameErrorTip.length以及msg么

不会。举个例子吧:
有语句
var result=nameInput.length&&nameInput.val()&&nameErrorTip.length&&msg;
如果nameInput.length等于0,为假值,几个表达式都是&&连接,那么一个为假值整个表达式都为假值,不需要再运算其他的表达式了。这时候result=0

var result=(nameInput.length&&nameInput.val()&&nameErrorTip.length)||"nameInput不存在";
如果nameInput.length等于0,为假值使得nameInput.length&&nameInput.val()&&nameErrorTip.length为假值;最后||了一个真值,所以最终结果为真值result="nameInput不存在

你可以自己写一个简单的 脚本测试一下
8 楼 crud0906 昨天  
好文章,收藏
9 楼 jxfsuda 昨天  
我现在的项目里面全部用 (fucntion(){})(); 这样的方式.省心
10 楼 sswh 昨天  
if(a){ 
    b 
}else{ 
    c 

//等价于 
(a&&(b))||c 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这两种写法是不等价的。 当a为true,b为false的时候,c一个会执行一个不会执行。
说实话,这种js的写法就是混乱的根源。完全是一种糟糕的代码风格。
11 楼 sswh 昨天  
return (nameInput.length&&nameInput.val()&&nameErrorTip.length&&msg)||"没有找到name输入框或者输入框没有值!"; 

~~~~~~~~~~~~~~~~~
这种写法除了炫耀作用外,没有一点价值,而且也是混乱的来源。
12 楼 sswh 昨天  
那么上面的(function(){})()中左侧定义function的()也是这个作用,将这个function给包裹起来,然后返回这个function。我们调用方法一般是a();那么(function(){})的作用就是返回这个function对象,然后(function(){})()右侧的()表示调用这个function。

~~~~~~~~~~~~~~~~~~
这个没有说清楚为什么需要在function前后添加括号的原因。
事实上是因为JS规范中表达式不能以function开头导致的。
类似的写法还有 +function(){}()    (function(){}())  也是可以的。

另外,如果目的是返回一个function,完全可以不使用括号。
var a = function(){return 22;}();
alert(a);
这个完全是可以的。
13 楼 my249645546 昨天  
sswh 写道
if(a){ 
    b 
}else{ 
    c 

//等价于 
(a&&(b))||c 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这两种写法是不等价的。 当a为true,b为false的时候,c一个会执行一个不会执行。
说实话,这种js的写法就是混乱的根源。完全是一种糟糕的代码风格。

你是对的 呵呵
14 楼 my249645546 昨天  
sswh 写道
return (nameInput.length&&nameInput.val()&&nameErrorTip.length&&msg)||"没有找到name输入框或者输入框没有值!"; 

~~~~~~~~~~~~~~~~~
这种写法除了炫耀作用外,没有一点价值,而且也是混乱的来源。

其实我也是反对在实际应用中采用这种写法的,因为会对初学者造成阅读障碍。我写这篇文章的目的不是为了让大家以后就这样用,而是告诉大家可以这样用,在一些开源代码中遇到了能看懂。
15 楼 surpass 昨天  
楼主写的不错
(function(){}){}
js模块化管理的开始。
16 楼 artair 昨天  
第一个解释起来非常的简单。
只要明白了,匿名函数,函数的执行。就够了!!!

分解出来就是

function niming = readydo() {...}; //定义一个函数

niming(); //执行它

如果把上面转成匿名函数方式:

先直接替换niming:

     readydo(){}();

看不清楚,那就加个括号吧

    (readydo(){})();

再把readydo变成匿名函数

    (function(){})();

ok!
17 楼 artair 昨天  
my249645546 写道
sswh 写道
return (nameInput.length&&nameInput.val()&&nameErrorTip.length&&msg)||"没有找到name输入框或者输入框没有值!"; 

~~~~~~~~~~~~~~~~~
这种写法除了炫耀作用外,没有一点价值,而且也是混乱的来源。

其实我也是反对在实际应用中采用这种写法的,因为会对初学者造成阅读障碍。我写这篇文章的目的不是为了让大家以后就这样用,而是告诉大家可以这样用,在一些开源代码中遇到了能看懂。


编译语言和解释语言有很大的差别,尽量减少行数,会提高执行效率,(尤其在以前)。对于许多老一代程序员来说,牺牲可读性,增加效率是很重要的,毕竟程序还仅运行在486,586上
18 楼 mfkvfn 昨天  
    var showName=function(){  
        var value,nameInput=$("#nameHide");  
        return nameInput.show(),value=nameInput.val();  
    }; 

写成
    var showName=function(){   
        return $("#nameHide").show().val();  
    }; 

就就行了。搞那么复杂干嘛
19 楼 elan1986 昨天  
看了这个才发现jQuery源代码不是这就是说晦涩
20 楼 竹林闲人 19 小时前  
LZ能否推荐下这方面的参考资料那~~,谢谢
21 楼 my249645546 7 小时前  
竹林闲人 写道
LZ能否推荐下这方面的参考资料那~~,谢谢

如果你想系统学习javascript的语言核心知识,那就看看《JavaScript权威指南》吧,讲得非常详细。
http://product.dangdang.com/product.aspx?product_id=22722790