前言

链模式相信大家很常见,尤其在 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
 }
})
复制代码

感谢    赞同    分享    收藏    关注    反对    举报    ...