ホームページ > ウェブフロントエンド > jsチュートリアル > JavaScriptにおける関数の合理化について詳しく解説

JavaScriptにおける関数の合理化について詳しく解説

青灯夜游
リリース: 2020-12-18 17:53:58
転載
6531 人が閲覧しました

JavaScriptにおける関数の合理化について詳しく解説

関連する推奨事項: 「JavaScript ビデオ チュートリアル

最近、コミュニティのテクノロジー ブログを読んでいたときに、偶然 Function Currie を目にしました という単語を変更し、手書きの js 関数カリー化 も要求してください。カリー化とはどのような高度なことなのでしょうか?聞いたことがない?

私は問題を提起し、それを具体的に研究し、いくつかの整理を行いました。

関数カリー化とは何ですか?

関数カリー化とは何ですか?まず、Wikipedia がどのように説明しているかを見てみましょう:

コンピュータ サイエンスでは、カリー化 (英語: カリー化) は、カリー化またはカリー化とも訳され、複数のパラメーターを受け入れる関数を 1 つのパラメーターを受け入れる関数に変換することです。単一のパラメーター (元の関数の最初のパラメーター) を持つ関数を受け取り、残りのパラメーターを受け入れて結果を返す新しい関数を返します。

この手法は、Moses Schönfinkel と Gottlob Frege によって発明されましたが、論理学者の Haskell Gary にちなんで Christopher Strachey によって命名されました。

直観的には、カリー化は「いくつかのパラメーターを修正すると、残りのパラメーターを受け入れる関数が得られる」と述べています。したがって、2 つの変数を持つ関数 y^x の場合、y=2 が固定されている場合、1 つの変数を持つ関数 2^x が得られます。

カリー化の概念は実際には複雑ではありません。わかりやすく言うと、パラメーターの一部を関数に渡して呼び出すだけで、残りは関数を返して処理させるだけです。パラメーター。

テキストの説明がまだ少し抽象的である場合は、add 関数を使用して簡単な関数のカリー実装を作成します。

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

// add函数柯里化后
var curryingAdd = function(x) {
  return function(y) {
    return x + y;
  };
};

// 函数复用
var increment = curryingAdd(1);
var addTen = curryingAdd(10);

increment(2);
// 3
addTen(2);
// 12
ログイン後にコピー

実際、add 関数の x パラメーターと y パラメーターは、最初に関数を使用して x を受け取り、次に y パラメーターを処理する関数を返すように変更されています。ここで、アイデアはより明確になります。つまり、パラメータの一部だけを渡して関数を呼び出し、残りのパラメータを処理する関数を返すようにするということです。

なぜ関数をカリー化する必要があるのでしょうか?

add 関数のカリー化に関する上記の説明を読んだ後、これをカプセル化するのに多大な労力を費やすことに何の意味があるのか​​という疑問が生じます。

1. パラメーターの再利用

実際、add 関数の最初のカリー化の例には、関数のカリー化によってもたらされる関数の再利用がすでに含まれています。 add 関数のカリー化を通じて、increment 関数と addTen 関数をすばやく実装します。例を見てみましょう:

// 正常正则验证字符串 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
ログイン後にコピー

上記の例は通常の検証です。通常、check を直接呼び出します。この関数は問題ありませんが、数字があるかどうかを確認する必要がある場所がたくさんありますが、実際には最初のパラメータ reg を再利用する必要があるため、他の場所で hasNumber、hasLetter、その他の関数を直接呼び出してパラメータを再利用できるようになります。電話。

2.事前確認
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 操作をカプセル化することが非常に一般的です。上記の最初の記述方法も比較的一般的ですが、2 番目の記述方法を見てみましょう。それは比較的異なります。最初の記述方法は、自己実行してから を返すことです。新しい関数は、実際にはどのメソッドを使用するかを事前に決定し、毎回判断することを回避します。

3. 遅延計算・演算
Function.prototype.bind = function (context) {
    var _this = this
    var args = Array.prototype.slice.call(arguments, 1)

    return function() {
        return _this.apply(context, args)
    }
}
ログイン後にコピー
弊社のjsでよく使われるbindと同様、実装機構はCurryingです。機能柯麗華?

一般的なカプセル化方法:

// 初步封装
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)
    }
}
ログイン後にコピー

ここでは、最初に予備的なカプセル化を示します。クロージャを通じて予備的なパラメータを保存し、次に残りの引数を取得してそれらを結合し、最後にカリー化に必要な関数を実行します。

しかし、上記の関数にはまだいくつかの欠陥があります。この方法で返された場合、もう 1 つのパラメータしか拡張できません。Currying(a)(b)(c) はサポートされていないようです (マルチパラメータ呼び出しはサポートされていません)、一般的にこの場合は、再帰を使用してさらに 1 層カプセル化することを考えるでしょう。

// 支持多参数传递
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 よりも小さい限り、再帰は実行され続けます。

関数カリー化のパフォーマンスはどのようなものですか?

カリー化のパフォーマンスに関しては、次の点を知っておく必要があります。

引数オブジェクトへのアクセスは、通常、名前付きパラメータへのアクセスよりも少し遅くなります。

    一部の古いバージョンでは、ブラウザでの argument.length の実装は非常に遅いです。
  • fn.apply(…) と fn.call(…) を使用すると、通常、fn(…) を直接呼び出すよりもわずかに遅くなります
  • 大きなネストされたスコープとクロージャー関数の数は、メモリと速度の両方の面でコストをもたらします。
  • 実際、ほとんどのアプリケーションでは、主なパフォーマンスのボトルネックは DOM ノードの操作であり、この JS のパフォーマンス損失は次のとおりです。基本的には微量ですので、カレーをそのまま安全にご使用いただけます。
精緻な面接の質問

// 实现一个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
ログイン後にコピー
概要

いくつかのパラメーターを渡すだけで、便利な新しい関数を動的に作成でき、さらに追加の利点ももたらします。複数のパラメーターがあっても、数学関数の定義は保持されます。

Currying 機能はとても使いやすく、毎日使うのがとても楽しいです。これは、関数型プログラミングを面倒で退屈なものにするために手元に置いておくべき不可欠なツールです。

プログラミング関連の知識について詳しくは、プログラミング入門をご覧ください。 !

以上がJavaScriptにおける関数の合理化について詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:segmentfault.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート