jQuery是目前最紅的Javascript Library之一,這要歸功於他使用簡單,而且對於CSS Selector的支援很廣泛。研究jQuery核心的重點,首先就是他為何使用簡單,其次就是他怎麼包裝DOM Node及事件。
以下用jquery-1.3.2.js來做觀察
架構
jQuery只暴露給Global兩個變數,一個是jQuery,一個是$,這兩個其實是同一件東西...可以用簡單的程式檢測一下:
<script type="text/javascript" src="js/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
if ($ === jQuery) alert("true");
});
</script>
所以碰到這兩個名字就要留意一下。
接著就來追蹤一下程式。首先會注意到,所有的程式都包在一個匿名函數中,這是大部分library與framework常見的做法。
其實沒幾行就看到了關鍵(24~27行):
jQuery = window.jQuery = window.$ = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context );
},
從24行開始,就在window(也就是global)生成了jQuery及$兩個變數,內容都是一個函數。同時也把這個函數指派給匿名函數內的jQuery變數,以加快內部存取。(這樣就不必做context switch,前面var window = this以及undefined的意思也一樣)
透過這個函數可以知道,我們在呼叫$()時,他其實就只是用jQuery.fn.init()這個constructor new 一個instance回傳。接下來看看與這個constructor相關的結構(35~541行):
jQuery.fn = jQuery.prototype = {
init: function( selector, context ) {
......
},
selector: '',
......
};
// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;
init()是jQuery.fn的property,所以使用new jQuery.fn.init()時,這個物件內的this會指向jQuery.fn。在此同時又指派jQuery.fn.init.prototype的值為jQuery.fn,所以透過jQuery()或是$()使用他的property時,就會沿著prototype chain找到jQuery.fn上所有的properties。
當init()收到selector時會依據他是什麼東西來做一些處理。簡單地說,你可以傳入dom node、function或是CSS3 Selector字串,他最後會想辦法找到你指定的所有dom node,如果找不到,他就會使用window.document當作找到的dom node,然後把this做成類似陣列的物件把找到的dom node存起來,而this.length也會改成這個陣列的元素數目。所以只要是掛在jQuery.fn底下的函數,都可以透過this[n]取得找到的dom node。
延伸性
接下來看如何延伸jQuery就知道有多簡單了(562~610行)。
jQuery.extend = jQuery.fn.extend = function() {
// copy reference to target object
var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
target = arguments[1] || {};
// skip the boolean and the target
i = 2;
}
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction(target) )
target = {};
// extend jQuery itself if only one argument is passed
if ( length == i ) {
target = this;
--i;
}
for ( ; i < length; i++ )
// Only deal with non-null/undefined values
if ( (options = arguments[ i ]) != null )
// Extend the base object
for ( var name in options ) {
var src = target[ name ], copy = options[ name ];
// Prevent never-ending loop
if ( target === copy )
continue;
// Recurse if we're merging object values
if ( deep && copy && typeof copy === "object" && !copy.nodeType )
target[ name ] = jQuery.extend( deep,
// Never move original objects, clone them
src || ( copy.length != null ? [ ] : { } )
, copy );
// Don't bring in undefined values
else if ( copy !== undefined )
target[ name ] = copy;
}
// Return the modified object
return target;
};
不管deep copy的話(比較少用,除非你想要extend很複雜的物件中的東西給jQuery或jQuery.fn),他就是把你傳入的物件,依序把property copy給自己(看是透過jQuery.extend()還是jQuery.fn.extend())。透過jQuery.extend()執行時,就會延伸jQuery,透過jQuery.fn.extend()執行時,就會延伸jQuery.fn。由於在jQuery.fn底下就可以存取到$(...)找到的dom node,所以這就是製作plugin的標準方法。而jQuery底下的property與函數,則可以當作靜態的工具來用。
實驗
接下來就試寫一個簡單的plugin,功能只有把jquery找到的dom node的tagName秀出來。(範例使用jsbin.com來做,程式跟html分開,大家有興趣可以上jsbin.com看一下說明)測試網址:http://jsbin.com/ewufu
網頁部份:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<title>Sandbox</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<style type="text/css" media="screen">
body { background-color: #000; font: 16px Helvetica, Arial; color: #fff; }
</style>
<div id="panel">target panel</div>
<div id="test1" class="cls">test1.cls</div>
<div id="test2" class="cls">test2.cls</div>
<div id="test3" class="cls">test3.cls</div>
程式部份:
$.fn.extend({showTagName: function(){
var i=0;
for(;i<this.length;i++) {
alert(this[i].tagName);
}
return this;
}});
$('.cls').showTagName();
結束。
小結
jQuery的核心,使用一個非常精簡有容易使用的架構,讓開發者可以很容易擴充,而使用者也很容易使用。而jQuery的核心研發人員,只需要加速CSS Selector的速度,並且維護與各家瀏覽器的相容性,然後大家用起來就很高興了。核心的程式加上註解也只有四千多行,除了最核心的架構與功能,最大的部份就是他的selector引擎,另外一個比較大的部分則是處理event的功能。這些細節就留待後話了。
trace javascript 程式,一直是很大的困擾,有沒有什麼好方法呢?
雖然現在的開發工具已經支援 javascript 的 code insight,但是要 trace javascript 程式好像還是蠻困難的一件事情。
firefox、chrome跟IE都有debuger可以做trace,可以設中斷點,也可以在debuger中顯示值。
chrome的debuger還有附一個profiler,可以快速找到程式的瓶頸(他會顯示所有資源在瀏覽器中載入所花費的時間)。
IE的debuger需要另外下載,據說IE8的做的不錯,但是我沒用過。
阿,還沒確認就碰到submit按鈕...
我大概列一下我比較常用的工具,我之後的文章也會提到一些。
很多人稱讚IE8的developer tools,不過我沒用過,也許有時間可以來試試看。
我之後會寫文章介紹jsunit、selenium、fireunit還有John Resig最近開發的一套分散測試工具叫做testswarm。
^^b
鑑往知來研究甚麼是 ES6 ,2019年 六月已經到了 ES10,發現版主這個說明詳細的 ES5,串聯了瀏覽器、JavaScript 與 jQuery,十分詳細。
附上程式碼檢測,jQuery只暴露給Global兩個變數,一個是jQuery,一個是$,這兩個其實是同一件東西...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
if ($ === jQuery) alert("true");
});
</script>
</head>
<body>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sandbox</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<style type="text/css" media="screen">
body { background-color: #000; font: 16px Helvetica, Arial; color: #fff; }
</style>
</head>
<body>
<div id="panel">target panel</div>
<span id="test1" class="cls">test1.cls</span>
<div id="test2" class="cls">test2.cls</div>
<span id="test3" class="cls">test3.cls</span>
<script>
$.fn.extend({showTagName: function(){
var i=0;
for(;i<this.length;i++) {
alert(this[i].tagName);
}
return this;
}});
$('.cls').showTagName();
</script>
</body>
</html>
感謝,我都沒有再更新了XD
fillano 出現了,大師早安!
早安XD