复制下划线的链方法样式

Copying underscore's chain method style

在下面的函数cat中你可以直接调用eatplay函数而无需实例化一个新的cat对象,这通过传入值来演示从函数 cat.eat 进入 cat.play 函数以获取状态。

var cat = function(obj){
  // if (obj instanceof cat) return obj;
  // if (!(this instanceof cat)) return new cat(obj);
  // this.catwrapped = obj;
}

cat.eat = function(food){
  if(food == "tuna") return 95
  if(food == "milk") return 35
  return 0
}

cat.play = function(energy){
  if(energy < 50) return 0
  return 100
}

var energy  = cat.eat("tuna")
var status = cat.play(energy)

console.log(status) // 100

我试图保留此功能并添加下划线样式链接。因此,您可以执行以下操作。这是如何实现的?

cat.day = function(obj){
  var instance = cat(obj);
  // instance._chain = true;
  return instance;
}

var status = cat.day()
  .eat("tuna")
  .play()
  .status()

console.log(status) // should log 100

我在 catcat.day 中至少需要多少代码才能完成这项工作?

您需要 return 对象的指针才能执行链式调用:

cat.play = function(energy){
  // do something with energy
  return cat
}

cat.eat = function(food){
  // do something with food
  return cat
}

var status = cat.day()
   .eat("tuna")
   .play()
   .status()

由于您将 return 用于不同的目的(之后忽略它),您需要重新考虑您的程序算法

更新。您可以根据参数创建一个函数来执行任务或 return 数据:

var energy = 0;
cat.eat = function(v) {
    // return something on empty call
    if (!arguments.length) return energy 
    // process passed data
    energy += v
    // return cat object to allow chaincals
    return cat
}

在这种情况下,cat.eat(10) 将允许您执行链式调用,而 cat.eat() 将 return 您的值存储在能量中

更新 2:您的函数不会更改任何内部变量,因此任何数据都将在标准链式调用中丢失。你所做的,你需要一个管道并将一个函数的直接输出到另一个输入。有多种方法可以做到这一点:

您可以尝试使用下划线减少功能:

var status = _.reduce([cat.play, cat.eat], function(status, f){ return f(status); }, "tuna");

或者你可以直接打电话给他们:

cat.status(cat.play(cat.eat("tuna")))

如果你创建内部变量来存储能量,你甚至可以调用下划线链:

var cat = function() {
  this.energy = 0; 
}

cat.eat = function(food){
  if (food == "tuna") cat.energy = 95
  else if(food == "milk") cat.energy = 35
  else cat.energy = 0
}

cat.play = function(){
  if(cat.energy < 50) cat.energy = 0
  else cat.energy = 100
}

var status = _.chain(cat)
   .eat("tuna")
   .play()
   .value()
   .energy

不过好像有点多余

我复制了所有使其可链接的下划线函数,并将其添加到对象的末尾。

这个想法是您可以创建像普通函数一样运行的函数,执行某种输入/输出操作。然后,您可以遍历所有这些函数并构建涉及使对象可链接的原型,例如存储数据和传输参数。

var _ = require("underscore")

var cat = function(obj) {
  if (obj instanceof cat) return obj;
  if (!(this instanceof cat)) return new cat(obj);
  this._wrapped = obj;
};

cat.eat = function(food){
  if(food == "tuna") return 95
  if(food == "milk") return 35
  return 0
}

cat.play = function(energy){
  if(energy < 50) return 0
  return 100
}

// Add a "chain" function. Start chaining a wrapped Underscore object.
cat.chain = function(obj) {
  var instance = cat(obj);
  instance._chain = true;
  return instance;
};

// Helper function to continue chaining intermediate results.
var chainResult = function(instance, obj) {
  return instance._chain ? cat(obj).chain() : obj;
};

var ArrayProto = Array.prototype
var push = ArrayProto.push

cat.mixin = function(obj) {
  _.each(_.functions(obj), function(name) {
    var func = cat[name] = obj[name];
    cat.prototype[name] = function() {
      var args = [this._wrapped];
      push.apply(args, arguments);
      return chainResult(this, func.apply(cat, args));
    };
  });
};

// Add all of the Underscore functions to the wrapper object.
cat.mixin(cat);

// Extracts the result from a wrapped and chained object.
cat.prototype.value = function() {
  return this._wrapped;
};


// var energy  = cat.eat("tuna")
// var status = cat.play(energy)
//
// console.log(status) // 100

var status = cat.chain("tuna")
  .eat()
  .play()
  .value()

console.log(status) // 100

更新:

我创建了使任何对象成为可链接对象成为下划线混合的函数。

这是混音:

var _ = require("underscore")

_.mixin({
  prototypeChain: function(thObj){
    return function(obj) {
      var instance = thObj(obj);
      instance._chain = true;
      return instance;
    }
  },
  prototypeExtend: function(theObj){
    var chainResult = function(instance, obj) {
      return instance._chain ? theObj(obj).chain() : obj;
    }
    var prototypes = _.map(_.functions(theObj), function(name) {
      var func = theObj[name];
      theObj.prototype[name] = function() {
        var args = [this._wrapped];
        Array.prototype.push.apply(args, arguments);
        return chainResult(this, func.apply(theObj, args));
      };
    });
    _.extend(theObj, prototypes)
  },
  prototypeValue: function(){
    return function(){
      return this._wrapped;
    }
  }
})

工作原理如下:

var cat = function(obj) {
  if (obj instanceof cat) return obj;
  if (!(this instanceof cat)) return new cat(obj);
  this._wrapped = obj;
};

cat.eat = function(food){
  if(food == "tuna") return 95
  if(food == "milk") return 35
  return 0
}

cat.play = function(energy){
  if(energy < 50) return 0
  return 100
}

cat.chain = _.prototypeChain(cat)
_.prototypeExtend(cat)
cat.prototype.value = _.prototypeValue()

var energy  = cat.eat("tuna")
var status = cat.play(energy)

console.log(status) // 100

var status = cat.chain("tuna")
  .eat()
  .play()
  .value()

console.log(status) // 100

我创建了一个 npm 模块 underscore-chainable 来做到这一点。

npm install underscore-chainable

然后

var _ = require("underscore")
_.mixin(require("underscore-chainable"))

var cat = _.makeChainable() // make the `cat` object chainable

cat.eat = function(food){
  if(food == "tuna") return 95
  if(food == "milk") return 35
  return 0
}

cat.play = function(energy){
  if(energy < 50) return 0
  return 100
}

_.extendChainable(cat) // extend the chainablity

// adds `.chain` and `.value`

var energy  = cat.eat("tuna")
var status = cat.play(energy)

console.log(status) // 100

var status = cat
  .chain("tuna")
  .eat()
  .play()
  .value()

console.log(status) // 100