Функциональный JavaScript: как реализовать Function.prototype.not - PullRequest
7 голосов
/ 21 июля 2010

Я работал над некоторым кодом ранее сегодня, когда понял: «Эй! Этот код был бы более лаконичным и семантическим, если бы я абстрагировал идею логического выражения не от анонимной функции и не от функции прототипа ...»

Рассмотрим генератор предикатов:

function equalTo(n) {
    return function(x) {
        return n==x;
    };
}

Так что вы можете использовать его так:

[1,2,3,4,1,2,3,4].filter(equalTo(2)) == [2,2]

Теперь моя идея состоит в том, чтобы сделать предикат "инвертором":

Function.prototype.not = function() {
    //???
}

Итак, вы можете сказать:

[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, чтобызнаю, что это можно использовать для этого, но только не как.

Ответы [ 3 ]

7 голосов
/ 21 июля 2010

Ваша реализация не будет работать по нескольким причинам:

  • Вам нужно вернуть функцию, а не логическое значение.
  • Вы должны передавать аргументы как есть, а не в массиве.
  • Вы должны сохранить контекст (ключевое слово this), в котором была бы вызвана функция.

Я бы реализовал это так:

Function.prototype.not = function (context) {
    var func = this;
    return function() { return !func.apply(context || this, arguments); };
}
  • Я возвращаю анонимную функцию (function() { ... })
  • Я вызываю apply, чтобы вызвать исходную функцию в текущем контексте с фактическими аргументами.
  • ( EDIT ) Бесплатный бонус: я добавил необязательный параметр context, который будет переопределять this для обратного вызова.
2 голосов
/ 21 июля 2010

Я бы, наверное, сделал это так (но, может быть, с некоторым пространством имен):

function not (x) {
  return !x;
}

function id (x) {
  return x;
}

function compose (/*funcs*/) {
  var args = arguments.length
    ? Array.prototype.slice.call (arguments)
    : [id]
    ;
  return function () {
    var val = args [args.length - 1].apply (null, arguments);
    for (var i = args.length - 2; i >= 0; --i) {
      val = args [i] (val);
    }
    return val;
  };
}

[1,2,3,4,1,2,3,4].filter (compose (not, equalTo (2)));
1 голос
/ 21 июля 2010

Используя вашу идею:

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]

Редактировать : вы можете написать вспомогательную функцию (лучше назватьнайдено) поэтому not не нужно каждый раз переопределять:

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; } 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...