詳細なJS関数カリー化を[ターン]

あなたは事実を発見した後に知ったとき、私は、実際には、カリー化、またはアルゴリズムに関連するブログはカリー化機能を言及見たときに、その単語を見て、非常にハイエンドを感じる言葉を見たのは初めて、これは、高階関数の特殊な使用です。それは本当に役割が有用であることが名前を持っている必要がありますどのように高に関係なくのようなものです。

まず、カリー化されて最終的に何を見て?


维基百科上说道:柯里化,英语:Currying(果然是满满的英译中的既视感),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

少し抽象的なこの説明を見て、我々は、追加機能は、単純な実装を行い、数多くの例で作られたました。


// 普通的add函数
function add(x, y) {
    return x + y
}

// Currying后
function curryingAdd(x) {
    return function (y) {
        return x + y
    }}

add(1, 2)           // 3
curryingAdd(1)(2)   // 3

実際に追加xの関数であり、最初の2つのパラメータを持つYは、関数がパラメータを処理するために、受信したxとyの関数を返すとなります。今は比較的明確であると考え、それを呼び出す関数に渡される引数の一部でしかありません、それは残りのパラメータを処理するための関数を返します。

しかし、ここではそんなに面倒な努力をカプセル化し、最後にそれが何を使用ですか?いいえ良いプログラマは、私たちがドロップすることは不可能である多くの事をやりたいん、この人生は不可能です。

カリー化を行にそれが良い何ですか?

  1. 多重化パラメータ

// 正常正则验证字符串 
reg.test(txt)

// 函数封装后
function check(reg, txt) {
    return reg.test(txt)
}

check(/\d+/g, 'test')       //false
check(/[a-z]+/g, 'test')    //true

// Currying后
function curryingCheck(reg) {
    return function(txt) {
        return reg.test(txt)
    }}

var hasNumber = curryingCheck(/\d+/g)var hasLetter = curryingCheck(/[a-z]+/g)

hasNumber('test1')      // true
hasNumber('testtest')   // false
hasLetter('21212')      // false

上記の例では、それを確認するために、直接呼び出しのための定期的なチェック、正常な機能ですが、多くの場所は、私は数があるかどうかを確認する必要がある場合は、実際には、最初の引数は多重REGにする必要があり、そうではないがあります場所は、それを呼び出すためのパラメータを再利用できるように、直接hasNumber、hasLetterおよび他の機能と呼ばれ、より便利にすることができます。

  1. 事前に確認してください

var on = function(element, event, handler) {
    if (document.addEventListener) {
        if (element && event && handler) {
            element.addEventListener(event, handler, false);
        }
    } else {
        if (element && event && handler) {
            element.attachEvent('on' + event, handler);
        }
    }}

var on = (function() {
    if (document.addEventListener) {
        return function(element, event, handler) {
            if (element && event && handler) {
                element.addEventListener(event, handler, false);
            }
        };
    } else {
        return function(element, event, handler) {
            if (element && event && handler) {
                element.attachEvent('on' + event, handler);
            }
        };
    }})();

//换一种写法可能比较好理解一点,上面就是把isSupport这个参数给先确定下来了
var on = function(isSupport, element, event, handler) {
    isSupport = isSupport || document.addEventListener;
    if (isSupport) {
        return element.addEventListener(event, handler, false);
    } else {
        return element.attachEvent('on' + event, handler);
    }}

私たちは、しかし、上に書いた最初は比較的一般的であり、一般的なDOM操作のいくつかは、その後も言えるプロジェクトプロセス、パッケージを行っているが、我々は第二の文言を見て、それは比較的最初の書き込みで自己実行され、新しいを返します。この関数は、これは実際のものは、すべての裁判官を避けることができます事前に決め方法です。

  1. 遅延操作

Function.prototype.bind = function (context) {
    var _this = this
    var args = Array.prototype.slice.call(arguments, 1)
 
    return function() {
        return _this.apply(context, args)
    }
}

同様に、これらの点のメリットが言った、。カリー化され、頻繁に達成するためのメカニズムを使用し、私たちのjsを結合して、問題がある見つけ、各ユースカリー化は、基本的な機能を変更するためにしなければならないでしょう

パッケージの普遍的な方法はありませんか?


// 初步封装var currying = function(fn) {
    // args 获取第一个方法内的全部参数
    var args = Array.prototype.slice.call(arguments, 1)
    return function() {
        // 将后面方法里的全部参数和args进行合并
        var newArgs = args.concat(Array.prototype.slice.call(arguments))
        // 把合并后的参数通过apply作为fn的参数并执行
        return fn.apply(this, newArgs)
    }
}

まず、ここでのパッケージは、必要に応じて、最後に実行機能をカリー化、残りの引数を取得することでスプライスし、生き残るために閉鎖による予備的な、初期パラメータです。

しかし、何がこのような状況が思うだろう一見一般、(マルチパラメータの呼び出しをサポートしていません)をサポートしていません、これはこの場合だけでは(A)(B)(C)をカリー化、マルチパラメータ展開、実際にあれば返すことができ、いくつかの欠陥を持っているようですその後、再帰的なカプセル化層を使用しました。


// 支持多参数传递function progressCurrying(fn, args) {

    var _this = this
    var len = fn.length;
    var args = args || [];

    return function() {
        var _args = Array.prototype.slice.call(arguments);
        Array.prototype.push.apply(args, _args);

        // 如果参数个数小于最初的fn.length,则递归调用,继续收集参数
        if (_args.length < len) {
            return progressCurrying.call(_this, fn, _args);
        }

        // 参数收集完毕,则执行fn
        return fn.apply(this, _args);
    }
}

実際には、ここでは予備的基礎であり、プラス再帰呼び出しは、限り、パラメータの数が初期fn.length未満であると、再帰を実装していきます。

メリットは終了し、一般的な方法もなっている、のは、パフォーマンスのカレーに焦点を当ててみましょう

いくつかのパフォーマンスの問題のカレー限り、あなたは同じ被写体に以下の4点を知っています:

  • アクセスは、引数が遅いと命名されるアクセスパラメータにオブジェクトより通常優れています
  • 実現arguments.lengthを、ブラウザの一部の古いバージョンではかなり遅いです
  • (...)fn.apply使用しfn.call(...)は、fnを直接呼び出し(...)少し遅くなるよりも通常高く、
  • ネストされたスコープとクロージャ機能を大量に作成すると、いずれかのメモリや速度に、支出をもたらします

実際には、ほとんどのアプリケーションにおいて、主な性能ボトルネックは、性能損失は基本的に無視できるので、カレーは、直接使用を保証することができるJS操作DOMノード、です。

最後に、古典的な顔の質問を拡張




// 实现一个add方法,使计算结果能够满足如下预期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;

function add() {
    // 第一次执行时,定义一个数组专门用来存储所有的参数
    var _args = Array.prototype.slice.call(arguments);

    // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
    var _adder = function() {
        _args.push(...arguments);
        return _adder;
    };

    // 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
    _adder.toString = function () {
        return _args.reduce(function (a, b) {
            return a + b;
        });
    }
    return _adder;
 }

add(1)(2)(3)                // 6
add(1, 2, 3)(4)             // 10
add(1)(2)(3)(4)(5)          // 15
add(2, 6)(1)                // 9

リファレンス情報

この記事はから再生されhttps://www.jianshu.com/p/2975c25e4d71

おすすめ

転載: www.cnblogs.com/gehai/p/11506385.html