在JavaScript中实现链式调用的实现

链式调用实现本身比较简单,也有很多文章详细阐述了其实现方式。本文更多从链式调用方法返回值的角度,更进一步来说明如何实现链式调用。

什么是链式调用

链式调用在 JavaScript 语言界很常见,如 jQuery 、 Promise 等,都是使用的链式调用。链式调用可以让我们在进行连续操作时,写出更简洁的代码。

new Promise((resolve, reject) => {
 resolve();
})
.then(() => {
 throw new Error('Something failed');
})
.then(() => {
 console.log('Do this whatever happened before');
})
.catch(() => {
 console.log('Do that');
})

逐步实现链式调用

假设,我们要实现一个 math 模块,使之能够支持链式调用:

const math = require('math');
const a = math.add(2, 4).minus(3).times(2);
const b = math.add(2, 4).times(3).divide(2);
const c = { a, b };

console.log(a.times(2) + b + 1); // 22
console.log(a.times(2) + b + 2); // 23
console.log(JSON.stringify(c)); // {"a":6,"b":9}

基本的链式调用

链式调用通常的实现方式,就是在函数调用结果返回模块本身。那么 math 模块的代码大致应该是这样子的:

export default {
 add(...args) {
  // add
  return this;
 },
 minus(...args) {
  // minus
  return this;
 },
 times(...args) {
  // times
  return this;
 },
 divide(...args) {
  // divide
  return this;
 },
}

方法如何返回值

上述代码实现了链式调用,但是也存在一个问题,就是无法获取计算结果。所以我们需要对模块进行改造,使用一个内部变量来存储计算结果。

export default {
 value: NaN,
 add(...args) {
  this.value = args.reduce((pv, cv) => pv + cv, this.value || 0);
  return this;
 },
}

这样,我们在最后一步,通过 .value 就可以拿到最终的计算结果了。

问题真的解决了吗

上面我们看似通过一个 value 变量解决了存储计算结果的问题,但是发生第二次链式调用时, value 的值因为已经有了初始值,我们会得到错误的计算结果!

const a = math.add(5, 6).value; // 11
const b = math.add(5, 7).value; // 23 而非 12

既然是因为 value 有了初始值,那么能不能在获取 value 的值时重置掉呢?答案是不能,因为我们并不能确定使用者会在什么时候取值。

另外一种思路是在每次链式调用之前生成一个新的实例,这样就可以确保实例之间相互独立了。

const math = function() {
 if (!(this instanceof math)) return new math();
};

math.prototype.value = NaN;

math.prototype.add = function(...args) {
 this.value = args.reduce((pv, cv) => pv + cv, this.value || 0);
 return this;
};

const a = math().add(5, 6).value;
const b = math().add(5, 7).value;

但是这样也不能彻底解决问题,假设我们如下调用:

const m = math().add(5, 6);
const c = m.add(5).value; // 16
const d = m.add(5).value; // 21 而非 16

所以,最终要解决这个问题,只能每个方法都返回一个新的实例,这样可确保无论怎么调用,相互之间都不会被干扰到。

math.prototype.add = function(...args) {
 const instance = math();
 instance.value = args.reduce((pv, cv) => pv + cv, this.value || 0);
 return instance;
};

如何支持不通过 .value 对结果进行普通运算

通过改造 valueOf 方法或者 Symbol.toPrimitive 方法。其中 Symbol.toPrimitive 方法优先 valueOf 方法被调用,除非是ES环境不支持。

如何支持 JSON.stringify 序列化计算结果

通过自定义 toJSON 方法。 JSON.stringify 将值转换为相应的JSON格式时,如果被转换值有 toJSON 方法,则优先使用该方法返回的值。

最终的完整实现代码

class Math {
 constructor(value) {
  let hasInitValue = true;
  if (value === undefined) {
   value = NaN;
   hasInitValue = false;
  }
  Object.defineProperties(this, {
   value: {
    enumerable: true,
    value: value,
   },
   hasInitValue: {
    enumerable: false,
    value: hasInitValue,
   },
  });
 }

 add(...args) {
  const init = this.hasInitValue ? this.value : args.shift();
  const value = args.reduce((pv, cv) => pv + cv, init);
  return new Math(value);
 }

 minus(...args) {
  const init = this.hasInitValue ? this.value : args.shift();
  const value = args.reduce((pv, cv) => pv - cv, init);
  return new Math(value);
 }

 times(...args) {
  const init = this.hasInitValue ? this.value : args.shift();
  const value = args.reduce((pv, cv) => pv * cv, init);
  return new Math(value);
 }

 divide(...args) {
  const init = this.hasInitValue ? this.value : args.shift();
  const value = args.reduce((pv, cv) => pv / cv, init);
  return new Math(value);
 }

 toJSON() {
  return this.valueOf();
 }

 toString() {
  return String(this.valueOf());
 }

 valueOf() {
  return this.value;
 }

 [Symbol.toPrimitive](hint) {
  const value = this.value;
  if (hint === 'string') {
   return String(value);
  } else {
   return value;
  }
 }
}

export default new Math();

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • JavaScript中两种链式调用实现代码

    一.方法体内返回对象实例自身(this) 复制代码 代码如下: function ClassA(){ this.prop1 = null; this.prop2 = null; this.prop3 = null; } ClassA.prototype = { method1 : function(p1){ this.prop1 = p1; return this; }, method2 : function(p2){ this.prop2 = p2; return this; }, metho

  • javascript中的链式调用

    jQuery中形如$("#txtName").addClass("err").css("font-size","12px").select().focus();的方式让人不得不为之着迷.其实现机制就是链式调用.链式调用就是调用对象的方法后返回到该对象,严格来讲它并不属于语法,而只是一种语法技巧,js令人着迷的一点就是这里. 没有返回值的方法属于赋值器方法,显然它很容易实现链式调用,前提是正确理解this指针的用法. 复制代码

  • 学习JavaScript设计模式(链式调用)

    1.什么是链式调用 这个很容易理解,例如: $(this).setStyle('color', 'red').show(); 一般的函数调用和链式调用的区别:调用完方法后,return this返回当前调用方法的对象. function Dog(){ this.run= function(){ alert("The dog is running...."); return this;//返回当前对象 Dog }; this.eat= function(){ alert("Af

  • JavaScript链式调用实例浅析

    本文实例分析了JavaScript链式调用.分享给大家供大家参考,具体如下: 对$函数你已经很熟悉了.它通常返回一个html元素或一个html元素的集合,如下: function$(){ var elements = []; for(vari=0,len=arguments.length;i<len;++i){ var element = arguments[i]; if(typeof element ==="string"){ element = document.getEle

  • JS链式调用的实现方法

    链式调用    链式调用其实只不过是一种语法招数.它能让你通过重用一个初始操作来达到用少量代码表达复杂操作的目的.该技术包括两个部分: 一个创建代表HTML元素的对象的工厂. 一批对这个HTML元素执行某些操作的方法. 调用链的结构$函数负责创建支持链式调用的对象 复制代码 代码如下: (function() {    /*     * 创建一个私有class     * @param {Object} els  arguments 所有参数组成的类数组     */    function _

  • JavaScript的级联函数用法简单示例【链式调用】

    本文实例讲述了JavaScript的级联函数用法.分享给大家供大家参考,具体如下: 级联函数 级联函数就是在对象调用中通过点的方式串联调用,在jQuery中就是链式调用, 其关键点就是在内部 return this 返回自身 应用 function Person() { this.name = ''; this.age = 0; this.weight = 10; } Person.prototype = { setName:function(name){ this.name = name; r

  • javascript简单链式调用案例分析

    本文实例讲述了javascript简单链式调用方法.分享给大家供大家参考,具体如下: jQuery用的就是链式调用.像一条连接一样调用方法. 链式调用的核心就是return this;,每个方法都返回对象本身. 下面是简单的模拟jQuery的代码: <script> window.$ = function (id) { return new _$(id); } function _$(id) { this.elements = document.getElementById(id); } _$

  • JavaScript中链式调用之研习

    一.对象链:方法体内返回对象实例自身(this) 复制代码 代码如下: function ClassA(){ this.prop1 = null; this.prop2 = null; this.prop3 = null; } ClassA.prototype = { method1 : function(p1){ this.prop1 = p1; return this; }, method2 : function(p2){ this.prop2 = p2; return this; }, m

  • 在JavaScript中实现链式调用的实现

    链式调用实现本身比较简单,也有很多文章详细阐述了其实现方式.本文更多从链式调用方法返回值的角度,更进一步来说明如何实现链式调用. 什么是链式调用 链式调用在 JavaScript 语言界很常见,如 jQuery . Promise 等,都是使用的链式调用.链式调用可以让我们在进行连续操作时,写出更简洁的代码. new Promise((resolve, reject) => { resolve(); }) .then(() => { throw new Error('Something fai

  • 详解JavaScript中的链式调用

    链模式 链模式是一种链式调用的方式,准确来说不属于通常定义的设计模式范畴,但链式调用是一种非常有用的代码构建技巧. 描述 链式调用在JavaScript语言中很常见,如jQuery.Promise等,都是使用的链式调用,当我们在调用同一对象多次其属性或方法的时候,我们需要多次书写对象进行.或()操作,链式调用是一种简化此过程的一种编码方式,使代码简洁.易读. 链式调用通常有以下几种实现方式,但是本质上相似,都是通过返回对象供之后进行调用. this的作用域链,jQuery的实现方式,通常链式调用

  • JavaScript中原型链存在的问题解析

    我们知道使用原型链实现继承是一个goodway:)看个原型链继承的例子. function A () { this.abc = 44; } A.prototype.getAbc = function (){ return this.abc; }; function B() { } B.prototype = new A(); // B通过A的实例完成了继承,形成了原型链(B的原型就是A的实例) var b = new B(); b.getAbc(); 关系如下:b(实例) ->B.prototy

  • Java及Android中常用链式调用写法简单示例

    本文实例讲述了Java及Android中常用链式调用写法.分享给大家供大家参考,具体如下: 最近发现,目前大火的许多开源框架中,大多都使用了一种"(方法).(方法).(方法)"的形式进行调用,最典型的就是RxJava.android中AlertDialog控件的源码也是这种形式的.查阅可知,大家把它叫做链式调用."行动是检验程序的唯一标准"0.0!查了.说了那么多,还是得自己写个例子并运行出预期的效果. /** * * 链式调用 * * @author k.k *

  • JavaScript中作用域链的概念及用途讲解

    从零开始讲解JavaScript中作用域链的概念及用途 引言 之前我写过一篇关于JavaScript中的对象的一篇文章,里面也提到了作用域链的概念,相信大家对这个概念还是没有很深的理解,并且这个概念也是面试中经常问到的,因为这个概念实在太重要了,在我们平时写代码时,也可能会因为作用域链的问题,而出现莫名其妙的bug,导致我们花费大量的时间都查找不出原因.所以我就准备单独写一篇关于作用域链的文章,来帮大家更好地理解这个概念. 正文 一.执行环境 首先,我们要引入一个概念,叫做执行环境(下面简称环境

  • 《javascript设计模式》学习笔记四:Javascript面向对象程序设计链式调用实例分析

    本文实例讲述了Javascript面向对象程序设计链式调用.分享给大家供大家参考,具体如下: 1.链式调用: jquery可能是目前大家最常用到的js框架了,也习惯了如下的调用方式: $('.someclass').show().css('xxx','xxxx').click(function(){}).xxx-- 这种调用方式就叫做链式调用,也就是说在调用一个方法之后,可以使用方法的返回值来继续进行方法调用. 2.如何实现链式调用: 了解了什么是链式调用,我们再来看如何实现链式调用,从1中所述

  • JavaScript DSL 流畅接口(使用链式调用)实例

    认真研究了一会DSL,发现了这么几件有趣的事,JavaScript用得最多的一个东西怕是链式调用 (方法链,即Method Chaining). 有意思的是Martin Flower指出: 复制代码 代码如下: I've also noticed a common misconception - many people seem to equate fluent interfaces with Method Chaining. Certainly chaining is a common tec

随机推荐