Functional JavaScript:如何实现Function.prototype.not

Functional JavaScript:如何实现Function.prototype.not,javascript,functional-programming,Javascript,Functional Programming,今天早些时候,我正在编写一些代码,这时我意识到,“嘿!如果我将布尔值的概念从匿名函数中抽象出来,并将其转换为原型函数,那么这段代码将更加简洁和语义化……” 考虑一个谓词生成器: function equalTo(n) { return function(x) { return n==x; }; } 因此,您可以这样使用它: [1,2,3,4,1,2,3,4].filter(equalTo(2)) == [2,2] 现在,我的想法是做一个谓词“inverser”

今天早些时候,我正在编写一些代码,这时我意识到,“嘿!如果我将布尔值的概念从匿名函数中抽象出来,并将其转换为原型函数,那么这段代码将更加简洁和语义化……”

考虑一个谓词生成器:

function equalTo(n) {
    return function(x) {
        return n==x;
    };
}
因此,您可以这样使用它:

[1,2,3,4,1,2,3,4].filter(equalTo(2)) == [2,2]
现在,我的想法是做一个谓词“inverser”:

所以你可以说:

[1,2,3,4,1,2,3,4].filter(equalTo(2).not) == [1,3,4,1,3,4]
我的第一次尝试可能非常幼稚:

Function.prototype.not = function () {
    return ! this(arguments);
}
也许还有它不起作用的原因

您将如何实现此功能,为什么?


我只是想了解一些功能性的想法,对JavaScript有足够的了解,知道它可以用来做这件事,但不知道如何做。

您的实现无法工作,原因有几个:

  • 您需要返回一个函数,而不是布尔值
  • 您应该按原样传递参数,而不是封装在数组中
  • 您应该保留调用该函数的上下文(
    this
    关键字)
我会这样实施:

Function.prototype.not = function (context) {
    var func = this;
    return function() { return !func.apply(context || this, arguments); };
}
  • 我返回一个匿名函数(
    function(){…}
  • 我调用
    apply
    在当前上下文中使用实际参数调用原始函数
  • 编辑)免费奖金:我添加了一个可选的
    上下文
    参数,该参数将覆盖回调的

    • 我可能会这样做(但可能需要某种名称空间):


      利用你的想法:

      function equalTo(n) {
          var fn = function(x) {
              return n == x;
          };
          fn.not = function(x) {
              return n != x; // use this for simpler conditions
              return !fn.apply(this, arguments); // use this for more complex logic
          }
          return fn;
      }
      
      因此,您的示例将起作用:

      [1,2,3,4,1,2,3,4].filter(equalTo(2).not) == [1,3,4,1,3,4]
      
      编辑:您可以编写一个助手函数(更好的名称可以找到),这样就不需要每次都重新定义
      而不是

      function generateFnWithNot(fn) {
          return function () {
              var f = fn.apply(this, arguments);
              f.not = function () {
                  return !f.apply(this, arguments);
              }
              return f;
          };
      }
      
      因此,在定义函数时,可以将其定义为普通函数,但包装
      generateFnWithNot
      调用除外:

      var equalTo = generateFnWithNot(function (n) {
          return function (x) {
              return n == x;
          };
      });
      
      equalTo(5) // resolves to function () { return n == 5; }
      equalTo(5).not // resolves to function () { return n != 5; } 
      

      很有启发性。谢谢。由于
      not
      是一个函数,您可以使用
      equalTo(2).not()
      调用它。最好将返回的函数指定为原型,这样您就可以调用equalTo.not()而不是equalTo.not()-例如function.prototype.not=(function(context){…})@卢克:那不行,因为呼叫站点的上下文是错误的。(当调用原型中的函数时,您将无法知道访问属性的是哪个函数)您可以使用
      对象。defineProperty
      ,但是@SLaks-“not”原型化为从equalTo返回的匿名函数,因此映射equalTo(2)。不起作用。我认为问题在于我最初的示例认为用法应该是equalTo.not(2),但它不是,它应该是,在直接调用中,equalTo(2)。not(variableToCheck)。anon函数的上下文保持不变。虽然我大体上同意您正在做的事情,但我觉得这是一种面向对象的方法,而不是功能性的方法。函数方法类似于not(equalTo(2))来创建一个逆函数predicate@Luke:我同意。我来自一个非常面向对象的背景,所以我想这只是代表了我从面向对象到函数式编程的转变…我完全一样,所以我完全理解:)在一个不相关的注释上-确保你查看clojure和coffeescript(事实上,我想你会喜欢coffeescript),我一定会看看这些。我以前看过一两次咖啡剧本,但都没想过。我想我会仔细检查一下。我喜欢这种方法。这是一个有趣的想法,因为
      not
      的范围是
      equalTo
      。唯一的绊脚石是它不能与其他函数很好地组合,因为您必须为每个函数重新定义
      not
      。我编辑了我的答案,以包括一个将向通用函数添加
      not
      的帮助函数,所以现在不需要为每个函数重新定义
      not
      ——只需要一个函数调用。
      [1,2,3,4,1,2,3,4].filter(equalTo(2).not) == [1,3,4,1,3,4]
      
      function generateFnWithNot(fn) {
          return function () {
              var f = fn.apply(this, arguments);
              f.not = function () {
                  return !f.apply(this, arguments);
              }
              return f;
          };
      }
      
      var equalTo = generateFnWithNot(function (n) {
          return function (x) {
              return n == x;
          };
      });
      
      equalTo(5) // resolves to function () { return n == 5; }
      equalTo(5).not // resolves to function () { return n != 5; }