ホームページ ウェブフロントエンド jsチュートリアル 関数型 JavaScript: .apply()、.call()、および引数オブジェクト

関数型 JavaScript: .apply()、.call()、および引数オブジェクト

Dec 14, 2016 am 09:21 AM

私たちは、真の関数型プログラミングを実行できるように JavaScript を調整する方法を探しています。そのためには、関数呼び出しと関数プロトタイプを詳しく理解する必要があります。

関数プロトタイプ

さて、上にリンクされた記事を読んでも無視しても、次に進む準備は完了です。
お気に入りのブラウザ + JavaScript コンソールをクリックしたら、Function.prototype オブジェクトのプロパティを見てみましょう:

; html-script: false ]
Object.getOwnPropertyNames(Function.prototype)
//=> ["length", "name", "arguments", "caller", 
//    "constructor", "bind", "toString", "call", "apply"]
ログイン後にコピー

ここでの出力は、使用しているブラウザと JavaScript のバージョンによって異なります。 (Chrome 33 を使用しています)
興味のあるプロパティがいくつか表示されます。この記事では、次について説明します。

Function.prototype.length

Function.prototype.call

Function.prototype.apply

最初のものはプロパティで、他の 2 つはメソッドです。これら 3 つに加えて、(非推奨になった) Function.prototype.arguments とは少し異なるこの特殊な変数引数についても説明したいと思います。

まず、何が起こっているのかを把握するために「テスター」関数を定義します。

; html-script: false ]
var tester = function (a, b, c){
    console.log({
        this: this,
        a: a,
        b: b,
        c: c
    });
};
ログイン後にコピー

この関数は、入力パラメータの値とその値である「コンテキスト変数」を記録するだけです。
さて、何か試してみましょう:

; html-script: false ]
tester("a"); 
//=> {this: Window, a: "a", b: (undefined), c: (undefined)}
 
tester("this", "is", "cool"); 
//=> {this: Window, a: "this", b: "is", c: "cool"}
ログイン後にコピー

2 番目と 3 番目のパラメータを入力しないと、プログラムはそれらを未定義として表示することに気付きました。さらに、この関数のデフォルトの「コンテキスト」がグローバル オブジェクト ウィンドウであることに注意してください。

Function.prototype.call を使用する

関数の .call メソッドは、コンテキスト変数 this を最初の入力パラメーターの値に設定し、他のパラメーターを 1 つずつ渡す方法でこの関数を呼び出します。関数を入力してください。
構文:

; html-script: false ]
fn.call(thisArg[, arg1[, arg2[, ...]]])
ログイン後にコピー

したがって、次の 2 行は同等です:

; html-script: false ]
tester("this", "is", "cool"); 
tester.call(window, "this", "is", "cool");
ログイン後にコピー

もちろん、必要に応じて任意のパラメーターを渡すことができます:

; html-script: false ]
tester.call("this?", "is", "even", "cooler"); 
//=> {this: "this?", a: "is", b: "even", c: "cooler"}
ログイン後にコピー

このメソッドの主な機能は、関数のこの変数の値を設定することです。あなたが呼ぶ 。

Function.prototype.applyを使用する

関数の.applyメソッドは、.callよりも実用的です。 .call と同様に、.apply も、コンテキスト変数 this を入力パラメーター シーケンスの最初のパラメーターの値に設定することによって呼び出されます。入力パラメーター シーケンスの 2 番目と最後のパラメーターは、配列 (または配列のようなオブジェクト) として渡されます。

構文:

; html-script: false ]
fun.apply(thisArg, [argsArray])
ログイン後にコピー

したがって、次の 3 行はすべて同等です:

; html-script: false ]
tester("this", "is", "cool"); 
tester.call(window, "this", "is", "cool"); 
tester.apply(window, ["this", "is", "cool"]);
ログイン後にコピー

パラメーター リストを配列として指定できることは、ほとんどの場合非常に便利です (そうすることの利点は後でわかります)。

たとえば、Math.max は可変個引数関数です (関数は任意の数のパラメーターを受け入れることができます)。

; html-script: false ]
Math.max(1,3,2);
//=> 3
 
Math.max(2,1);
//=> 2
ログイン後にコピー

それでは、数値の配列があり、Math.max 関数を使用して最大のものを見つける必要がある場合、1 行のコードでこれを行うにはどうすればよいでしょうか?

; html-script: false ]
var numbers = [3, 8, 7, 3, 1];
Math.max.apply(null, numbers);
//=> 8
ログイン後にコピー

.apply メソッドは、特別な引数変数、つまり引数オブジェクト

と組み合わせると、その重要性が実際に分かり始めます。

すべての関数式には、そのスコープで使用できる特別なローカル変数、つまり引数があります。そのプロパティを調べるために、別のテスター関数を作成しましょう:

; html-script: false ]
var tester = function(a, b, c) {
    console.log(Object.getOwnPropertyNames(arguments));
};
ログイン後にコピー

注: この場合、引数には列挙可能としてマークされていないいくつかのプロパティがあるため、上記のように Object.getOwnPropertyNames を使用する必要があります。そのため、コンソールを使用するだけです。 log(arguments) この方法では、それらは表示されません。

ここで、古い方法に従い、テスター関数を呼び出してテストします。

; html-script: false ]
tester("a", "b", "c");
//=> ["0", "1", "2", "length", "callee"]
 
tester.apply(null, ["a"]);
//=> ["0", "length", "callee"]
ログイン後にコピー

arguments 変数の属性には、関数に渡される各パラメーターに対応する属性が含まれます。これらは、.length 属性および .callee 属性と何ら変わりません。 。

.callee 属性は、現在の関数を呼び出した関数への参照を提供しますが、これはすべてのブラウザーでサポートされているわけではありません。今のところ、このプロパティは無視します。
テスター関数を再定義して、もう少し機能を充実させましょう:

; html-script: false ]
var tester = function() {
    console.log({
        'this': this,
        'arguments': arguments,
        'length': arguments.length
    });
};
 
tester.apply(null, ["a", "b", "c"]);
//=> { this: null, arguments: { 0: "a", 1: "b", 2: "c" }, length: 3 }
ログイン後にコピー

引数: それはオブジェクトですか、それとも配列ですか?

arguments は多かれ少なかれ似ていますが、まったく配列ではないことがわかります。多くの場合、配列ではない場合でも配列として扱いたいと思うでしょう。引数を配列に変換する非常に優れたショートカット関数があります:

; html-script: false ]
function toArray(args) {
    return Array.prototype.slice.call(args);
}
 
var example = function(){
    console.log(arguments);
    console.log(toArray(arguments));
};
 
example("a", "b", "c");
//=> { 0: "a", 1: "b", 2: "c" }
//=> ["a", "b", "c"]
ログイン後にコピー

ここでは Array.prototype.slice メソッドを使用して、配列のようなオブジェクトを配列に変換します。このため、引数オブジェクトは .apply と組み合わせて使用​​すると非常に便利になります。

いくつかの役立つ例

Log Wrapper

は logWrapper 関数を構築しますが、それは単項関数でのみ正しく動作します。

; html-script: false ]
// old version
var logWrapper = function (f) {
    return function (a) {
        console.log('calling "' + f.name + '" with argument "' + a);
        return f(a);
    };
};
ログイン後にコピー

もちろん、私たちの既存の知識を使用して、任意の関数を提供できる logWrapper 関数を構築することができます:

; html-script: false ]
// new version
var logWrapper = function (f) {
    return function () {
        console.log('calling "' + f.name + '"', arguments);
        return f.apply(this, arguments);
    };
};
ログイン後にコピー

; html-script: false ]
f.apply(this, arguments);
ログイン後にコピー
我们确定这个函数f会在和它之前完全相同的上下文中被调用。于是,如果我们愿意用新的”wrapped”版本替换掉我们的代码中的那些日志记录函数是完全理所当然没有唐突感的。
把原生的prototype方法放到公共函数库中
浏览器有大量超有用的方法我们可以“借用”到我们的代码里。方法常常把this变量作为“data”来处理。在函数式编程,我们没有this变量,但是我们无论如何要使用函数的!
ログイン後にコピー
; html-script: false ]
var demethodize = function(fn){
    return function(){
        var args = [].slice.call(arguments, 1);
        return fn.apply(arguments[0], args);
    };
};
ログイン後にコピー
を呼び出すことによって: 他の例:

; html-script: false ]
// String.prototype
var split = demethodize(String.prototype.split);
var slice = demethodize(String.prototype.slice);
var indexOfStr = demethodize(String.prototype.indexOf);
var toLowerCase = demethodize(String.prototype.toLowerCase);
 
// Array.prototype
var join = demethodize(Array.prototype.join);
var forEach = demethodize(Array.prototype.forEach);
var map = demethodize(Array.prototype.map);
ログイン後にコピー

もちろん、他にもたくさんあります。これらがどのように実行されるかを見てみましょう:

; html-script: false ]
("abc,def").split(",");
//=> ["abc","def"]
 
split("abc,def", ",");
//=> ["abc","def"]
 
["a","b","c"].join(" ");
//=> "a b c"
 
join(["a","b","c"], " ");
// => "a b c"
ログイン後にコピー

余談:

実際に demethodize 関数を使用するより良い方法はパラメーターの反転であることを後で説明します。

在函数式编程情况下,你通常需要把“data”或“input data”参数作为函数的最右边的参数。方法通常会把this变量绑定到“data”参数上。举个例子,String.prototype方法通常操作的是实际的字符串(即”data”)。Array方法也是这样。

为什么这样可能不会马上被理解,但是一旦你使用柯里化或是组合函数来表达更丰富的逻辑的时候情况会这样。这正是我在引言部分说到UnderScore.js所存在的问题,之后在以后的文章中还会详细介绍。几乎每个Underscore.js的函数都会有“data”参数,并且作为最左参数。这最终导致非常难重用,代码也很难阅读或者是分析。:-(

管理参数顺序

; html-script: false ]
// shift the parameters of a function by one
var ignoreFirstArg = function (f) {
    return function(){
        var args = [].slice.call(arguments,1);
        return f.apply(this, args);
    };
};
 
// reverse the order that a function accepts arguments
var reverseArgs = function (f) {
    return function(){
        return f.apply(this, toArray(arguments).reverse());
    };
};
ログイン後にコピー

组合函数

在函数式编程世界里组合函数到一起是极其重要的。通常的想法是创建小的、可测试的函数来表现一个“单元逻辑”,这些可以组装到一个更大的可以做更复杂工作的“结构”

; html-script: false ]
// compose(f1, f2, f3..., fn)(args) == f1(f2(f3(...(fn(args...)))))
var compose = function (/* f1, f2, ..., fn */) {
    var fns = arguments,
        length = arguments.length;
    return function () {
        var i = length;
        // we need to go in reverse order
        while ( --i >= 0 ) {
            arguments = [fns[i].apply(this, arguments)];
        }
        return arguments[0];
    };
};
 
 
// sequence(f1, f2, f3..., fn)(args...) == fn(...(f3(f2(f1(args...)))))
var sequence = function (/* f1, f2, ..., fn */) {
    var fns = arguments,
        length = arguments.length;
    return function () {
        var i = 0;
        // we need to go in normal order here
        while ( i++ < length ) {
            arguments = [fns[i].apply(this, arguments)];
        }
        return arguments[0];
    };
};
ログイン後にコピー

例子:

; html-script: false ]
// abs(x) = Sqrt(x^2)
var abs = compose(sqrt, square);
 
abs(-2); // 2
ログイン後にコピー


このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

JavaScriptの文字列文字を交換します JavaScriptの文字列文字を交換します Mar 11, 2025 am 12:07 AM

JavaScript文字列置換法とFAQの詳細な説明 この記事では、javaScriptの文字列文字を置き換える2つの方法について説明します:内部JavaScriptコードとWebページの内部HTML。 JavaScriptコード内の文字列を交換します 最も直接的な方法は、置換()メソッドを使用することです。 str = str.replace( "find"、 "置換"); この方法は、最初の一致のみを置き換えます。すべての一致を置き換えるには、正規表現を使用して、グローバルフラグGを追加します。 str = str.replace(/fi

独自のAjax Webアプリケーションを構築します 独自のAjax Webアプリケーションを構築します Mar 09, 2025 am 12:11 AM

それで、あなたはここで、Ajaxと呼ばれるこのことについてすべてを学ぶ準備ができています。しかし、それは正確には何ですか? Ajaxという用語は、動的でインタラクティブなWebコンテンツを作成するために使用されるテクノロジーのゆるいグループ化を指します。 Ajaxという用語は、もともとJesse Jによって造られました

独自のJavaScriptライブラリを作成および公開するにはどうすればよいですか? 独自のJavaScriptライブラリを作成および公開するにはどうすればよいですか? Mar 18, 2025 pm 03:12 PM

記事では、JavaScriptライブラリの作成、公開、および維持について説明し、計画、開発、テスト、ドキュメント、およびプロモーション戦略に焦点を当てています。

ブラウザでのパフォーマンスのためにJavaScriptコードを最適化するにはどうすればよいですか? ブラウザでのパフォーマンスのためにJavaScriptコードを最適化するにはどうすればよいですか? Mar 18, 2025 pm 03:14 PM

この記事では、ブラウザでJavaScriptのパフォーマンスを最適化するための戦略について説明し、実行時間の短縮、ページの負荷速度への影響を最小限に抑えることに焦点を当てています。

ブラウザ開発者ツールを使用してJavaScriptコードを効果的にデバッグするにはどうすればよいですか? ブラウザ開発者ツールを使用してJavaScriptコードを効果的にデバッグするにはどうすればよいですか? Mar 18, 2025 pm 03:16 PM

この記事では、ブラウザ開発者ツールを使用した効果的なJavaScriptデバッグについて説明し、ブレークポイントの設定、コンソールの使用、パフォーマンスの分析に焦点を当てています。

jQueryマトリックス効果 jQueryマトリックス効果 Mar 10, 2025 am 12:52 AM

マトリックスの映画効果をあなたのページにもたらしましょう!これは、有名な映画「The Matrix」に基づいたクールなJQueryプラグインです。プラグインは、映画の古典的な緑色のキャラクター効果をシミュレートし、画像を選択するだけで、プラグインはそれを数値文字で満たされたマトリックススタイルの画像に変換します。来て、それを試してみてください、それはとても面白いです! それがどのように機能するか プラグインは画像をキャンバスにロードし、ピクセルと色の値を読み取ります。 data = ctx.getimagedata(x、y、settings.greasize、settings.greasize).data プラグインは、写真の長方形の領域を巧みに読み取り、jQueryを使用して各領域の平均色を計算します。次に、使用します

シンプルなjQueryスライダーを構築する方法 シンプルなjQueryスライダーを構築する方法 Mar 11, 2025 am 12:19 AM

この記事では、jQueryライブラリを使用してシンプルな画像カルーセルを作成するように導きます。 jQuery上に構築されたBXSLiderライブラリを使用し、カルーセルをセットアップするために多くの構成オプションを提供します。 今日、絵のカルーセルはウェブサイトで必須の機能になっています - 1つの写真は千の言葉よりも優れています! 画像カルーセルを使用することを決定した後、次の質問はそれを作成する方法です。まず、高品質の高解像度の写真を収集する必要があります。 次に、HTMLとJavaScriptコードを使用して画像カルーセルを作成する必要があります。ウェブ上には、さまざまな方法でカルーセルを作成するのに役立つ多くのライブラリがあります。オープンソースBXSLiderライブラリを使用します。 BXSLiderライブラリはレスポンシブデザインをサポートしているため、このライブラリで構築されたカルーセルは任意のものに適合させることができます

Angularを使用してCSVファイルをアップロードおよびダウンロードする方法 Angularを使用してCSVファイルをアップロードおよびダウンロードする方法 Mar 10, 2025 am 01:01 AM

データセットは、APIモデルとさまざまなビジネスプロセスの構築に非常に不可欠です。これが、CSVのインポートとエクスポートが頻繁に必要な機能である理由です。このチュートリアルでは、Angular内でCSVファイルをダウンロードおよびインポートする方法を学びます

See all articles