ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScriptの関数を詳しく解説_基礎知識

JavaScriptの関数を詳しく解説_基礎知識

WBOY
WBOYオリジナル
2016-05-16 16:31:141229ブラウズ

はじめに

多くの従来の言語 (C/C/Java/C# など) では、関数は言語キーワードを使用して宣言し、必要に応じて呼び出すことしかできません。関数をパラメータとして使用する 別の関数に渡す、ローカル変数に代入する、または戻り値として使用するには、関数ポインタやデリゲートなどの特別なメソッドを経由する必要があります。
JavaScript の世界では、関数は従来の関数の使用方法 (宣言と呼び出し) をすべて備えているだけでなく、値を割り当てたり、パラメーターを渡したり、単純な値を返したりすることもできます。このような関数は 3 番目の関数とも呼ばれます。 -クラスの関数。それだけでなく、JavaScript の関数はクラスのコンストラクターとしても機能し、Function クラスのインスタンスです。このような複数のアイデンティティにより、JavaScript 関数は非常に重要になります。

1. 初心者レベルの JavaScript 関数

他の言語と同様、JavaScript 関数は最初に宣言して後で使用するという原則に従い、関数名には文字、数字、アンダースコア、または $ のみを含めることができ、数字で始めることはできません。関数を宣言するには 2 つの一般的な方法があります:

コードをコピーします コードは次のとおりです:

// 関数 myfunc
を直接宣言します function myfunc(/* 引数 */) {
}

//匿名関数をローカル変数 myfunc
に代入します var myfunc = function(/* 引数 */) {
}

関数宣言の上記 2 つの方法には微妙な違いがあることに注意してください。最初のメソッドは、呼び出し前、呼び出し後、または実行されない位置で宣言されたかどうかに関係なく、宣言された時点では名前付き関数です。 (return ステートメントや決して true にならない分岐内など) はスコープ全体でアクセスできます。2 番目の方法は、厳密には関数宣言ではない匿名関数を変数に割り当てる方法です (関数宣言)。この関数は、代入の前にコードからアクセスすることはできません。つまり、呼び出す前に代入を完了する必要があります。そうしないと、呼び出し時にエラー「TypeError: unknown is not a function」が発生します。例:

コードをコピーします コードは次のとおりです:

myfunc1(); // myfunc1 が直接宣言されているため正常に呼び出すことができます

function myfunc1() {
}

myfunc2(); // エラー TypeError: 未定義は関数ではありません

var myfunc2 = function() {
};


関数を呼び出す基本的な方法は、かっこのペアを使用する従来の言語と同じです: myfunc()。 JavaScript 関数は直接または間接的な再帰呼び出しもサポートしています。たとえば、古典的なフィボナッチ関数は次のように JavaScript で実装できます。

コードをコピーします コードは次のとおりです:
関数 fib(n) {
If (n == 1 || n == 2) {
1 を返します。
} else {
fib(n - 2) fib(n - 1) を返します。 }
}


JavaScript の関数は可変長パラメーターを処理できます。関数内には、呼び出し時に渡されるすべてのパラメーターを含む配列のようなオブジェクトである、arguments という名前のローカル変数があります。長さ属性はパラメーターの数を示します。 。例:

コードをコピーします コードは次のとおりです:
関数 test() {
アラート(引数.長さ); }

テスト(1); // 1
テスト(1, 'a') // 2
test(true, [], {}); // 3 引数は、C 言語の printf と同様の関数を実装するために使用でき、メソッドのポリモーフィズムを実装するためにも使用できます。

2. 高度な JavaScript 関数

2.1 匿名関数と入れ子関数

JavaScript では、匿名関数と呼ばれる、名前のない関数を宣言できます。同時に、JavaScript では、入れ子関数と呼ばれる関数を関数内で宣言することもできます。入れ子関数のスコープは親関数全体です。

前の関数宣言のセクションでは、匿名関数と入れ子関数の使用について説明しました。匿名関数には名前がないため、コンテキストを汚染する新しい変数が導入されることはなく、新しい変数スコープがもたらされます。地球環境汚染を防ぐために使用されます。

JavaScript ランタイムには特別なグローバル環境 (グローバル オブジェクト) があり、このオブジェクトにはグローバル関数と変数が格納されます。実際の開発では、グローバル オブジェクト変数に重複が発生した場合に、複数のサードパーティ ライブラリや複数の JS ファイルが使用されることがよくあります。または関数宣言はコード実行時に混乱を引き起こします。たとえば、2 つの js ファイルが続けて導入され、それぞれが内部使用のために独自の関数ログを定義します。2 番目に導入された関数は最初の定義を上書きし、後続の実行でログ関数を呼び出しても問題が発生しない可能性があります。エラーの原因となります。現時点では、匿名関数を使用して js 全体にロジックをラップすると、このエラーを回避できます。この方法は、ほとんどのオープン ソース js ライブラリで使用されています。

コードをコピーします コードは次のとおりです:

(function() { // 匿名関数

関数ログ(msg) {
コンソール.ログ(メッセージ); }

// その他のコード

}()); // すぐに実行

上記のコードは単純な例であり、ログ関数のスコープはこの匿名関数に限定されており、匿名関数の外側を括弧 () で囲んで関数式を形成しています。 function の後に、関数がすぐに実行されることを示す括弧のペアが続き、元のコードが通常どおり実行できるようになります。ただし、この方法で宣言された関数や var で宣言された変数などは内部的なものであり、匿名関数以外のコードからアクセスすることはできません。いくつかの関数をインターフェースとして公開する必要がある場合、いくつかの方法があります:

コードをコピーします コードは次のとおりです:
var mylib = (関数(グローバル) {

関数ログ(msg) {
コンソール.ログ(メッセージ); }

log1 = log; // 方法 1: var を使用せずに変数宣言のデフォルトの動作を使用し、log1 をグローバル変数にします (非推奨)

global.log2 = log; // 方法 2: log2 属性をグローバル オブジェクトに直接追加し、値を log 関数に割り当てます (推奨)

return { // 方法 3: 匿名関数の戻り値を通じて一連のインターフェイス関数コレクション オブジェクトを取得し、それらをグローバル変数 mylib に割り当てます (推奨)
ログ: ログ
};

}(ウィンドウ));


2.2 高階関数


関数がパラメータまたは戻り値として使用される場合、それは高階関数と呼ばれます。これは、JavaScript のすべての関数が高階関数として使用できることです。これも、1 番目のタイプの関数の特徴です。以下では、これら 2 つの使用方法をそれぞれ分析します。

コードをコピーします コードは次のとおりです:
関数 negative(n) {
Return -n // n の反対の値を取得します
}

関数 square(n) {
n*n を返します // n の 2 乗
}

関数 process(nums, callback) {
var 結果 = [];
for(var i = 0, length = nums.length; i result[i] = callback(nums[i]); // 配列 nums 内のすべての要素を処理のためにコールバックに渡し、戻り値を結果として保存します
}

結果を返します
}

var nums = [-3, -2, -1, 0, 1, 2, 3, 4]; var n_neg = プロセス(数値, 負)
// n_neg = [3, 2, 1, 0, -1, -2, -3, -4]; var n_square = プロセス(数値, 平方)
// n_square = [9, 4, 1, 0, 1, 4, 9, 16];

上記のコードは、関数をパラメーターとして別の関数プロセス呼び出しに渡す例を示しています。プロセス関数の実装では、コールバックはブラック ボックスとして扱われ、パラメーターを渡して取得する役割を果たします。戻り値の具体的な実装は以前は明確ではありませんでした。 20 行目と 22 行目が実行される場合のみ、コールバックはそれぞれ負または二乗で表され、各要素に対して逆の値または二乗値の演算がそれぞれ実行されます。

コードをコピーします コードは次のとおりです:

関数ジェネレーター() {
変数 i = 0; 戻り関数() {

を返します };
}

var gen1 =generator() // 自然数生成器を取得します
var gen2 =generator() // 別の自然数生成器を取得します
var r1 = gen1() // r1 = 0
var r2 = gen1() // r2 = 1
var r3 = gen2() // r3 = 0
var r4 = gen2() // r4 = 1
;
上記のコードは、関数を戻り値として使用する例を示しています。ジェネレーターは自然数生成関数であり、戻り値は自然数生成関数です。ジェネレーターが呼び出されるたびに、結果として無名関数が返されます。この無名関数は、実際に呼び出されるときに、それぞれの自然数を順番に返します。ジェネレーターの変数 i は、この匿名関数が呼び出されるたびに 1 ずつ増加します。これは実際にはクロージャです。以下にクロージャを紹介します。




2.3 終了 クロージャ (クロージャ) は新しい概念ではありません。クロージャは多くの関数型言語で使用されます。 JavaScript では、インライン関数内の外部関数のスコープ内で変数を使用するときにクロージャが使用されます。一般的な例えを使用して、クロージャとクラスの関係を説明します。クラスは関数を伴うデータであり、クロージャはデータを伴う関数です。
クロージャで使用される変数の特徴の 1 つは、親関数が返されたときに変数が解放されず、クロージャのライフ サイクルが終了したときに終了することです。たとえば、前のセクションのジェネレーターの例のように、gen1 と gen2 は、gen1 または gen2 の 2 つの変数が同じである限り、独立変数 i を使用します (gen1 の i が 1 増加しても、gen2 の i は影響を受けず、その逆も同様です)。 JavaScript エンジンによってガベージ コレクションされない場合、それぞれの変数 i は解放されません。 JavaScript プログラミングでは、クロージャは無意識のうちに使用されますが、クロージャのこの機能により使いやすさがもたらされますが、メモリ リークなどの問題が発生しやすくなります。例:

コードをコピーします コードは次のとおりです:
var elem = document.getElementById('test'); elem.addEventListener('click', function() {
alert('elem.tagName をクリックしました); });



このコードの機能は、クリックされたときにノードのラベル名を表示することです。DOM ノードのクリック イベント処理関数として匿名関数を登録し、関数内で DOM オブジェクト要素を参照します。閉じたバッグ。これにより、循環参照が生成されます。つまり、DOM-> Closure->DOM-> Closure... DOM オブジェクトは、クロージャが解放されるまで解放されず、クロージャは DOM のイベント処理関数として機能します。オブジェクトが存在するため、DOM オブジェクトが解放される前にクロージャは解放されません。DOM ツリー内で DOM オブジェクトが削除されても、この循環参照が存在するため、DOM オブジェクトもクロージャも解放されません。次の方法を使用すると、このメモリ リークを回避できます:

コードをコピーします コードは次のとおりです: var elem = document.getElementById('test'); elem.addEventListener('click', function() {
alert('You clicked ' this.tagName); // elem 変数を直接参照しなくなりました
});



上記のコードでは、elem の代わりに this が使用されています (DOM イベント処理関数では、this ポインターは DOM 要素自体を指します)。そのため、JS ランタイムは親クラスの変数が使用されているとは考えなくなります。この関数では、クロージャーは形成されなくなります。
クロージャによっても、同様のメモリ リークの問題が数多く発生します。このような問題を回避するには、コードを作成するときにのみクロージャに注意してください。

2.4 クラス コンストラクター

JavaScript 関数はクラスのコンストラクターとしても機能するため、関数を宣言している限り、new キーワードを使用してクラスのインスタンスを作成できます。

コードをコピー

コードは次のとおりです:

関数 人(名前) {
This.name = 名前
This.toString = function() {
'こんにちは、' this.name '!' を返します。 };
}

var p = 新しい人('Ghostheaven'); alert(p); // こんにちは、Ghostheaven! 上の例では、 Person 関数がクラスのコンストラクターとして使用されており、インスタンスにプロパティとメソッドを追加できます。 . オブジェクト指向JavaScriptプログラミングの詳細についてはこちらの記事が参考になります。ここで話したいのは、JavaScript 関数をクラス コンストラクターとして使用する場合の戻り値の問題です。


function MyClass(name) {
This.name = 名前
戻り値; // コンストラクターの戻り値
}

var obj1 = 新しい MyClass('foo'); var obj2 = MyClass('foo'); var obj3 = 新しい MyClass({}); var obj4 = MyClass({});



上記のコンストラクターは非常に特殊で、return ステートメントを持っています。では、obj1 ~ obj4 はそれぞれどのオブジェクトを指しているのでしょうか?実際の結果は次のとおりです:

コードをコピーします

コードは次のとおりです: obj1 = MyClass オブジェクト obj2 = 'foo' obj3 = {}
obj4 = {}



具体的な理由はこの記事で説明されていますが、この記事では詳しく説明しません。戻り値を持つコンストラクターは奇妙な結果を生成するため、コンストラクター内で戻り値を持つ return ステートメントを呼び出さないでください。空のリターンでも問題ありません)。

3. JavaScript 関数のモンスターレベル


モンスターレベルの機能指導エリアへようこそ。ここでは、モンスターと冷静かつ快適に対峙する方法を教えます。 。 。

3.1 関数クラス

JavaScript ランタイムには Function と呼ばれる組み込みクラスがあり、 function キーワードを使用して関数を宣言することは、実際には Function クラス オブジェクトを作成する短縮形式です。すべての関数には、call、apply、などの Function クラスのメソッドがすべて含まれています。そして、bind を実行します。このステートメントは、instanceof キーワードを使用して検証できます。
Function はクラスであるため、そのコンストラクターは Function であり (それ自体も Function クラスのオブジェクトです)、new キーワードを通じて関数オブジェクトを生成できるはずです。ここで最初のモンスターが登場します。それは、Function クラスを使用して関数を構築する方法です。 Function の構文は次のとおりです:


コードをコピーします

このうち、arg1、arg2、... argN はパラメータ名を表す文字列であり、functionBody も関数本体を表す文字列です。Function のコンストラクタは、多かれ少なかれ、次のパラメータ名を扱います。 lastparameters 関数本体では、前のパラメータはパラメータとして扱われます。

コードをコピーします

上記のメソッドは、Function を通じて関数を構築します。この関数は、function キーワードで宣言された他の関数とまったく同じです。
これを見て、なぜこんなモンスターが必要なのか疑問に思う人も多いだろう。 「存在するものはすべて合理的です。」 Function クラスには、さまざまな関数ロジックを動的に生成したり、eval 関数の関数を置き換えたりして、現在の環境を汚染しないようにするための独自の用途があります*。

3.2 自己更新機能


多くの言語では、一度関数を宣言すると、同じ名前の関数を再度宣言することはできず、構文エラーが発生します。ただし、JavaScript の関数は繰り返し宣言できるだけでなく、関数自体を更新することもできます。自らを喰らう怪物がここに!



コードをコピー

コードは次のとおりです:

関数 selfUpdate() {
window.selfUpdate = function() {
アラート('2 回目の実行!'); };

アラート('最初の実行!'); }

selfUpdate(); // 最初の実行
selfUpdate(); // 2 回目の実行! この関数はロジックを 1 回だけ実行するために使用でき、最初の実行後にロジック全体を新しいロジックに置き換えます。


概要

JavaScript 関数は非常に強力で、多くの問題を見事に解決しますが、多くのマイナスな問題も引き起こします。モンスターレベルの関数の使い方は、あまり知られていないものです。特に必要な場合を除き、安易に使用しないでください。そうしないと、コードが読みにくくなり、開発効率に影響します。

* 新しい ECMAScript では Strict モードが導入され、eval 関数が大幅に制限され、環境が汚染されないようにすることもできます

理解できましたか? 非常に実践的な内容です。不足している点があれば、専門家に指導を求めてください。一緒に進歩していきましょう。

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