[js高手之路] 設計模式系列課程 - jQuery的鏈式調用與靈活的構造函數


一、我們從一個簡單的構造函數+原型程序開始

1 var G = function(){};
2         G.prototype = {
3             length : 5,
4             size : function(){
5                 return this.length;
6             }
7         }

上例是個非常簡單的程序,如果需要調用,我們可以用new的方式

var oG = new G();
console.log( oG.size() ); //5
1、常見的錯誤調用方式1
console.log( G.size() ); //報錯
G.size這種調用,是把size當做靜態方法調用,如果需要正常的調用, 應該把size方法加在函數本身,如:
G.size = function(){
return 10;
}
2、常見的錯誤調用方式2
G().size() //報錯
G()返回的是undefined, undefined.size() 肯定是報錯, size是函數G原型對象上的方法,如果要確保這種方式調用正常,那么我們就要讓G() 返回一個G函數的實例
所以,我們可以這樣做:
1         var G = function () {
2             if( this instanceof G ) {
3                 return this;
4             }else {
5                 return new G();
6             }
7         };

把G的構造函數改造一下,判斷this是否是當前G函數的實例,如果是,直接返回,如果不是,返回new G()  這樣根據原型對象的查找原則,就能確保調用到size方法

完整的代碼:

 1         var G = function () {
 2             if( this instanceof G ) {
 3                 return this;
 4             }else {
 5                 return new G();
 6             }
 7         };
 8         G.prototype = {
 9             length: 5,
10             size: function () {
11                 return this.length;
12             }
13         }
14         console.log( G().size() );

在jquery框架中,他是怎么做的?

 1         var G = function () {
 2             return G.fn;
 3         };
 4         G.fn = G.prototype = {
 5             length: 5,
 6             size: function () {
 7                 return this.length;
 8             }
 9         }
10         console.log( G.prototype.size() ); //5
11         console.log( G().size() ); //5

在jquery中, 為函數G增加一個屬性fn( 記住:在js中函數是對象,這其實就是給對象增加了一個屬性 ),然后在G函數中返回 G.fn 就是就是返回G.prototype

那么用G().size 其實就是相當於調用 G.prototype.size()

二、在jquery中,這個構造函數一般是用來選擇元素的。

G返回的是G的原型對象,我們要增加選擇元素的功能,自然而然,就是往原型對象上添加:

 1         var G = function ( id ) {
 2             return G.fn.init( id );
 3         };
 4         G.fn = G.prototype = {
 5             init : function( id ){
 6                 return document.getElementById( id );
 7             },
 8             length: 5,
 9             size: function () {
10                 return this.length;
11             }
12         }

向G的原型對象上添加一個init方法, 然后在構造函數中調用

1         window.onload = function(){
2             console.log( G( 'box' ) );
3             G('box').style.backgroundColor = 'red';
4             // G('box').size(); //報錯,無法鏈式調用
5         }
6 
7     <div id="box">ghost wu tell you how to learn design pattern</div>

雖然通過init方法,能夠選擇到dom元素,但是不能實現鏈式調用, 因為G('box')返回的是一個dom對象, 而在dom對象上是沒有size這個方法的,因為size是G.prototype上的

所以要實現鏈式調用,就要確保init方法返回的是G的實例或者G.prototype,  這個時候,this就可以派上用場了

 1 <script>
 2         var G = function (id) {
 3             return G.fn.init(id);
 4         };
 5         G.fn = G.prototype = {
 6             init: function (id) {
 7                 this[0] = document.getElementById(id);
 8                 this.length = 1;
 9                 return this;
10             },
11             length: 0,
12             size: function () {
13                 return this.length;
14             }
15         }
16         window.onload = function () {
17             console.log(G('box'));
18             console.log( G('box2').size() );
19         }
20     </script>
1     <div id="box">ghost wu tell you how to learn design pattern</div>
2     <div id="box2">id為box2的第二個div</div>

把選擇到的元素放在this中, 這個時候的this指向的是G.fn,G.prototype?

因為在構造函數中,是這樣調用的: G.fn.init( id ), 我把G.fn標成紅色, 也就是相當於G.fn是一個對象,沒錯他確實就是一個對象G.prototype,所以在init中的this指向的就是

init方法前面的對象( G.fn, G.prototype ).

三、this覆蓋

接下來,就會產生一個問題, this共用之后,元素選擇就會產生覆蓋

 1 <script>
 2         var G = function (id) {
 3             return G.fn.init(id);
 4         };
 5         G.fn = G.prototype = {
 6             init: function (id) {
 7                 this[0] = document.getElementById(id);
 8                 this.length = 1;
 9                 console.log( this === G.fn, this === G.prototype, this );
10                 return this;
11             },
12             length: 0,
13             size: function () {
14                 return this.length;
15             }
16         }
17 
18         window.onload = function(){
19             console.log( G( 'box' ) );
20             console.log( G( 'box2' ) );
21         }
22     </script>
23 
24     <div id="box">ghost wu tell you how to learn design pattern</div>
25     <div id="box2">id為box2的第二個div</div>

調用兩次構造函數G 去獲取元素的時候, this[0] 現在都指向了 id為box2的元素, 把第一次G('box')選擇到的id為box的元素覆蓋了,產生覆蓋的原因是this共用,那么我們

可以通過什么方法把this分開呢?不同的this指向不同的實例? 用什么? 恩,對了, 用new,每次new一個構造函數就會生成新的實例

四、解決this覆蓋與鏈式調用

 1     <script>
 2         var G = function (id) {
 3             return new G.fn.init(id);
 4         };
 5         G.fn = G.prototype = {
 6             init: function (id) {
 7                 this[0] = document.getElementById(id);
 8                 this.length = 1;
 9                 return this;
10             },
11             length: 0,
12             size: function () {
13                 return this.length;
14             }
15         }
16         window.onload = function(){
17             console.log( G( 'box' ) );
18             console.log( G( 'box2' ) );
19         }
20     </script>
21     <div id="box">ghost wu tell you how to learn design pattern</div>
22     <div id="box2">id為box2的第二個div</div>

通過構造函數中new G.fn.init( id ) 的方式,每次生成一個新的實例,但是產生了一個新的問題,不能鏈式調用, 因為init中的this發生了改變,不再指向( G.fn, G.prototype ).

G('box2').size();//報錯,
為了能夠調用到size(), 所以在執行完構造函數之后,我們要確保this指向G的實例,或者G的原型對象,
只需要把init函數的原型對象指向G.fn就可以了
 1         var G = function (id) {
 2             return new G.fn.init(id);
 3         };
 4         G.fn = G.prototype = {
 5             init: function (id) {
 6                 this[0] = document.getElementById(id);
 7                 this.length = 1;
 8                 return this;
 9             },
10             length: 0,
11             size: function () {
12                 return this.length;
13             }
14         }
15         G.fn.init.prototype = G.fn;

加上G.fn.init.prototype = G.fn;我們就修正了this的覆蓋與鏈式調用問題

 五、擴展選擇器

上面支持id選擇器,我們只需要在init函數加上其他類型的擴展就可以了,比如,我這里增加了一個標簽選擇器

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 
 4 <head>
 5     <meta charset="UTF-8">
 6     <meta name="viewport" content="width=device-width, initial-scale=1.0">
 7     <meta http-equiv="X-UA-Compatible" content="ie=edge">
 8     <title>Document</title>
 9     <style>
10         div,p {
11             border:1px solid red;
12             margin: 10px;
13             padding: 10px;
14         }
15     </style>
16     <script>
17         var G = function ( selector, context ) {
18             return new G.fn.init( selector, context );
19         };
20         G.fn = G.prototype = {
21             constructor : G,
22             init: function ( selector, context ) {
23                 this.length = 0;
24                 context = context || document;
25                 if ( selector.indexOf( '#' ) == 0 ) {
26                     this[0] = document.getElementById( selector.substring( 1 ) );
27                     this.length = 1;
28                 }else {
29                     var aNode = context.getElementsByTagName( selector );
30                     for( var i = 0, len = aNode.length; i < len; i++ ){
31                         this[i] = aNode[i];
32                     }
33                     this.length = len;
34                 }
35                 this.selector = selector;
36                 this.context = context;
37                 return this;
38             },
39             length: 0,
40             size: function () {
41                 return this.length;
42             }
43         }
44         G.fn.init.prototype = G.fn;
45 
46         window.onload = function(){
47             console.log( G('#box')[0] );
48             var aP = G('p', G('#box')[0]);
49             // var aP = G('p');
50             // var aP = G('#p1');
51             for( var i = 0, len = aP.size(); i < len; i++ ){
52                 aP[i].style.backgroundColor = 'blue';
53             }
54         }
55     </script>
56 </head>
57 
58 <body>
59     <div id="box">
60         <p>跟着ghostwu學習設計模式</p>
61         <p>跟着ghostwu學習設計模式</p>
62         <p>跟着ghostwu學習設計模式</p>
63         <p>跟着ghostwu學習設計模式</p>
64     </div>
65     <p id="p1">跟着ghostwu學習設計模式</p>
66     <p>跟着ghostwu學習設計模式</p>
67 </body>
68 
69 </html>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM