【问题标题】:JS: And if every function return "this" as a default value?JS:如果每个函数都返回“this”作为默认值?
【发布时间】:2018-03-28 00:29:32
【问题描述】:

我有一个代码设计问题。考虑以下代码:

thatObj.doThis().setThat().add().update();

为了允许链接,我经常写return this;,有时,我在这里或那里忘记这样做,然后我得到一个错误。

在许多情况下,我并不是要求特定结果(例如 thatObj.getName()thatObj.getChildren()),而是想要进行一些更新或调用设置器(例如 thatObj.setName("foo")thatObj.addChild(child)thatObj.update() ),我想知道 return this; 对于任何方法调用是否更方便,我的意思是作为 javascript 默认行为,如果不是,不这样做的原因可能是什么。

【问题讨论】:

  • 不太可能发生对 JavaScript 行为的彻底改变。
  • 最好的办法是创建一个可以扩展的类并提供你想要的接口
  • 一堂课?但是我说的是每个对象的每个方法,为了避免写return this,从现在开始说JS总是会从任何方法返回this是个好主意吗? @Pointy,是的,我也这么认为,但我一直在想,这种 JS 设计的缺点可能是什么。顺便说一句,适用于任何面向对象的编程语言。这是一个宽泛的问题,涉及性能、优化等。可能过于宽泛,无法用几句话来回答。
  • 这将基于这样的假设,即该通用编程语言的默认编码样式将是具有很多副作用的流畅(链接)样式。有些人选择创建链式接口。不是每个人都这样做。并非所有链接接口都基于返回 this,因此即使在想要创建链接接口的人的子集中,也只有一部分人会从这种非常特殊的行为中受益。我几乎从不在 JavaScript 中使用this,因为它的行为很奇怪,所以我对这种默认行为完全没有兴趣。
  • 您的问题基本上可以归结为“从根本上改变一种语言以适应我的个人编码风格是否有任何不利之处”?是的,你不是所有人。

标签: javascript ecmascript-next


【解决方案1】:
  • 如果你没有明确返回,JS 会返回undefined
  • JS 构造函数返回this,除非你的构造函数返回一个对象。
  • CoffeeScript 默认返回最后一个表达式,
  • 您希望对象上的所有方法默认返回 this

每个人都有自己的看法,什么是“正确”的做法。

从现在开始说 JS 将始终从任何方法返回 this 是个好主意吗?

从一个时刻到另一个时刻,至少 2/3 的网络将被破坏。那么,告诉我,这是个好主意吗?

JS 很久以前就制定了规则,基本不会改变(正如 Pointy 已经提到的)。那么你为什么不处理这种行为:

//extracted that from the function to avoid memory leakage
function wrapFunction(fn) {
  return function() {
    let result = fn.apply(this, arguments);
    return result === undefined ? this : result;
  }
}

//filter === false   => only own methods
//filter === true    => own methods and inherited methods
//filter is Array    => only the passed keys (if they are methods)
//filter is RegExp   => use the RegExp to filter the keys
//filter is function => regular filterFunction
function returnThisAsDefault(objectOrConstructor, filter = false) {
  if (objectOrConstructor !== Object(objectOrConstructor))
    throw new TypeError("Passed argument must be an object or a constructor. Got ", typeof objectOrConstructor);

  const validKey = key => typeof proto[key] === "function" && key !== "constructor" && key !== "prototype";

  let proto = typeof objectOrConstructor === "function" ?
    objectOrConstructor.prototype :
    objectOrConstructor;

  let filterFn = Array.isArray(filter) ? filter.includes.bind(filter) :
    filter === false || filter === true ? () => true :
    filter instanceof RegExp ? filter.test.bind(filter) :
    typeof filter === "function" ? filter :
    () => false;

  let wrapped = {};
  for (let p = proto, done = new Set(["constructor", "prototype"]); p != null && p !== Object.prototype;) {
    for (let key of Object.getOwnPropertyNames(p)) {
      if (typeof proto[key] !== "function" || done.has(key) || !filterFn.call(p, key)) continue;
      done.add(key);

      let d = Object.getOwnPropertyDescriptor(p, key);
      //typeof d.value !== "function" means that proto[key] contains a getter returning a function
      if (!d.writable && !d.configurable || typeof d.value !== "function") {
        console.log(`function ${JSON.stringify(key)} not fit to be wrapped`, d);
        continue;
      }

      d.value = wrapFunction(d.value);
      wrapped[key] = d;
    }

    if (filter === false) break;
    else p = Object.getPrototypeOf(p);
  }

  Object.defineProperties(proto, wrapped);
  return objectOrConstructor;
}

let thatObject = returnThisAsDefault({
  doThis() {
    console.log("doThis()");
  },

  setThat() {
    console.log("setThat()");
  },

  add() {
    console.log("add()");
  },

  update() {
    console.log("update()");
    return "success";
  },
});

let result = thatObject.doThis().setThat().add().update();

console.log("result: ", result);
.as-console-wrapper {
  top: 0;
  max-height: 100%!important
}

【讨论】:

  • 我试过你的代码。非常好,我从没想过在声明后包装每个方法。谢谢
  • 当然现在改变 JS 行为不是一个好主意。真正做到这一点并不是我的目的。我询问这种范式的缺点是什么。好处可能是:摆脱 var 声明/命名。允许强大的内联脚本(以 jQuery 的方式)。减少冗余代码... ...但可能不太可读?
猜你喜欢
  • 2013-01-09
  • 2019-02-22
  • 2011-12-01
  • 2018-09-23
  • 1970-01-01
  • 2011-04-22
  • 1970-01-01
  • 1970-01-01
  • 2012-08-03
相关资源
最近更新 更多