JS中链式调用的实现和应用

你好同学,我是沐爸,欢迎点赞、收藏和关注。个人知乎

不知你是否注意过,链式调用在编程中还是很普遍的,无论是原生JavaScript还是在一些框架和库中,都会看到它们的身影。今天我们一探究竟,看下链式调用是怎么实现的,以及在项目中的应用吧。

在JavaScript中,链式调用(Chaining)是一种非常流行的设计模式,它允许你以流畅的方式连续调用同一个对象的方法。链式调用通常是通过在每个方法的末尾返回对象本身(通常是**this**关键字)来实现的。这样做的好处是代码更加简洁、易读,并且提高了代码的可维护性。

链式调用的实现

构造函数的实现

function Chain(value = 0) {
  this.value = value

  // 设置value的值并返回对象本身
  this.setValue = function (value) {
    this.value = value
    return this
  }

  // 增加value的值并返回对象本身
  this.addValue = function (value) {
    this.value += value
    return this
  }

  // 返回value的值
  this.getValue = function () {
    return this.value // 这里不返回this,因为getValue后通常不需要继续链式调用
  }
}

// 使用
const chain = new Chain()
let res = chain.setValue(5).addValue(10).getValue()
console.log(res) // 15

在这个例子中,Chain类的每个方法(除了getValue,因为它不参与链式调用)在执行完自己的任务后,都返回了对象本身(this),这允许你能够连续地调用这些方法。

原型的实现

使用原型prototye改写构造函数示例:

function Chain(value = 0) {
  this.value = value
}

Chain.prototype.setValue = function (value) {
  this.value = value
  return this
}

Chain.prototype.addValue = function (value) {
  this.value += value
  return this
}

Chain.prototype.getValue = function () {
  return this.value
}

// 使用
const chain = new Chain()
let res = chain.setValue(5).addValue(10).getValue()
console.log(res) // 15

Chain 函数定义了 value 属性,并使用 Chain.prototype 来定义 setValueaddValuegetValue 方法。这些方法都是实例方法,它们修改或返回 value 属性的值,并返回 this 以支持链式调用。

类的实现

使用类class改写普通函数示例:

class Chain {
  constructor(value = 0) {
    this.value = value
  }

  setValue(value) {
    this.value = value
    return this
  }

  addValue(value) {
    this.value += value
    return this
  }

  getValue() {
    return this.value
  }
}

// 使用
const chain = new Chain()
let res = chain.setValue(5).addValue(10).getValue()
console.log(res) // 15

Chain 类有一个构造函数,用于初始化 value 属性。setValueaddValuegetValue 方法都是实例方法,它们修改或返回 value 属性的值,并返回 this 以支持链式调用。

使用Proxy实现链式调用

var pipe = function (value) {
  var funcStack = []
  var oproxy = new Proxy(
    {},
    {
      get: function (pipeObject, fnName) {
        if (fnName === 'get') {
          return funcStack.reduce(function (val, fn) {
            return fn(val)
          }, value)
        }
        funcStack.push(window[fnName])
        return oproxy
      }
    }
  )

  return oproxy
}

var double = (n) => n * 2
var pow = (n) => n * n
var reverseInt = (n) => n.toString().split('').reverse().join('') | 0

pipe(3).double.pow.reverseInt.get // 63

上面的示例来自:https://es6.ruanyifeng.com/#docs/proxy

JS原生对象中的链式调用

数组的链式调用

在JavaScript中,数组(Array)原生支持链式调用,这主要得益于数组方法返回数组本身或者新的数组对象。数组的大部分方法都支持链式调用。

const arr = [1, 2, 3, 4, 5]
  .filter((number) => number > 2) // 过滤出大于2的数
  .map((number) => number * 2) // 将结果翻倍
  .reduce((acc, number) => acc + number, 0) // 计算总和

console.log(arr) // 输出: 24

在这个示例中,我们首先使用.filter()过滤数组,然后使用.map()对结果进行映射,最后使用.reduce()计算所有结果的总和。每个方法都返回一个新的数组或累加值,允许我们继续调用下一个数组方法,实现链式调用。

Promise的链式调用

const p = Promise.resolve(1) // 创建一个已解决的Promise

p.then((result) => {
  console.log(result) // 输出: 1
  return result + 1 // 返回一个新的Promise
})
  .then((result) => {
    console.log(result) // 输出: 2
    return result * 2 // 返回一个新的Promise
  })
  .then((result) => {
    console.log(result) // 输出: 4,链式调用结束
  })
  .catch((error) => {
    console.error('发生错误:', error) // 错误处理
  })

在这个示例中,每个.then()方法调用后都返回一个新的Promise,这个Promise将接收上一个Promise的结果作为输入。链中的每个.then()方法都可以根据需要进行异步操作,并且它们的返回值可以被下一个.then()捕获,.catch()方法用来捕获链中任何地方发生的错误。

库和框架中的链式调用

链式调用在某些库和框架的API设计中非常常见。

jQuery

jQuery的DOM操作方法就大量使用了链式调用,使得操作DOM元素变得非常便捷和直观。

$('#myElement')
    .removeClass('old-class')
    .addClass('new-class')
    .hide()
    .fadeIn(1000);

在上面的jQuery示例中,addClasshidefadeIn方法都返回了jQuery对象本身,允许这些方法被连续调用,形成链式调用。
addClass源码示例:

jQuery.fn.extend({
  addClass: function (value) {
    var classNames, cur, curValue, className, i, finalValue

    if (isFunction(value)) {
      return this.each(function (j) {
        jQuery(this).addClass(value.call(this, j, getClass(this)))
      })
    }

    classNames = classesToArray(value)

    if (classNames.length) {
      return this.each(function () {
        curValue = getClass(this)
        cur = this.nodeType === 1 && ' ' + stripAndCollapse(curValue) + ' '

        if (cur) {
          for (i = 0; i < classNames.length; i++) {
            className = classNames[i]
            if (cur.indexOf(' ' + className + ' ') < 0) {
              cur += className + ' '
            }
          }

          finalValue = stripAndCollapse(cur)
          if (curValue !== finalValue) {
            this.setAttribute('class', finalValue)
          }
        }
      })
    }

    return this // 返回jQuery对象本身
  }
})

Lodash

Lodash 的很多函数都支持链式调用,这意味着你可以将多个方法连续调用,而不需要创建临时变量。

数组示例:

const result = _([1, 2, 3, 4, 5])
  .filter((n) => n > 2) // 过滤出大于 2 的数字
  .map((n) => n * 2) // 将结果中的每个数字乘以 2
  .value() // 获取最终结果

console.log(result) // [6, 8, 10]

对象示例:

const users = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
  { name: 'Carol', age: 22 }
]

const names = _(users)
  .filter({ age: 30 }) // 过滤出年龄为 30 的用户
  .map('name') // 取出这些用户的名字
  .value() // 获取最终结果

console.log(names) // ['Bob']

字符串示例:

const namesString = _(['Alice', 'Bob', 'Carol'])
  .join(' & ') // 将名字数组连接成一个字符串,元素之间用 " & " 分隔
  .chain() // 开启链式调用
  .trim() // 去除字符串两端的空白字符
  .toUpperCase() // 将字符串转换为大写
  .value() // 获取最终结果

console.log(namesString) // 输出: "ALICE & BOB & CAROL"

Axios

Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 Node.js 环境。Axios 提供了链式调用的能力,使得你可以连续地使用多个实例方法来处理 HTTP 请求。

axios
  .get('https://api.example.com/data')
  .then((response) => {
    console.log(response.data) // 处理响应数据
  })
  .catch((error) => {
    console.error('请求失败:', error) // 处理请求错误
  })

Node.js

在Node.js中,链式调用通常与流、事件和异步操作相关。

Node.js的流API允许你以链式的方式连接数据读取和写入操作:

const fs = require('fs')
const zlib = require('zlib')

// 创建一个可读流
const rs = fs.createReadStream('input.txt')

// 使用 gzip 压缩数据
const gzip = zlib.createGzip()

// 创建一个写入流并压缩数据
const ws = fs.createWriteStream('input.txt.gz')

rs
  .pipe(gzip) // 将读取的数据传递给 gzip 流
  .pipe(ws) // 将 gzip 压缩后的数据写入文件

rs.on('error', (err) => {
  console.error('读取错误:', err)
})

ws.on('error', (err) => {
  console.error('写入错误:', err)
})

Node.js的EventEmitter类允许你以链式的方式注册多个事件监听器:

const emitter = new EventEmitter()

emitter
  .on('event1', () => {
    console.log('事件1发生')
  })
  .on('event2', () => {
    console.log('事件2发生')
  })
  .emit('event1') // 触发事件1

是不是像我说的?链式调用在前端编程中大量存在。希望对你有所帮助,下期再见!

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沐爸muba

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值