如何在JavaScript中检测方法链的结束?

时间:2013-01-02 14:11:21

标签: javascript jquery jquery-plugins

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

例如,在我写的插件中:

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

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

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

5 个答案:

答案 0 :(得分:4)

以下是从your project拉出的示例:

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覆盖来让您的方法仍然返回父方法的副本 - 但也允许将它们视为字符串。

以下是一个示例:[使用fiddle]

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.log$.fn.text。以上是上面的例子,但是有数字:http://jsfiddle.net/emqVe/1/

答案 1 :(得分: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' ]

请注意。 Class和instance关键字可能不熟悉。这是我在使用闭包而不是原型继承来从原型构造实例时使用的约定。你可以用self替换实例(而self = this而不是instance = {})..

答案 2 :(得分:1)

没有(合法的,简单的或好的)方法来找出里面的一个方法,结果外面,在它返回之后。您应该使用“链末端标记”方法。

再想一想,您是否正在寻找应用于对象的 last method ,或者您想要检测更明确的东西吗?也许你失去了在决策上应用方法的可能性(假傻的方法名称):

obj.turnLeft().pushUp().makeBig().makeSilent;
if (colorSupport) {
  obj.paintRed();
} else {
  obj.paintStripes();
}
obj.makeShine().lastMethodCallSoObjectIsNowInFinalState();

答案 3 :(得分:1)

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

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

foo返回调用myPlugin的{​​{1}},其中bar返回myPlugin,其中_foo将返回myPlugin _bar _foo myPlugin 1}}被称为。

如此有效,当_foo返回其值(results())时,它就在使用该值之前。除非myPlugin是精神上的,否则它无法知道接下来会发生什么。

正如您的评论所指出的,最好的办法是采用一些“结束”方法,例如setTimeout(..., 0)

另一个建议是将处理程序传递给myPlugin,并使用returnValue调用该句柄来设置值。在handler中设置了foo,bar,_foo和_bar都设置的值。我们称之为return。修改myPlugin以接受方法,因为它是唯一的参数。我们称之为window.setTimeout(function () { handler(returnValue); }, 0); 。此方法的第一个参数将包含该值。在myPlugin内部,在returnValue之前,执行:

{{1}}

由于setTimeout的函数参数在执行完成之前不会被调用,因此它将包含为{{1}}设置的最后一个值 - 实际上是链中最后一次调用所设置的值。我认为这是你想要实现的最接近的选项,因为开发人员不必担心他的哪个方法最后被调用。

答案 4 :(得分: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?");