将方法与 JavaScript 链接起来

Chaining methods with javascript

本文关键字:链接 起来 JavaScript 方法      更新时间:2023-09-26

我正在尝试使用javascript方法创建链接,类似于我们对jquery的链。请让我知道如何用javascript实现链接。

var controller = {
    currentUser: '',
    fnFormatUserName: function(user) {
        this.currentUser = user;
        return this.currentUser.toUpperCase();
    },
    fnCreateUserId: function() {
        return this.currentUser + Math.random();
    }
}
var output = controller.fnFormatUserName('Manju').fnCreateUserId();

正如我已经解释过的,由于您从fnFormatUserName返回一个字符串,因此不能将其用于链接。

要启用链接,您需要返回调用方法的对象。因此,您不能使用 getter 方法进行链接。

在您的示例中,处理它的方法是让 getter 方法和方法更新可用于链接的对象,例如

var controller = {
  currentUser: '',
  fnFormatUserName: function(user) {
    this.currentUser = user.toUpperCase();
    return this;
  },
  fnCreateUserId: function() {
    this.userId = this.currentUser + Math.random();
    return this;
  },
  getUserId: function() {
    return this.userId;
  }
}
var output = controller.fnFormatUserName('Manju').fnCreateUserId().getUserId();
document.body.innerHTML = output;


另一个版本可能是

var controller = {
  currentUser: '',
  fnFormatUserName: function(user) {
    if (arguments.length == 0) {
      return this.currentUser;
    } else {
      this.currentUser = user.toUpperCase();
      return this;
    }
  },
  fnCreateUserId: function() {
    this.userId = this.currentUser + Math.random();
    return this;
  },
  getUserId: function() {
    return this.userId;
  }
}
var output = controller.fnFormatUserName('Manju').fnCreateUserId().getUserId();
r1.innerHTML = output;
r2.innerHTML = controller.fnFormatUserName();
<div id="r1"></div>
<div id="r2"></div>

您可以使用代理来修饰方法,以便它们返回对象本身("this")而不是实际的方法返回值。下面是一个链接器函数的实现,它将对任何对象执行此操作。该代码还声明了一个特殊符号"target",可用于访问原始对象(和未更改的方法返回值),丢弃链接代理。

const target = Symbol('Symbol for the target of the chainer proxy');
const targetSymbol = target;
const chainer = (target) =>
  new Proxy(target, {
    get: (_, prop, receiver) =>
      prop === targetSymbol
        ? target
        : typeof target[prop] === 'function'
        ? new Proxy(target[prop], {
            apply: (f, _, args) => {
              f.apply(target, args);
              return receiver;
            },
          })
        : target[prop],
  });
const controller = {
    currentUser: '',
    fnFormatUserName: function(user) {
        return this.currentUser = user.toUpperCase();
    },
    fnCreateUserId: function() {
        return this.currentUser + Math.random();
    }
}

const output = chainer(controller).fnFormatUserName('Manju')[target].fnCreateUserId();
console.log(output);

另一种选择是,修饰的方法将始终返回具有两个属性的中间对象:this 上下文("this")和对具有未修饰方法的原始对象的引用("target")。见下文。

const chainer = (target) =>
  new Proxy(target, {
    get: (_, prop, receiver) =>
        typeof target[prop] === 'function'
        ? new Proxy(target[prop], {
            apply: (f, _, args) => {
              f.apply(target, args);
              return {
                this: receiver,
                target,
              }
            },
          })
        : target[prop],
  });
const counter = {
  value: 0,
  increment: function() { 
    return ++this.value;
  }
}
const value = chainer(counter)
    .increment().this
    .increment().target
    .increment();
console.log(value);

我想

这可能被视为"作弊",但您可以通过扩展String.prototype来轻松获得类似的结果,例如:

String.prototype.upper=function(){return this.toUpperCase()};
String.prototype.makeId=function(){return this+Math.random()};
// test
const str="abc";
console.log(str.upper().makeId(), str);

当然,它将更改当前会话中所有字符串的行为,因为它们现在将具有.upper()的其他方法和与之关联的.makeId()