【问题标题】:Copying underscore's chain method style复制下划线的链式方法样式
【发布时间】:2015-07-22 00:46:09
【问题描述】:

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

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 中拥有最少多少代码才能完成这项工作?

【问题讨论】:

  • 哇,刚刚发现underscore loops over itself创建了所有prototypes
  • 我想我错了,它只是将数组操作添加到下划线库中。
  • 你的函数有在做什么吗?如果它们没有副作用,您可以完全省略对它们的调用。他们不应该至少改变 cat 实例吗?目前,您的函数只返回一些数字。下一个函数返回一个不同的数字,没有任何关联。链接在这里似乎毫无用处。
  • @Bergi 与它们相关的是它们被链接起来,就像下划线一样。前一个函数值通过管道传递到下一个函数。这就是 underscore 的作用,它产生的调用更加清晰,然后为每个函数返回值。 Here's a primer on underscore chaining.
  • 不,这就是我要指出的:在您的示例函数中没有什么可以管道的。您的函数既不会返回可以调用哪些方法的猫,也不会返回可以作为参数传递给下一个函数的结果。

标签: javascript oop object chaining


【解决方案1】:

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

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 用于不同的目的(并在之后忽略它),您需要重新考虑您的程序算法

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

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() 将返回值,存储在能量中

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

你可以尝试使用下划线reduce函数:

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

不过好像有点多余

【讨论】:

  • 下划线不会在每个函数调用结束时返回this_cat,因为它应该返回操作的值。我发现它创建了所有以编程方式执行的原型。
  • 您必须将所有方法复制到一个新的 cat 对象中:S,以及结果
  • 每个下划线函数似乎都没有复制任何东西,它只是seems to return the result
  • 我只希望每个函数都返回一个值,并且可以链接。我不希望 chanablity 依赖于是否有争论。这是制作函数的一种草率方式。
  • 您不能对返回值的函数进行链式调用。为此,函数必须返回一个对象。那是一个 JavaScript 规则。您能否发布您所指的下划线函数调用的示例,以便我可以得到一张图片,您正在创建什么模式。
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

    我为此创建了一个 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
    

    【讨论】:

      猜你喜欢
      • 2020-02-27
      • 2013-06-29
      • 1970-01-01
      • 2018-04-01
      • 1970-01-01
      • 2023-04-04
      • 1970-01-01
      • 2014-06-13
      • 2013-03-07
      相关资源
      最近更新 更多