美文网首页
链模式讲解

链模式讲解

作者: Splendid飞羽 | 来源:发表于2021-05-20 23:07 被阅读0次

链模式
通常情况下,通过对构造函数使用 new 会返回一个绑定到 this 上的新实例,所以我们可以在 new 出来的对象上直接用 . 访问其属性和方法。如果在普通函数中也返回当前实例,那么我们就可以使用 . 在单行代码中一次性连续调用多个方法,就好像它们被链接在一起一样,这就是链式调用,又称链模式。

之前建造者模式、组合模式等文章已经用到了链模式,日常使用的 jQuery、Promise 等也使用了链模式,我们对使用形式已经很熟悉了,下面一起来看看链模式的原理。

  1. 什么是链模式 1.1 链模式的实现

在 jQuery 时代,下面这样的用法我们很熟悉了:

// 使用链模式
$('div')
  .show()
  .addClass('active')
  .height('100px')
  .css('color', 'red')
  .on('click', function(e) {
      // ... 
  })

源码中的链模式 3.1 jQuery 中的链模式 1. jQuery 构造函数

jQuery 方法看似复杂,可以简写如下:


var jQuery = function(selector, context) {
   // jQuery 方法返回的是 jQuery.fn.init 所 new 出来的对象
   return new jQuery.fn.init(selector, context, rootjQuery)
}

jQuery.fn = jQuery.prototype = {
   constructor: jQuery,
   // jQuery 对象的构造函数
   init: function(selector, context, rootjQuery) {
       // ... 一顿匹配操作,返回一个拼装好的伪数组的自身实例
       // 是 jQuery.fn.init 的实例,也就是我们常用的 jQuery 对象
       return this
   },
   selector: '',
   eq: function() { ... },
   end: function() { ... },
   map: function() { ... },
   last: function() { ... },
   first: function() { ... },
   // ... 其他方法
}

jQuery.fn.init 的实例都拥有 jQuery.fn 相应的方法jQuery.fn.init.prototype = jQuery.fn
// 此处源码位于 src/core.jsreturn new jQuery.fn.init(...) 这句看似复杂,其实也就是下面的这个 init 方法,
这个方法最后返回的是我们常用的 jQuery 对象,下面还有一句 jQuery.fn.init.prototype = jQuery.fn,
因此最上面的 jQuery 方法返回的 new 出来的 jQuery.fn.init 实例将继承 jQuery.fn` 上的方法:

const p = $("<p/>")
$.fn === p.__proto__   // true

因此返回出来的实例也将继承 eq、end、map、last 等 jQuery.fn 上的方法。

1、jQuery 实例方法

下面我们一起看看,show、hide、toggle 这些方法是如何实现链模式的呢 :

jQuery.fn.extend({
   show: function() {
       var elem
       
       for (i = 0; i < this.length; i++) {
           // ... 
           elem = this[i]
           
           if (elem.style.display === 'none') {
               elem.style.display = 'block'
           }
       }

       return this
   },
   hide: function() { ... },
   toggle: function() { ... }
})
这里首先使用了一个方法 jQuery.fn.extend(),简单看一下这个方法做啥的:

jQuery.extend = jQuery.fn.extend = function(options) {
   // ... 一系列啰啰嗦嗦的判断
 
   for (name in options) {
       this[name] = options[ name ]  // 此处 this === jQuery.fn
   }
}

// 此处源码位于 src/core.js

这个方法就是把传参的对象的值赋值给 jQuery.fn,因为这时候这个方法是通过上下文对象 jQuery.fn.extend()方式来调用,属于隐式绑定。(对 this 绑定规则的同学参看本专栏第 2 篇文章)
show 方法为例,此时这个方法被赋到 jQuery.fn 对象上,而通过上文我们知道,jQuery.fn.init.prototype = jQuery.fn,而jQuery.fn.init这个方法是作为构造函数被 jQuery 函数 new 出来并返回,因此 show 方法此时可以被jQuery.fn.init实例访问到,也就可以被$('selector')访问到,因此此时我们已经可以: $('p').show()了。
那么我们再回头来看看show方法的实现,show方法将匹配的元素的 display 置为 block之后返回了 this。注意了,此时的 this也是隐式绑定,而且是通过$('p')点出来的,因此返回的值就是$('p') 的引用。
经过以上步骤,我们知道 show方法返回的仍然是 $('p') 的引用,我们可以继续在之后点出来其他 jQuery.fn对象上的方法,css、hide、toggle、addClasson等等方法同理,至此,jQuery 的链模式就形成了。

2、Underscore 中的链模式

如果你用过 Underscore,那么你可能知道 Underscore 提供的一个链模式实现 _.chain。通过这个方法,可以方便地使用Underscore提供的一些方法链模式地对数据进行处理。另外,Lodashchain 实现和 Underscore 的基本一样,可以自行去LodashGitHub仓库阅读。
比如这里我们需要对一个用户对象数组进行一系列操作,首先按年龄排序,去掉年龄为奇数的人,再将这些用户的名字列成数组:

var users = [
    { 'name': 'barney', 'age': 26 },
    { 'name': 'fred', 'age': 21 },
    { 'name': 'pebbles', 'age': 28 },
    { 'name': 'negolas', 'age': 23 }
]

_.chain(users)
  .sortBy('age')
  .reject(user => user.age % 2)
  .map(user => user.name)
  .value()

// 输出: ["barney", "pebbles"]

经过 _.chain方法处理后,就可以使用 Underscore 提供的其他方法对这个数据进行操作,下面一起来看看源码是如何实现链模式。

首先是 _.chain方法:

_.chain = function(obj) {
    var instance = _(obj)  // 获得一个经 underscore 包裹后的实例
    instance._chain = true // 标记是否使用链式操作
    return instance
}

此处源码位于 underscore.js#L1615-L1619

这里通过_(obj)的方式把数据进行了包装,并返回了一个对象,结构如下:

{
    _chain: true,
    _wrapped: [...],
    __proto__:  ...
}

返回的对象的隐式原型可以访问到 Undersocre 提供的很多方法,如下图:

image

这个 chain 方法的作用就是创建一个包裹了 objUnderscore 实例对象,并标记该实例是使用链模式,最后返回这个包装好的链式化实例(叫链式化是因为可以继续调用 underscore 上的方法)。

我们一起看看 sort 方法是如何实现的:

var chainResult = function (instance, obj) {
    return instance._chain ? _(obj).chain() : obj;  // 这里 _chain 为 true
};

_.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
    var method = Array.prototype[name];
    _.prototype[name] = function() {
        var obj = this._wrapped;
        method.apply(obj, arguments);   // 执行方法

        return chainResult(this, obj);
    };
});

此处源码位于 underscore.js#L1649-L1657

  • sort 方法执行之后,把结果重新放在 _wrapped 里,并执行 chainResult 方法,这个方法里由于 _chain 之前已经置为 true,因此会继续对结果调用 chain() 方法,包装成链式化实例并返回。
  • 最后的这个_.value 方法比较简单,就是返回链式化实例的_wrapped 值:
_.prototype.value = function() {
   return this._wrapped;
};

此处源码位于 underscore.js#L1668-L1670

总结一下,只要一开始调用了 chain 方法, _chain 这个标志位就会被置为 true,在类似的方法中,返回的值都用 chainResult 包裹一遍,并判断这个 _chain 这个标志位,为 true 则返回链式化实例,供给下一次方法调用,由此形成了链式化调用

相关文章

  • 链模式讲解

    链模式通常情况下,通过对构造函数使用 new 会返回一个绑定到 this 上的新实例,所以我们可以在 new 出来...

  • 责任链拦截器模式

    一 ,责任链模式 1,有序责任链模式 1,应用场景任务处理 和审批流程 2,逻辑讲解比如 在一个公司场景里面 工程...

  • 62 - 责任链模式

    本文主要讲解职责链模式的原理和实现。除此之外,还会利用职责链模式,带你实现一个可以灵活扩展算法的敏感词过滤框架 职...

  • 设计模式之责任链模式(Chain of Responsibili

    引入责任链模式 责任链模式的实例 责任链模式的分析 责任链模式的优势 责任链模式的应用 引入责任链模式 责任链模式...

  • 11.10设计模式-责任链模式-详解

    设计模式-责任链模式 责任链模式详解 责任链模式在android的实际运用 1.责任链模式详解 2.责任链模式在a...

  • 责任链模式实践之Zookeeper责任链模式

    责任链模式实践之Zookeeper责任链模式 一,责任链模式 定义:责任链模式(Chain of Responsi...

  • 责任链模式实践之Zuul责任链模式

    责任链模式实践之Zuul责任链模式 一,什么是责任链模式 责任链(Chain of Responsibility)...

  • Java设计模式之责任链模式

    一、责任链模式的定义二、责任链模式的使用场景三、责任链模式UML类图四、责任链模式具体实例五、责任链模式代码实现 ...

  • 设计模式之责任链模式

    责任链模式 责任链模式(Chain of Responsibility Pattern) 责任链模式将链中每一个节...

  • 设计模式之责任链模式

    责任链模式应用 1. 责任链模式介绍 责任链模式(Chain of Responsibility Pattern)...

网友评论

      本文标题:链模式讲解

      本文链接:https://www.haomeiwen.com/subject/likijltx.html