方法链可以像 Javascript 中内置函数的实现方式一样实现吗?

Spa*_*ner 7 javascript method-chaining

我认为我在方法链接方面缺少一些东西。对我来说感觉不完整。

方法链的工作原理是让每个方法返回this,以便可以调用该对象上的另一个方法。然而,返回值是this函数的结果而不是函数的结果这一事实对我来说似乎很不方便。

这是一个简单的例子。

const Obj = {
    result: 0,
    addNumber: function (a, b) {
        this.result = a + b;
        return this;
    },

    multiplyNumber: function (a) {
        this.result = this.result * a;
        return this;
    },
}

const operation = Obj.addNumber(10, 20).multiplyNumber(10).result
console.log(operation)
Run Code Online (Sandbox Code Playgroud)

关键点:

  1. 链中的每个方法Obj.addNumber(10, 20).multiplyNumber(10)都会返回this
  2. 链的最后一部分.result是返回 以外的值的部分this

这种方法的问题在于,它需要您附加一个属性/方法才能在末尾获取除 之外的值this

将其与 JavaScript 中的内置函数进行比较。

const str = "  SomE RandoM StRIng  "

console.log(str.toUpperCase()) // "  SOME RANDOM STRING  "
console.log(str.toUpperCase().trim()) // "SOME RANDOM STRING"
console.log(str.toUpperCase().trim().length) // 18
Run Code Online (Sandbox Code Playgroud)

关键点:

  1. 链中的每个函数都返回该函数的结果this(也许这是在幕后完成的)
  2. 在链的末端不需要任何属性/方法来获取结果。

我们可以实现方法链来像 Javascript 中内置函数一样运行吗?

Koo*_*Inc 0

您可以使用带有-trap的Proxy 对象来避免隐式返回值的必要性this 能够隐式返回值。get

在这里你可以找到一个更通用的工厂。

const log = Logger();

log(`<code>myNum(42)
  .add(3)
  .multiply(5)
  .divide(3)
  .roundUp()
  .multiply(7)
  .divide(12)
  .add(-1.75)</code> => ${
    myNum(42)
    .add(3)
    .multiply(5)
    .divide(3)
    .roundUp()
    .multiply(7)
    .divide(12)
    .add(-1.75)}`,
  );

log(`\n<code>myString(\`hello world\`)
  .upper()
  .trim()
  .insertAt(6, \`cruel coding \`)
  .upper()</code> => ${
    myString(`hello world`)
        .upper()
        .trim()
        .insertAt(6, `cruel coding `)
        .upper()
    }`);

log(`<br><code>myString(\`border-top-left-radius\`).toUndashed()</code> => ${
  myString(`border-top-left-radius`).toUndashed()}`);

// the proxy handling
function proxyHandlerFactory() {
  return {
    get: (target, prop) => {
      if (prop && target[prop]) {
        return target[prop];
      } 
      return target.valueOf;
    }
  };
}

// a wrapped string with chainable methods
function myString(str = ``) {
  const proxyHandler = proxyHandlerFactory();
  const obj2Proxy = {
    trim: () => nwProxy(str.trim()),
    upper: () => nwProxy(str.toUpperCase()),
    lower: () => nwProxy(str.toLowerCase()),
    insertAt: (at, insertStr) => 
      nwProxy(str.slice(0, at) + insertStr + str.slice(at)),
    toDashed: () => 
      nwProxy(str.replace(/[A-Z]/g, a => `-${a.toLowerCase()}`.toLowerCase())),
    toUndashed: () => nwProxy([...str.toLowerCase()]
      .reduce((acc, v) => {
        const isDash = v === `-`;
        acc = { ...acc,
          s: acc.s.concat(isDash ? `` : acc.nextUpcase ? v.toUpperCase() : v)
        };
        acc.nextUpcase = isDash;
        return acc;
      }, {
        s: '',
        nextUpcase: false
      }).s),
    valueOf: () => str,
  };

  function nwProxy(nwStr) {
    str = nwStr || str;
    return new Proxy(obj2Proxy, proxyHandler);
  }

  return nwProxy();
}

// a wrapped number with chainable methods
function myNum(n = 1) {
  const proxyHandler = proxyHandlerFactory();
  const obj2Proxy = {
    add: x => nwProxy(n + x),
    divide: x => nwProxy(n / x),
    multiply: x => nwProxy(n * x),
    roundDown: () => nwProxy(Math.floor(n)),
    roundUp: () => nwProxy(Math.ceil(n)),
    valueOf: () => n,
  };

  function nwProxy(nwN) {
    n = nwN || n;
    return new Proxy(obj2Proxy, proxyHandler);
  }

  return nwProxy();
}

// ---- for demo ---- //
function Logger() {
  const report =
    document.querySelector("#report") ||
    document.body.insertAdjacentElement(
      "beforeend",
      Object.assign(document.createElement("pre"), {
        id: "report"
      })
    );

  return (...args) => {
    if (!args.length) {
      return report.textContent = ``;
    }

    args.forEach(arg =>
      report.insertAdjacentHTML(`beforeEnd`,
        `<div>${arg.replace(/\n/g, `<br>`)}</div>`)
    );
  };
}
Run Code Online (Sandbox Code Playgroud)
body {
  font: 12px/15px verdana, arial;
  margin: 0.6rem;
}

code {
  color: green;
}
Run Code Online (Sandbox Code Playgroud)