11

首先也是最重要的是,我试图检测方法链的结束调用。我还想设计一种方法来检测我在方法链中的方法调用中有多少方法“进入”或“向下”对象链。

例如,在我正在编写的插件中:

var result = $("#someDiv").myPlugin.foo().bar()._foo()._bar();

假设该方法当前正在 .bar() 中执行,我想知道我是整个链中的 2 个方法。

我需要以某种方式抽象这些信息的原因是,当我到达链中的最后一个方法时,我可以返回结果而不是插件对象,从而在该点打破链,以便访问我们的数据“结果”变量。

4

5 回答 5

5

这是从您的项目中提取的示例:

var strLenA = parseInt( P.strlen('some string').data );
var strLenB = parseInt( P.strlen('another string').data );
var totalStrLen = strLenA + strLenB;
console.log(strLenA, strLenB, totalStrLen);

从这里我可以看出为什么我们的答案并不充分——以及为什么你想摆脱.data. 令人高兴的.data是,无论如何,您总是返回一个字符串。因此,您可以使用神秘的.toString覆盖让您的方法仍然返回父方法的副本 - 但也允许将它们视为字符串。

这是一个例子:[用小提琴]

var stringMagic = function() {
    var chain = "",
        self = this;
    self.toString = function () { return chain; }; // Where the real magic happens.
    self.add = function(str) {
        chain += str + " ";
        return self;
    };
};


var magi = new stringMagic();
alert(magi.add("hello").add("world")); // Alerts, "hello world"
magi.add("and").add("thanks").add("for").add("all").add("the").add("fish");
alert(magi); // Alerts, "hello world and thanks for all the fish"

在您的情况下,您可能需要做的就是更改.data为并将P.toString包装在一个函数中。

将来当您添加对其他数据类型(例如数字和布尔值)的支持时,您可以valueOf像使用toString. 事实上,toString当返回值不是字符串时,您还应该继续包含当他们将该数字视为字符串时 - 就像在console.logor中一样$.fn.text。这是上面的示例,但带有数字:http: //jsfiddle.net/emqVe/1/

于 2013-01-02T15:54:42.753 回答
2

为了完整起见。另一种选择是传递一个对象,该对象将随着链的进展而更新。这将让您在适合您的时候访问结果值(而不必在链的末尾添加它)。

而不是这样的语法:

var result = chainableFunction.doThis().doThat().result;

然后,您将拥有:

chainableFunction.update(variableToUpdate).doThis().doThat();
var result = variableToUpdate.result;

逻辑与其他人提出的解决方案非常相似。使用哪一个可能取决于您的用例。必须用 .result 结束链的一个可能问题是没有什么可以阻止您以这种方式使用它:

var chainedUp = chainableFunction.doThis().doThat();
doSomething(chainedUp.result);
... 
chainedUp.doOneMoreThing()
... 
doSomething(chainedUp.result);  // oups, .result changed!

使用 variableToUpdate 选项,结果值不受未来函数调用的影响。同样,这在某些情况下可能是可取的,而在其他情况下则不然。

下面的完整示例

#!/usr/bin/env node

var ChainUp = {};
(function(Class) {

  // Pure functions have no access to state and no side effects
  var Pure = {};
  Pure.isFunction = function(fn) {
     return fn && {}.toString.call(fn) === '[object Function]';
  };

  Class.instance = function() {
    var instance = {};
    var result;
    var updateRef;

    function callBack(fn) {
      // returning a clone, not a reference.
      if(updateRef) { updateRef.r = (result || []).slice(0); } 
      if(Pure.isFunction(fn)) { fn(result); }
    }

    instance.update = function(obj) {
      updateRef = obj;
      return instance;
    };

    instance.one = function(cb) {
        result = (result || []); result.push("one");
        callBack(cb);
        return instance;
      };
      instance.two = function(cb) {
        result = (result || []); result.push("two");
        callBack(cb);
        return instance;
      };
      instance.three = function(cb) {
        result = (result || []); result.push("three");
        callBack(cb);
        return instance;
      };
      instance.result = function() {
        return result;
      };
    return instance;
  };

}(ChainUp));


var result1 = {};
var chain = ChainUp.instance().update(result1);
var one = chain.one(console.log); // [ 'one' ]
console.log(one.result());        // [ 'one' ]
console.log(result1.r);           // [ 'one' ]

var oneTwo = chain.two(); 
console.log(oneTwo.result());  // [ 'one', 'two' ]
console.log(result1.r);        // [ 'one', 'two' ]

var result2 = {};
var oneTwoThree = chain.update(result2).three();
console.log(oneTwoThree.result()); // [ 'one', 'two', 'three' ]
console.log(result2.r);            // [ 'one', 'two', 'three' ]

console.log(result1.r);             // [ 'one', 'two' ]

笔记。类和实例关键字可能不熟悉。这是我在使用闭包而不是原型继承从原型构造实例时使用的约定。您可以用 self 替换实例(并且 self = this 而不是 instance = {})..

于 2013-09-26T12:30:05.313 回答
1

没有(合法的或简单的或好的)方法可以在方法内部找出结果在外部发生了什么,然后返回。您应该使用“链结束标记”方法。

再想一想,您是在寻找应用于对象的最后一种方法,还是想检测更明确的东西?也许您失去了将方法应用于决策的可能性(使用虚假的愚蠢方法名称):

obj.turnLeft().pushUp().makeBig().makeSilent;
if (colorSupport) {
  obj.paintRed();
} else {
  obj.paintStripes();
}
obj.makeShine().lastMethodCallSoObjectIsNowInFinalState();
于 2013-01-02T14:29:40.860 回答
1

在确定返回值时,无法确定调用是否是链中的最后一个实例,原因如下:

var result = $("#someDiv").myPlugin.foo().bar()._foo()._bar();

foo返回myPlugin哪个bar被调用哪个返回myPlugin哪个_foo被调用哪个返回myPlugin哪个_bar被调用。

如此有效地,当_foo返回它的值 ( myPlugin) 时,它是在该值被使用之前。除非_foo是通灵者,否则它无法知道接下来会发生什么。

正如您在评论中指出的那样,您最好的选择是使用一些“结束”方法,例如results().

另一个建议是将处理程序传递给myPlugin被调用的处理程序以使用setTimeout(..., 0). 在myPluginfoo、bar、_foo 和 _bar 中设置一个值。让我们称之为returnValue。修改 myPlugin 以接受一个方法作为它的唯一参数。让我们称之为handler。此方法的第一个参数将包含该值。在 myPlugin 内部,在你的 之前return,做:

window.setTimeout(function () {
    handler(returnValue);
}, 0);

由于 setTimeout 的函数参数在执行完成之前不会被调用,因此它将包含最后设置returnValue的值 - 实际上是链中最后一次调用设置的值。我认为这是与您尝试实现的目标最接近的选择,因为开发人员不必担心最后调用他的哪个方法。

于 2013-01-02T14:30:22.020 回答
0

没有本地方法,但是,您可以根据需要向要链接的方法添加参数。并通过将其设置为 来确定当前呼叫是否是最新的true,如下所示:

var AlertText = {
  text: "",
  chainEntry: function(textToAdd, isTheLastOne) {
    this.text += textToAdd;
    // Perform only if this is told to be the last call in the chain:
    if (isTheLastOne) {
      alert(this.text);
      this.text = "";
    }
    //
    return this;
  }
};

AlertText
  .chainEntry("Hi, ")
  .chainEntry("how ")
  .chainEntry("are ")
  .chainEntry("you?", true); // alert("Hi, how are you?");
于 2017-08-13T13:04:00.097 回答