第二章 jQuery技術解密 (二)
2.2.6 延續 -- 迭代器
在 jQuery 框架中,jQuery 物件是一個很奇怪的概念,具有多重身份,所以很多初學者一聽說 jQuery 物件就感覺很是不解,誤以為它是 John Resig 製造的新概念。我們可以對jQuery 物件進行如下分解。
第一,jQuery 物件是一個資料集合,它不是一個個體物件。因此,你無法直接使用 JavaScript 的方法來操作它。
第二,jQuery 物件實際上就是一個普通的物件,因為它是通過 new 運算子建立的一個新的例項物件。它可以繼承原型方法或屬性,同時也擁有 Object 型別的方法和屬性。
第三,jQuery 物件包含陣列特性,因為它賦值了陣列元素,以陣列結構儲存返回的資料。我們可以以 JavaScript 的概念理解 jQuery 物件,例如下面的示例。
- <scripttype="text/javascript">
- varjquery={//定義物件直接量
- name:"jQuery",//以屬性方式儲存資訊
- value:"1.3.2"
- };
- jquery[0]="jQuery";//以陣列方式儲存資訊
- jquery[1]="1.3.2";
- alert(jquery.name);//返回"jQuery"
- alert(jquery[0]);//返回"jQuery"
- </script>
第四,jQuery 物件包含的資料都是 DOM 元素,是通過陣列形式儲存的,即通過 jQuery[n] 形式獲取。同時 jQuery 物件又定義了幾個模仿 Array 基本特性的屬性,如 length 等。
所以,jQuery 物件是不允許直接操作的,只有分別讀取它包含的每一個 DOM 元素,才能實現各種操作,如插入、刪除、巢狀、賦值和讀寫 DOM 元素屬性等。
那麼如何實現直接操作 jQuery 物件中的 DOM 元素呢?
在實際應用中,我們可以看到類似下面的 jQuery 用法。
$("div").html()
也就是直接在 jQuery 物件上呼叫 html(),並實現操作 jQuery 包含的所有 DOM 元素。那麼這個功能是怎麼實現的呢?
jQuery 定義了一個工具函式 each(),利用這個工具可以遍歷 jQuery 物件中所有的 DOM 元素,並把需要操作的內容封裝到一個回撥函式中,然後通過在每個 DOM 元素上呼叫這個回撥函式即可。實現程式碼如下所示,演示效果如圖 2.2 所示。
- <scripttype="text/javascript">
- var$=jQuery=function(selector,context){//定義類
- returnnewjQuery.fn.init(selector,context);//返回選擇器的例項
- };
- jQuery.fn=jQuery.prototype={//jQuery類的原型物件
- init:function(selector,context){//定義選擇器構造器
- selector=selector||document;//設定預設值為document
- context=context||document;//設定預設值為document
- if(selector.nodeType){//如果選擇符為節點物件
- this[0]=selector;//把引數節點傳遞給例項物件的陣列
- this.length=1;//並設定例項物件的length屬性,定義包含的元素個數
- this.context=selector;//設定例項的屬性,返回選擇範圍
- returnthis;//返回當前例項
- }
- if(typeofselector==="string"){//如果選擇符是字串
- vare=context.getElementsByTagName(selector);//獲取指定名稱的元素
- for(vari=0;i<e.length;i++){//遍歷元素集合,並把所有元素填入到當前例項陣列中
- this[i]=e[i];
- }
- this.length=e.length;//設定例項的length屬性,即定義包含的元素個數
- this.context=context;//設定例項的屬性,返回選擇範圍
- returnthis;//返回當前例項
- }else{
- this.length=0;//否則,設定例項的length屬性值為0
- this.context=context;//設定例項的屬性,返回選擇範圍
- returnthis;//返回當前例項
- }
- },
- html:function(val){//模仿jQuery框架中的html()方法,為匹配的每一個DOM元素插入html程式碼
- jQuery.each(this,function(val){//呼叫jQuery.each()工具函式,為每一個DOM元素執行回撥函式
- this.innerHTML=val;
- },val);
- },
- jquery:"1.3.2",//原型屬性
- size:function(){//原型方法
- returnthis.length;
- }
- };
- jQuery.fn.init.prototype=jQuery.fn;//使用jQuery的原型物件覆蓋init的原型物件
- //擴充套件jQuery工具函式
- jQuery.each=function(object,callback,args){
- for(vari=0;i<object.length;i++){
- callback.call(object[i],args);
- }
- returnobject;
- };
- $("div").html("測試程式碼");
- </script>
注意,在上面的程式碼中,each() 函式的當前作用物件是 jQuery 物件,故 this 指向當前 jQuery 物件,即 this 表示一個集合物件;而在 html() 方法中,由於 each() 函式是在指定 DOM 元素上執行的,所以該函式內的 this 指標指向的是當前 DOM 元素物件,即 this 表示一個元素。
2.2.7 延續 -- 功能擴充套件
根據一般設計習慣,如果要為 jQuery 或者 jQuery.prototype 新增函式或方法,可以直接通過點語法實現,或者在 jQuery.prototype 物件結構中增加一個屬性即可。但是,如果分析 jQuery 框架的原始碼,你會發現它是通過 extend() 函式來實現功能擴充套件的。例如,下面兩段程式碼都是 jQuery 框架通過 extend() 函式來擴充套件功能的。
jQuery.extend({ // 擴充套件工具函式
noConflict: function(deep){},
isFunction: function(obj){},
isArray: function(obj){},
isXMLDoc: function(elem){},
globalEval: function(data){}
});
或者
jQuery.fn.extend({ // 擴充套件 jQuery 物件方法
show: function(speed, callback){},
hide: function(speed, callback){},
toggle: function(fn, fn2){},
fadeTo: function(speed, to, callback){},
animate: function(prop, speed, easing, callback){},
stop: function(clearQueue, gotoEnd){}
});
這樣做的好處是什麼呢?
extend() 函式能夠方便使用者快速擴充套件 jQuery 框架的功能,但是不會破壞 jQuery 框架的原型結構,從而避免後期人工手動新增工具函式或者方法破壞 jQuery 框架的單純性,同時也方便管理。如果不需要某個外掛,只需要簡單地刪除即可,而不需要在 jQuery 框架原始碼中去篩選和刪除。
extend() 函式的功能實現起來也很簡單,它只是把指定物件的方法複製給 jQuery 物件或者 jQuery.prototype 物件。例如,在下面的示例中,我們為 jQuery 類和原型定義了一個擴充套件功能的函式 extend() ,該函式的功能很簡單,它能夠把指定引數物件包含的所有屬性複製給 jQuery 或者 jQuery.prototype 物件,這樣就可以在應用中隨時呼叫它,並動態擴充套件 jQuery 物件的方法。
- <div></div>
- <div></div>
- <div></div>
- <scripttype="text/javascript">
- var$=jQuery=function(selector,context){//定義類
- returnnewjQuery.fn.init(selector,context);//返回選擇器的例項
- };
- jQuery.fn=jQuery.prototype={//jQuery類的原型物件
- init:function(selector,context){//定義選擇器構造器
- selector=selector||document;//設定預設值為document
- context=context||document;//設定預設值為document
- if(selector.nodeType){//如果選擇符為節點物件
- this[0]=selector;//把引數節點傳遞給例項物件的陣列
- this.length=1;//並設定例項物件的length屬性,定義包含的元素個數
- this.context=selector;//設定例項的屬性,返回選擇範圍
- returnthis;//返回當前例項
- }
- if(typeofselector==="string"){//如果選擇符是字串
- vare=context.getElementsByTagName(selector);//獲取指定名稱的元素
- for(vari=0;i<e.length;i++){//遍歷元素集合,並把所有元素填入到當前例項陣列中
- this[i]=e[i];
- }
- this.length=e.length;//設定例項的length屬性,即定義包含的元素個數
- this.context=context;//設定例項的屬性,返回選擇範圍
- returnthis;//返回當前例項
- }else{
- this.length=0;//否則,設定例項的length屬性值為0
- this.context=context;//設定例項的屬性,返回選擇範圍
- returnthis;//返回當前例項
- }
- },
- jquery:"1.3.2",//原型屬性
- size:function(){//原型方法
- returnthis.length;
- }
- };
- jQuery.fn.init.prototype=jQuery.fn;//使用jQuery的原型物件覆蓋init的原型物件
- //jQuery功能擴充套件函式
- jQuery.extend=jQuery.fn.extend=function(obj){
- for(varpropinobj){
- this[prop]=obj[prop];
- }
- returnthis;
- };
- //擴充套件jQuery物件方法
- jQuery.fn.extend({
- test:function(){
- alert("測試擴充套件功能");
- }
- });
- //測試程式碼
- $("div").test();
- </script>
在上面的示例中,先定義了一個功能擴充套件函式 extend(),然後為 jQuery.fn 原型物件呼叫 extend() 函式,為其新增一個測試方法 test()。這樣就可以在實踐中應用,如 $("div").test() 。
jQuery 框架定義的 extend() 函式的功能要強大很多,它不僅能夠完成基本的功能擴充套件,還可以實現物件合併等功能。
2.2.8 延續 -- 引數處理
在很多時候,你會發現 jQuery 的方法都要求傳遞的引數為物件結構,例如:
$.ajax({
type: "GET",
url: "test.js",
dataType: "script"
});
使用物件直接量作為引數進行傳遞,方便引數管理。當方法或者函式的引數長度不固定時,使用物件直接量作為引數存在很多優勢。例如,對於下面的用法,ajax()函式就需要進行更加複雜的引數排查和過濾。
$.ajax("GET", "test.js", "script");
如果 ajax() 函式的引數長度是固定的,且是必須的,那麼通過這種方式進行傳遞也就無所謂了,但是如果引數的個數和排序是動態的,那麼使用 $.ajax("GET", "test.js", "script"); 這種方法是無法處理的。而 jQuery 框架的很多方法都包含大量的引數,且都是可選的,位置也沒有固定要求,所以使用物件直接量是惟一的解決方法。
使用物件直接量作為引數傳遞的載體,這裡就涉及引數處理問題。如何解析並提出引數?如何處理引數和預設值?我們可以通過下面的方法來實現。
- <scripttype="text/javascript">
- var$=jQuery=function(selector,context){//定義類
- returnnewjQuery.fn.init(selector,context);//返回選擇器的例項
- };
- jQuery.fn=jQuery.prototype={//jQuery類的原型物件
- init:function(selector,context){//定義選擇器構造器
- selector=selector||document;//設定預設值為document
- context=context||document;//設定預設值為document
- if(selector.nodeType){//如果選擇符為節點物件
- this[0]=selector;//把引數節點傳遞給例項物件的陣列
- this.length=1;//並設定例項物件的length屬性,定義包含的元素個數
- this.context=selector;//設定例項的屬性,返回選擇範圍
- returnthis;//返回當前例項
- }
- if(typeofselector==="string"){//如果選擇符是字串
- vare=context.getElementsByTagName(selector);//獲取指定名稱的元素
- for(vari=0;i<e.length;i++){//遍歷元素集合,並把所有元素填入到當前例項陣列中
- this[i]=e[i];
- }
- this.length=e.length;//設定例項的length屬性,即定義包含的元素個數
- this.context=context;//設定例項的屬性,返回選擇範圍
- returnthis;//返回當前例項
- }else{
- this.length=0;//否則,設定例項的length屬性值為0
- this.context=context;//設定例項的屬性,返回選擇範圍
- returnthis;//返回當前例項
- }
- },
- setOptions:function(options){
- this.options={//方法的預設值,可以擴充套件
- StartColor:"#000",
- EndColor:"#DDC",
- Step:20,
- Speed:10
- };
- jQuery.extend(this.options,options||{});//如果傳遞引數,則覆蓋原預設引數
- },
- jquery:"1.3.2",//原型屬性
- size:function(){//原型方法
- returnthis.length;
- }
- };
- jQuery.fn.init.prototype=jQuery.fn;//使用jQuery的原型物件覆蓋init的原型物件
- jQuery.extend=jQuery.fn.extend=function(destination,source){//重新定義extend()函式
- for(varpropertyinsource){
- destination[property]=source[property];
- }
- returndestination;
- };
- </script>
在上面的示例中,定義了一個原型方法 setOptions(),該方法能夠對傳遞的引數物件進行處理,並覆蓋預設值。這種用法在本書外掛部分還將進行講解。
在 jQuery 框架中, extend() 函式包含了所有功能,它既能夠為當前物件擴充套件方法,也能夠處理引數物件,並覆蓋預設值。
2.2.9 涅槃 -- 名字空間
現在,我們終於模擬出了 jQuery 框架的雛形,雖然它還比較稚嫩,經不起風雨,但至少能夠保證讀者理解 jQuery 框架構成的初期狀態。不過對於一個成熟的框架來說,需要設計者考慮的問題還是很多的,其中最核心的問題就是名字空間衝突問題。
當一個頁面中存在多個框架,或者自己寫了很多 JavaScript 程式碼,我們是很難確保這些程式碼不發生衝突的,因為任何人都無法確保自己非常熟悉 jQuery 框架中的每一行程式碼,所以難免會出現名字衝突,或者功能覆蓋現象。為了解決這個問題,我們必須把 jQuery 封裝在一個孤立的環境中,避免其他程式碼的干擾。
在詳細講解名字空間之前,我們先來溫習兩個 JavaScript 概念。首先,請看下面的程式碼。
var jQuery = function(){};
jQuery = function(){};
上面所示的程式碼是兩種不同的寫法,且都是合法的,但是它們的語義完全不同。第一行程式碼宣告瞭一個變數,而第二行程式碼定義了 Window 物件的一個屬性,也就是說它等同於下面的語句。
window.jQuery = function();
在全域性作用域中,變數和 Window 物件的屬性是可以相等的,也可以是互通的,但是當在其他環境中 (如區域性作用域中),則它們是不相等的,也是無法互通的。
因此如果希望 jQuery 具有類似 $.method(); 呼叫方式的能力,就需要將 jQuery 設定為 Window 物件的一個屬性,所以你就會看到 jQuery 框架中是這樣定義的。
- <scripttype="text/javascript">
- varjQuery=window.jQuery=window.$=function(selector,context){
- returnnewjQuery.fn.init(selector,context);
- };
- </script>
(function(){
alert("觀察我什麼時候出現");
})();
這是一個典型的匿名函式基本形式。為什麼要用到匿名函式呢?
這時就要進入正題了,如果希望自己的 jQuery 框架與其他任何程式碼完全隔離開來,也就是說如果你想把 jQuery 裝在一個封閉空間中,不希望暴露內部資訊,也不希望別的程式碼隨意訪問,匿名函式就是一種最好的封閉方式。此時我們只需要提供介面,就可以方便地與外界進行聯絡。例如,在下面的示例中分別把 f1 函式放在一個匿名函式中,而把 f2 函式放在全域性作用域中。可以發現,全域性作用域中的 f2 函式可以允許訪問,而匿名函式中的 f1 函式是禁止外界訪問的。
- <scripttype="text/javascript">
- (function(){
- functionf1(){
- return"f1()";
- }
- })();
- functionf2(){
- return"f2()";
- }
- alert(f2());//返回"f2()"
- alert(f1());//丟擲異常,禁止訪問
- </script>
當然,$ 和 jQuery 名字並非是 jQuery 框架的專利,其他一些經典框架中也會用到 $ 名字,也許讀者也會定義自己的變數 jQuery 。
在這之前我們需要讓它與其他框架協同工作,這就帶來一個問題,如果我們都使用 $ 作為簡寫形式就會發生衝突,為此 jQuery 提供了一個 noConflit() 方法,該方法能夠實現禁止 jQuery 框架使用這兩個名字。為了實現這樣的目的,jQuery 在框架的最前面,先使用 _$ 和 _jQuery 臨時變數寄存 $ 和 jQuery 這兩個變數的內容,當需要禁用 jQuery 框架的名字時,可以使用一個臨時變數 _$ 和 _jQuery 恢復 $ 和 jQuery 這兩個變數的實際內容。實現程式碼如下。
- <scripttype="text/javascript">
- (function(){
- var
- window=this,
- undefined,
- _jQuery=window.jQuery,//暫存jQuery變數內容
- _$=window.$,//暫存$變數內容
- jQuery=window.jQuery=window.$=function(selector,context){
- returnnewjQuery.fn.init(selector,context);
- },
- quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,
- isSimple=/^.[^:#\[\.,]*$/;
- jQuery.fn=jQuery.prototype={
- init:function(selector,context){//定義選擇器構造器
- selector=selector||document;//設定預設值為document
- context=context||document;//設定預設值為document
- if(selector.nodeType){//如果選擇符為節點物件
- this[0]=selector;//把引數節點傳遞給例項物件的陣列
- this.length=1;//並設定例項物件的length屬性,定義包含的元素個數
- this.context=selector;//設定例項的屬性,返回選擇範圍
- returnthis;//返回當前例項
- }
- if(typeofselector==="string"){//如果選擇符是字串
- vare=context.getElementsByTagName(selector);//獲取指定名稱的元素
- for(vari=0;i<e.length;i++){//遍歷元素集合,並把所有元素填入到當前例項陣列中
- this[i]=e[i];
- }
- this.length=e.length;//設定例項的length屬性,即定義包含的元素個數
- this.context=context;//設定例項的屬性,返回選擇範圍
- returnthis;//返回當前例項
- }else{
- this.length=0;//否則,設定例項的length屬性值為0
- this.context=context;//設定例項的屬性,返回選擇範圍
- returnthis;//返回當前例項
- }
- },
- setOptions:function(options){
- this.options={//方法的預設值,可以擴充套件
- StartColor:"#000",
- EndColor:"#DDC",
- Step:20,
- Speed:10
- };
- jQuery.extend(this.options,options||{});//如果傳遞引數,則覆蓋原預設引數
- },
- jquery:"1.3.2",//原型屬性
- size:function(){//原型方法
- returnthis.length;
- }
- };
- jQuery.fn.init.prototype=jQuery.fn;//使用jQuery的原型物件覆蓋init的原型物件
- jQuery.extend=jQuery.fn.extend=function(destination,source){//重新定義extend()函式
- for(varpropertyinsource){
- destination[property]=source[property];
- }
- returndestination;
- };
- })();
- </script>
相關文章
- 第二章 jQuery技術解密(一)jQuery解密
- 第二章 jQuery技術解密 (四)jQuery解密
- 第二章 jQuery技術解密 (三)jQuery解密
- 第二章 jQuery技術解密 (五)jQuery解密
- 第二章 jQuery技術解密 (六)jQuery解密
- 第二章 jQuery技術解密 (七)jQuery解密
- 精通struts技術第二章(4) (轉)
- 精通struts技術第二章(5) (轉)
- jQuery第二章知識點jQuery
- jQuery第二章選擇器jQuery
- 第二章投資技術《第五節 背離》
- jQuery第二章課後作業jQuery
- 第二章投資技術《第三節 K線秘籍》
- 騰訊萬億級 Elasticsearch 技術解密Elasticsearch解密
- ChatGPT軟體技術棧解密ChatGPT解密
- Oracle Flashback 技術大解密Oracle解密
- 解密數倉的SQL ON ANYWHERE技術解密SQL
- C#語言和資料庫技術基礎 ----------------- 第二章 簡答三 、C#資料庫
- OceanBase-【OBCP】認證-第二章 OB 儲存引擎高階技術儲存引擎
- 社交軟體紅包技術解密(十二):解密抖音春節紅包背後的技術設計與實踐解密
- 第二章
- 有獎書評活動:《京東技術解密》解密
- 讀《JavaScript核心技術開發解密》筆記JavaScript解密筆記
- 解密阿里巴巴安全技術體系解密阿里
- 【技術解密】SequoiaDB分散式儲存原理解密分散式
- [解密] DNA儲存技術究竟牛在哪裡?解密
- [原創]京東技術解密讀書筆記解密筆記
- 第二章 - 程式
- [web安全]黑客攻防技術寶典-瀏覽器實戰篇--第二章習題答案Web黑客瀏覽器
- 讀《圖解密碼技術》(三):金鑰、隨機數和應用技術圖解解密密碼隨機
- vivo營銷自動化技術解密|開篇解密
- 解密|一文帶你看懂外掛技術解密
- 網際網路面試知識技術總結(javacore、jvm、redis、kafka、zookeeper、mysql、hystrix)-第二章面試JavaJVMRedisKafkaMySql
- 第二章 程式管理
- 容器技術之Dockerfile(二)Docker
- javascript之DOM技術(二)JavaScript
- docker技術總結(二)Docker
- 地球上最大的PHP站點後端技術解密PHP後端解密