如何在JavaScript中检测链式函数调用?

时间:2012-10-01 19:22:35

标签: javascript

var Obj = {
   func1 : function() {
      // some code
      if (this._hasChainedFunc()) {
         // block should be CALLED
      }
      return this;
   },
   func2 : function() {  
      // some code
      if (this._hasChainedFunc()) {
         // block should be NOT called
      }
      return this;
   },
   _hasChainedFunc : function() {
     // code which detects if there is a chained function???
   }
}

Obj.func1().func2();

是否有可能实现函数 _hasChainedFunc()?此函数应在第一次调用时返回 true (因为之后调用func2()),第二次调用时返回 false

在更高级的版本中, _hasChainedFunc()也可能会返回之后实际调用的函数。

8 个答案:

答案 0 :(得分:4)

从技术上讲,你可以事先知道在当前通话之后是否还有另一个被锁定的电话 - 这显然没有意义,因为它意味着你知道一些以前会被调用的代码它被称为。如果没有预编译器,你就无法做到这一点,我想这不是你想要的。

相反, 可以检查在当前通话之前是否存在先前已锁定的通话。这只需要您在对象中保留一些关于先前调用的状态,并在您调用其上的新函数时更新它。如果您只使用一个调用链,则可以通过func1func2更改this对象上的某个状态来执行此操作。

如果要在同一对象上调用多个链,则会遇到如何检测链末端的问题。为此,您需要使每个链接函数返回原始this周围的包装器,这将存储有关先前调用的状态。

如果您使用包装器方法,obj.func1().func2()会在func1上调用obj,但会在func2返回的包装器上调用func1,此包装器可以请注意之前的func1电话。如果您稍后致电obj.func2().func1(),则会在func2上调用obj,而在知道之前的func1调用等的包装器上调用func2

答案 1 :(得分:1)

不,这是不可能的。

它在语义上与:

相同
var tmp = Obj.func1();
tmp.func2();

调用Obj.func1()时,无法知道后续结果是否会用于调用func2

您可以实现的最佳效果是func2检测先前是否func1被调用,但是为了使其按照您所描述的方式工作,需要func1能够预测未来。

答案 2 :(得分:0)

您可以做的是添加一个成员属性,指示它是否是对该对象进行的第一次调用:

var Obj = {
   _first : true,

   func1 : function() {
      // some code
      if (this._hasChainedFunc()) {
         // block should be CALLED
      }
      return this;
   },
   func2 : function() {  
      // some code
      if (this._hasChainedFunc()) {
         // block should be NOT called
      }
      return this;
   },
   _hasChainedFunc : function() {
       var isFirst = this._first;
       this._first = false;
       return isFirst;
   }
}

Obj.func1().func2();

但是,这意味着您必须在每次调用之前重置对象的状态(通过将this._first设置回true)。你可能想重新考虑一下这个问题。

答案 3 :(得分:0)

这是我怎么做的:

var Obj = {
    first:0,   //<--- will store whether it's the first call
    func1 : function() {
            // some code
        if (this._hasChainedFunc()) {
            console.log("called1");
        }
         return this;
    },
    func2 : function() {  
        // some code
        if (this._hasChainedFunc()) {
            console.log("called2");
        }
        return this;
    },
    _hasChainedFunc : function() {
        return (this.first++ > 0);
    }
}

Obj.func1().func2();

这似乎有效:

  called2

http://jsfiddle.net/2VThj/1/

答案 4 :(得分:0)

你为什么要这样做?

除了这个问题之外,您可以,而不是返回实际对象,复制它,并添加一个属性来告诉您它是对象的返回版本。这是我能想到的唯一方法。听起来很复杂,取决于这个对象的复杂程度。

类似的东西:

func1 : function() {
  // some code
  if (this._hasChainedFunc()) {
     // block should be CALLED
  }
  return deepCloneWithFlag(this);
},
_hasChainedFunc : function() {
   return this.flag;
}

答案 5 :(得分:0)

不。这不行。你可能可能告诉func1()在某个时刻被调用过这个对象,但你无法告诉 WHEN 它被调用,即在func2之前

例如:

obj.func1();
obj.func2();

等同于您的示例调用。并且func1无法知道将来会调用func2。

我使用chain functionsdocs)解决了与此类似的问题。这允许真正的函数链接,具有“预见”的能力,以查看链中的内容。

答案 6 :(得分:0)

你可以做的是有两个独立的类,一个用于链中的第一个元素,另一个用于其余元素。然后你要做的就是改变第一个类,从第二个类而不是当前对象返回一个等价对象。

var Class1 = function(state){
   return {
       func1 : function() {
           // some code
           // block should be CALLED
           return Class2(state)
       },
       func2 : function() {  
           // some code
           // block should be NOT called
           return Class2(state)     
       }
    };
}


var Class2 = function(state){
   return {
       func1 : function() {
           // some code
           return this;
       },
       func2 : function() {  
           // some code
           return this;
       }
    };
}

Class1(initial_state).func1().func2();

答案 7 :(得分:0)

虽然知道在Javascript中无法使用另一个函数后将调用函数,但这是一个链接对象的解决方案:

(function(window, undefined)
{

    var chainify = function(prop)
    {
        return new chainify.init(prop);
    };

    /**
     * 
     * @param prop :
     *            Properties to apply to the object
     * @returns {chainify.init}
     */
    chainify.init = function(prop)
    {
        for ( var key in prop)
            this[key] = prop[key];
    };

    chainify.init.prototype = {

        _attributes : {},

        _chain_in_progress : false,
        _chain_level : 1,
        _chain_function : '',

        /**
         * Returns the chained object
         * 
         * @param name -
         *            name of the previous function
         * @this chainify.init
         * @returns {chainify.init}
         */
        _chain : function(name)
        {
            var tmp = chainify(this);
            tmp._chain_in_progress = true;
            tmp._chain_function = name || '';
            _chain_level++;
            return tmp;
        },

        get : function(key)
        {
            return this._attributes[key];
        },

        set : function(key, value)
        {
            this._attributes[key] = value;
            return this;
        },

        attr : function(prop)
        {
            for ( var key in prop)
                this._attributes[key] = prop[key];
            return this;
        },

    };

    // Make global
    window.chainify = chainify;
})(window);



var myObject = window.chainify({

    // f1() function is using _chain()
    f1 : function(s)
    {
        // Do something
        this.set('s1', s);
        if (this._chain_in_progress) alert('f1 after ' + this._chain_function);

        // return the chain by calling this._chain()
        return this._chain('f1');
    },

    // f2() function is using _chain()
    f2 : function(s)
    {
        this.set('s2', s);
        if (this._chain_in_progress) alert('f2 after ' + this._chain_function);
        return this._chain('f1');
    },

    // that() function is not using _chain(), but we return this so the chaining
    // is not broken
    that : function(s)
    {
        // Do something
        return this;
    }
});


// Check if the f1 function is working
myObject.f1('a'); // Set s1 to "a"
alert(myObject.get('s1')); // should be "a"

// check if the f2 chaining is working
myObject.f1('b').f1('c'); // f1 after f1
alert(myObject.get('s1')); // should be "c" -> changed on last f1 function

// Check if the f2 function is working
myObject.f2('a');
alert(myObject.get('s2')); // should be "a"

// check if the f2 and f1 chaining is working
myObject.f2('b').f1('c').f1('d').f2('e'); // f1 after f2, f1 after f1 ...

alert(myObject.get('s1')); // should be "d" -> changed on last f1 function
alert(myObject.get('s2')); // should be "e" -> changed last f2 function

// check the chain with that() -
myObject.that('b').f1('a').f1('z'); // f1 chained after f1
alert(myObject.get('s1')); // should be "z" -> changed on last f1 function