前言
鏈模式相信大家很常見,尤其在jq中,我們非常習慣的可以使用jq的鏈式模式中連續的進行方法的呼叫,雖然我們知道其在方法執行後會返回this的當前物件,來實現這個模式的設計,我們還是進行更加詳細的研究學習吧。
基於原型的繼承
首先我們知道的是鏈模式是基於原型,比如我們在假設我們的方法為A。我們現在的方法和屬性都是定義在原型鏈上,a本身是方法,我們通過例項化方法可以訪問到原型鏈上的方法。
function A (){
}A.prototype = {
length:2, size:function(){
return this.length
}
}let a = new A()console.log(a.size()) //2// 沒有size 是因為方法定義在原型鏈上console.log(A.size())// 沒有size執行是因為a方法的執行沒有任何返回值console.log(A().size())複製程式碼
但是這樣是有問題的,我們知道jq的方式是直接用返回物件並沒有例項化的方法,那我們可以進行藉助另一個物件來實現。
藉助助手
我們可以藉助另外的物件進行,上面講到A().size()不行是因為a方法沒有任何任何值,那麼我們可以進行返回。如下的方式改造就可以直接a方法呼叫函式了。
function A (){
return B
}let B = A.prototype = {
length:2, size:function(){
return this.length
}
}console.log(A().size()) //2// 為了減少變數的建立,我們直接用A的屬性來承接A.fn = A.prototype = {
length:2, size:function(){
return this.length
}
}複製程式碼
獲取元素
我們知道jq最終是為了獲取元素的集合,所以目前的方式肯定是不合適的。所以我們需要在原型物件上定義一個init方法來獲取物件。那麼我們需要在A方法執行的時候直接返回我們的初始化方法。
function A (seletor){
return A.fn.init(seletor)
}A.fn = A.prototype = {
init:function(seletor){
return document.getElementById(seletor)
}, length:2, size:function(){
return this.length
}
}console.log(A("demo")) // dom 複製程式碼
返回我們需要的方法
雖然之前的設計可以得到元素了,但這樣的元素不具有我們想要的方法,那麼如何讓返回的元素具有我們想要的方法呢,在方法體重我們可以看到A.fn是具有我們的方法的。
function A (seletor){
return A.fn.init(seletor)
}A.fn = A.prototype = {
init:function(seletor){
this[0] = document.getElementById(seletor) this.length = 1 return this
}, length:2, size:function(){
return this.length
}
}console.log(A("demo").size()) // 1 得到校驗後的長度複製程式碼
此時發現另外的問題了,就是我們進行另外一個元素的使用時,元素會互相覆蓋,原因是因為我們每次都是返回利用的同一個物件。
覆蓋獲取
解決覆蓋獲取的方式也很簡單,恢復例項化即可。但這樣也會導致方法無法使用。報錯如下:A(...).size is not a function
function A (seletor){
return new A.fn.init(seletor)
}複製程式碼
方法丟失
原因是因為前者返回的是當前物件,也就是A.fn和A.prototype,但如果是new的進行的是屬性的複製,與前面不同。
經過測試,init方法中第一種方式返回的確實是a.fn,a.prototype,而如果是返回new a.fn.init,那麼返回的是a.fn.a.init
jq的解決方案
直接將a.fn的原型指向存在的物件即可。
A.fn.init.prototype = A.fn複製程式碼
這樣就可以滿足我們的基本需求了。
豐富的元素獲取
上面的程式碼可以實現獲取id元素的標籤,但實際上我們有各種可能,這種時候只要判斷選擇器的內容,然後針對性的獲取元素就好。
init:function(selector,context){
this.length = 0 context = context || document if(~selector.indexOf("#")>
-1){
this[0] = document.getElementById(selector.slice(1)) this.length = 1
} ...
}複製程式碼
陣列與物件
雖然我們實現了基本的方法,但發現仍然存在一個使用上的特性,就是jq的元素支援陣列的很多操作,為了讓我們的返回物件也具有類似的功能,我們需要在原型鏈上追加push\sort\split等類似陣列的方法來實現。
A.fn = A.prototype = {
// 陣列方法新增 增強陣列特性 push:[].push, sort:[].sort, splice:[].splice
}複製程式碼
方法擴充extend
A.extend = A.fn.extend = function(){
var i = 1,len = arguments.length, target = arguments[0] if(i === len){
target = this ;
i--
} for(;
i<
len;
i++){
for(j in arguments[i]){
target[j] = arguments[i][j]
}
} return target ;
}複製程式碼
新增方法,返回this
所以如果你要新增方法,你直接用就可以。
A.fn.extend = ({
html:function(){
var arg = arguments,len = arguments.length;
if(len === 0){
return this[0] &
&
this[0].innerHtml;
} else {
for(var i = this.length -1 ;
i >
=0 ;
i-- ){
this[i].innerHtml = arg[0]
}
} // 返回this 支援鏈式呼叫 return this
}
})複製程式碼