简体   繁体   English

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

[英]How to detect the end of a method chain in JavaScript?

Firstly and most importantly I'm trying to detect the end call of a method chain. 首先,最重要的是,我正在尝试检测方法链的结束调用。 I would also like to devise a way to detect how many methods "in" or "down" an object chain I am within my method calls in a method chain. 我还想设法一种方法来检测我在方法链中的方法调用中有多少方法“进入”或“向下”一个对象链。

For instance, in the plugin I'm writing: 例如,在我写的插件中:

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

Say the method is currently executing in .bar() I would like to know that I'm 2 methods down the chain. 假设该方法当前正在.bar()中执行我想知道我是链中的两种方法。

The reason I need to abstract this information in some manner is so when I reach the last method in the chain I can return a result instead of the plugin object thus breaking the chain at that point for the sake of gaining access to our data in the 'result' variable. 我需要以某种方式抽象这些信息的原因是当我到达链中的最后一个方法时,我可以返回一个结果而不是插件对象,从而在该点打破链,以便获取对我们数据的访问权限。 '结果'变量。

Here's an example pulled from 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);

From this I can see why our answers aren't really adequate - and why you want to get rid of .data . 从这里我可以看出为什么我们的答案不够充分 - 以及你为什么要摆脱.data Happily, your .data always returns a string, anyway. 令人高兴的是,无论如何,你的.data总是返回一个字符串。 So, you can use the mystical .toString override to have your methods still return a copy of the parent method - but also allow for them to be treated like strings. 因此,您可以使用神秘的.toString重写来使您的方法仍然返回父方法的副本 - 但也允许将它们视为字符串。

Here's an example: [with a 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"

In your case, probably all you'd have to do is change .data in P to .toString and wrap it in a function. 在您的情况下,您可能需要做的就是将P .data更改为.toString并将其包装在函数中。

In the future when you add support for other data types such as numbers and booleans, you can use valueOf in the same way you use toString . 在当您添加其他数据类型,如数字和布尔的支持以后,可以使用valueOf你用同样的方法toString In fact, you should also continue to include toString when the return value is not a string for when they're treating that number as a string - like in console.log or $.fn.text . 实际上,当返回值不是字符串时,你还应该继续包含toString ,因为它们将该数字视为字符串 - 例如在console.log$.fn.text Here's the example above, but with numbers: http://jsfiddle.net/emqVe/1/ 这是上面的例子,但有数字: http//jsfiddle.net/emqVe/1/

For the sake of completeness. 为了完整起见。 Yet another alternative is to pass a an object that will get updated as the chain progress. 另一种替代方法是传递一个将随着链的进展而更新的对象。 That would let you access the result value whenever suits you (instead of having to add it at the end of the chain). 这样您就可以在适合的时候访问结果值(而不必在链的末尾添加它)。

Instead of a syntax like this: 而不是像这样的语法:

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

You would then have: 然后你会:

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

The logic is very much the same as the solution proposed by others. 逻辑与其他人提出的解决方案非常相似。 Which one to use probably depends on your use cases. 使用哪一个可能取决于您的使用案例。 A possible issue with having to end the chain with .result is that nothing prevents you from using it this way: 不得不用.result结束链的一个可能的问题是没有什么能阻止你以这种方式使用它:

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

With the variableToUpdate option, the result value is not affected by future function calls. 使用variableToUpdate选项,结果值不受未来函数调用的影响。 Again, that could be desirable in some contexts, not in others. 同样,在某些情况下,这可能是可取的,而在其他情况下则不是。

Full example below 完整的例子如下

#!/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' ]

Note. 注意。 The Class and instance keywords are probably unfamiliar. Class和instance关键字可能不熟悉。 That's a convention that I use when using closures instead of prototypical inheritance to construct instances from a prototype. 这是我在使用闭包而不是原型继承来从原型构造实例时使用的约定。 You could replace instance with self (and self = this instead of instance = {}).. 你可以用self替换实例(而self = this而不是instance = {})..

There are no (legal or easy or nice) way to find out inside a method what happens with the result outside , after it returns with it. 有没有(法律或轻松或漂亮)的方式找出其结果发生,它与它返回后一种方法里面 You should use a "chain end mark" method. 您应该使用“链末端标记”方法。

Think again, are you looking for the last method applied on an object, or do you want to detect something more explicite thing? 再想一想,您是在寻找应用于对象的最后一种方法 ,还是想要检测更明确的东西? Maybe you lose a possibility to apply methods on a decision (with fake silly method names): 也许你失去了在决策上应用方法的可能性(假傻方法名称):

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

It isn't possible to determine if a call is the last instance in a chain when determining the return value , and here is why: 在确定返回值时 ,无法确定调用是否是链中的最后一个实例,这就是原因:

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

foo returns myPlugin on which bar is called which returns myPlugin on which _foo is called which returns myPlugin on which _bar is called. foo返回myPlugin了哪个bar myPlugin ,返回myPlugin_foo myPlugin ,返回myPlugin _bar

So effectively, when _foo returns its value ( myPlugin ), it is before that value is utilized. 如此有效,当_foo返回其值( myPlugin )时,它就在使用该值之前。 Unless _foo is psychic, it can't know what will happen next. 除非_foo是通灵的,否则它无法知道接下来会发生什么。

As pointed out in your comments, your best bet is to have some "end" method, like results() . 正如你的评论中所指出的,你最好的选择是采用一些“结束”方法,比如results()

Another suggestion would be to pass a handler in to myPlugin that gets called to set the value using setTimeout(..., 0) . 另一个建议是将一个处理程序传递给myPlugin ,调用它来使用setTimeout(..., 0)设置值。 Have a value in myPlugin that foo, bar, _foo, and _bar all set. myPlugin中有一个foo,bar,_foo和_bar全部设置的值。 Let's call it returnValue . 我们称之为returnValue Modify myPlugin to accept a method as it's only parameter. 修改myPlugin以接受方法,因为它是唯一的参数。 Let's call that handler . 我们称之为handler This method's first argument will contain the value. 此方法的第一个参数将包含该值。 Inside of myPlugin, before your return , do: 在myPlugin内部,在您return之前,执行:

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

Since setTimeout's function parameter will not be called until execution is finished, it will contain the last value set for returnValue - effectively the value set by the last call in the chain. 由于setTimeout的函数参数在执行完成之前不会被调用,因此它将包含为returnValue设置的最后一个值 - 实际上是链中最后一次调用所设置的值。 I'd consider this the closest option to what you are trying to achieve, since the developer doesn't have to worry about which of his methods are called last. 我认为这是你想要实现的最接近的选项,因为开发人员不必担心他的哪个方法最后被调用。

There is no native way, however, you can add a parameter to the method meant to be chained as you want. 没有本机方式,但是,您可以根据需要向方法添加参数。 And determine if the current call is the latest by setting it to true , as in: 并通过将其设置为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?");

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM