5
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()也可能返回之后实际调用的函数。

4

9 回答 9

6

从技术上讲,您永远无法提前知道在当前调用之后是否有另一个调用链接——这显然没有意义,因为这意味着您知道某些代码在调用之前将被调用。如果没有预编译器,你就无法做到这一点,我猜这不是你想要的。

相反,可以检查在当前调用之前是否有先前的调用链接。这只需要你在对象中保留一些关于先前调用的状态,并在你调用它的新函数时更新它。如果您只使用一个调用链,您可以通过在返回对象之前创建func1func2更改对象的某些状态来做到这一点。this

如果要在同一个对象上调用多个链,则面临如何检测链末端的问题。为此,您需要让每个链式函数返回一个围绕 original的包装器this,该包装器将存储有关先前调用的状态。

如果您使用包装器方法,则obj.func1().func2()调用func1on obj,但func2在从返回的包装器上调用,func1并且此包装器可以知道先前的func1调用。如果您稍后调用obj.func2().func1()thenfunc2现在调用,objfunc1在知道先前func2调用的包装器上调用,等等。

于 2012-10-01T19:36:51.463 回答
3

(注意:此答案最初由 Scholle 作为问题的一部分发布。我将其从问题中提取为实际答案,因为它本来应该放在首位。这不是我的解决方案,因此我将其标记为社区维基。)

Scholle最终创建了一个可以满足他需求的图书馆。
在 GitHub 上可用,一些文档在这里

简而言之:获取任意 JavaScript 函数并“链化”它:

var Model = function() {};

Model.prototype.func1 = function() {
  console.log('func1 has ' + this.c_getPredecessors().length + ' preceding functions');
  return this.c_delay().c_chain(function() { 
    console.log('func1 has ' + this.c_getSuccessors().length + ' succeeding functions');     
    console.log('func1 processing...');
    this.c_next();
  });
};

Model.prototype.func2 = function() {
  console.log('func2 has ' + this.c_getPredecessors().length + ' preceding functions');
  return this.c_delay().c_chain(function() {
    console.log('func2 has ' + this.c_getSuccessors().length + ' succeeding functions');     
    console.log('func2 processing...');
    this.c_next();
  });
};

将其链化并实例化,并调用一些函数:

chainify(Model);

var Obj = new Model();
Obj.func1().func2();

控制台输出:

func1 has 0 preceding functions
func2 has 1 preceding functions
func1 has 1 succeeding functions
func1 processing...
func2 has 0 succeeding functions
func2 processing... 

当然,这是一个简单的例子。它只是演示了每个函数现在都能够访问有关当前函数调用之前和之后发生的事情的信息。

于 2017-08-12T14:40:40.090 回答
1

不,这是不可能的。

它在语义上等同于:

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

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

你能做到的最好的事情是func2检测是否func1以前被调用过,但要让它按照你描述的方式工作,就需要func1能够预测未来。

于 2012-10-01T19:27:03.403 回答
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)。您可能需要重新考虑如何处理此问题。

于 2012-10-01T19:29:14.547 回答
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/

于 2012-10-01T19:29:52.463 回答
0

你为什么想做这个?

撇开这个问题不谈,您可以不返回实际对象,而是对其进行克隆,并添加一个属性来告诉您它是该对象的返回版本。这是我能想到的唯一方法。不过听起来很复杂,具体取决于这个对象的复杂程度。

就像是:

func1 : function() {
  // some code
  if (this._hasChainedFunc()) {
     // block should be CALLED
  }
  return deepCloneWithFlag(this);
},
_hasChainedFunc : function() {
   return this.flag;
}
于 2012-10-01T19:31:11.960 回答
0

没有。这行不通。你可能会说 func1() 在某个时候在这个对象上被调用过,但是你不能知道它是什么时候被调用的,即在 func2之前

例如这个:

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

相当于您的示例调用。而且 func1 不可能知道 func2 将来会被调用。

我用链函数文档)解决了一个与此类似的问题,这允许真正的函数链,能够“向前看”以查看链中的内容。

于 2012-10-01T19:36:29.857 回答
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();
于 2012-10-01T19:43:51.810 回答
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
于 2013-02-08T19:53:06.397 回答