導入
多くの伝統的な言語(C/C ++/Java/C#など)では、機能は二流市民として存在します。言語のキーワードを使用して関数を宣言してから呼び出すことができます。関数をパラメーターとして別の関数に渡すか、ローカル変数に値を割り当てる必要がある場合、または戻り値として割り当てる必要がある場合は、関数ポインターやプロキシ(デリゲート)などの特別な方法でブレークスルーを行う必要があります。
JavaScriptの世界では、機能は一流の市民です。従来の関数(宣言と呼び出し)を使用するすべての方法を持っているだけでなく、値を割り当て、パスパラメーターを渡し、単純な値のように返すこともできます。このような関数は、ファーストクラス関数とも呼ばれます。それだけでなく、JavaScriptの関数もクラスコンストラクターとして機能し、関数クラスのインスタンスでもあります。このような複数のアイデンティティにより、JavaScript機能が非常に重要になります。
1。JavaScript関数エントリレベル
通常の言語と同様に、JavaScript関数は、最初に宣言の原則に従ってから使用します。関数名には、文字、数字、アンダースコア、または$のみを含めることができ、数字から始めることはできません。関数を宣言するには、2つの一般的な方法があります。
コードコピーは次のとおりです。
//関数myfuncを直接宣言します
function myfunc(/ * arguments */){
}
//匿名関数をローカル変数myfuncに割り当てます
var myfunc = function(/ * arguments */){
}
上記の2つの関数宣言方法には微妙な違いがあることに注意してください。最初の方法は、宣言された場合の指名された関数であり、それがコールの前、宣言された関数であるか、それが実行されない場所(例えば、返品声明の後、または真実ではないブランチで)であっても、範囲全体でアクセスできます。 2番目の方法は、匿名関数を変数に割り当てることです。厳密に言えば、これは関数宣言ではなく、関数式です。割り当ての前に、この関数には任意のコードでアクセスできません。つまり、呼び出し前に割り当てを完了する必要があります。そうしないと、「TypeError:未定義は関数ではありません」という呼び出し時にエラーが表示されます。例えば:
コードコピーは次のとおりです。
myfunc1(); // myfunc1は直接宣言方法を使用するため、正常に呼び出すことができます
機能myfunc1(){
}
myfunc2(); //エラーTypeRror:未定義は関数ではありません
var myfunc2 = function(){
};
関数の基本呼び出し方法は、従来の言語と同じように呼び出されます:myfunc()。 JavaScript関数は、直接的または間接的な再帰呼び出しもサポートしています。たとえば、古典的なフィボナッチ関数は、次のようなJavaScriptに実装できます。
コードコピーは次のとおりです。
関数fib(n){
if(n == 1 || n == 2){
返品1;
} それ以外 {
return fib(n -2) + fib(n -1);
}
}
JavaScriptの関数は、可変長パラメーターを処理できます。それらはすべて、関数内の引数と呼ばれるローカル変数を持っています。これは、呼び出し時に渡されたすべてのパラメーターを含む配列のようなオブジェクトであり、パラメーターの数を表すために長さの属性を持っています。例えば:
コードコピーは次のとおりです。
function test(){
アラート(arguments.length);
}
テスト(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){
console.log(msg);
}
//その他のコード
}()); //すぐに実行します
上記のコードは簡単な例です。ログ関数の範囲は、この匿名関数に限定されています。匿名関数は、外側の括弧()のペアに含まれており、関数式を形成します。式の値は関数であり、その後、関数がすぐに実行されることを示して、元のコードを正常に実行できることを示す括弧のペアが続きます。ただし、このように宣言された関数、VARなどを介して宣言された変数は内部であり、匿名関数以外のコードではアクセスできません。いくつかの関数をインターフェイスとして公開する必要がある場合、いくつかの方法があります。
コードコピーは次のとおりです。
var mylib =(function(global){
関数ログ(MSG){
console.log(msg);
}
log1 = log; //方法1:VARなしで変数宣言のデフォルトの動作を使用して、log1のグローバル変数になります(推奨されません)
Global.log2 = log; //方法2:log2属性をグローバルオブジェクトに直接追加し、ログ関数に割り当てます(推奨)
return {//方法3:匿名関数を介して一連のインターフェイス関数コレクションオブジェクトを返し、グローバル変数mylibに割り当てます(推奨)
ログ:ログ
};
}(ウィンドウ));
2.2高次関数
関数がパラメーターまたは戻り値として使用される場合、それは高次関数と呼ばれます。 JavaScriptの関数は、高次関数として使用できます。これは、最初のタイプの関数の機能でもあります。以下のこれら2つの使用方法を分析しましょう。
コードコピーは次のとおりです。
関数ネガティブ(n){
return -n; // nの反対の値を取得します
}
関数四角(n){
n*nを返します。 // nの正方形
}
関数プロセス(nums、callback){
var result = [];
for(var i = 0、length = nums.length; i <length; i ++){
result [i] = callback(nums [i]); //配列番号のすべての要素を処理するためにコールバックに渡し、結果として返品値を保存します
}
返品結果;
}
var nums = [-3、-2、-1、0、1、2、3、4];
var n_neg = process(nums、negative);
// n_neg = [3、2、1、0、-1、-2、-3、-4];
var n_square = process(nums、square);
// n_square = [9、4、1、0、1、4、9、16];
上記のコードは、パラメーターとして関数を別の関数プロセス呼び出しに渡す例を示しています。プロセス関数の実装では、コールバックはブラックボックスと見なされ、パラメーターを渡してから戻り値を取得する責任があります。コールバックの特定の実装は、呼び出し前に明確ではありません。 20行と22行が実行された場合にのみ、コールバックはそれぞれ負または正方形で表され、各要素は反対の値または正方形の値で使用されます。
コードコピーは次のとおりです。
function generator(){
var i = 0;
return function(){
I ++を返します。
};
}
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では、埋め込み関数の外部関数の範囲内で変数を使用する場合、閉鎖を使用します。一般的に使用されるアナロジーを使用して、閉鎖とクラスの関係を説明します。クラスは関数を持つデータであり、閉鎖はデータの関数です。
閉鎖で使用される変数には、親関数が戻ったときにリリースされないが、閉鎖のライフサイクルの終了時に終了するという特徴があります。たとえば、前のセクションのジェネレーターの例のように、Gen1とGen2はそれぞれ独立変数Iを使用します(Gen1のIが1増加すると、Gen2は影響を受けません、逆も同様です)。 2つの変数GEN1またはGEN2がJavaScriptエンジンによって収集されていない限り、それぞれの変数Iはリリースされません。 JavaScriptプログラミングでは、閉鎖は無意識に使用されます。閉鎖のこの機能は使いやすいですが、メモリの漏れの問題にも簡単につながります。例えば:
コードコピーは次のとおりです。
var elem = document.getElementById( 'test');
elem.addeventlistener( 'click'、function(){
alert( 'you clicked' + elem.tagname);
});
このコードの目的は、ノードをクリックするときにラベル名を表示することです。 DOMノードのクリックイベント処理機能として匿名関数を登録します。 DOMオブジェクトELEMは、閉鎖を形成する関数で参照されます。これにより、循環参照が生成されます。つまり、dom-> clossary-> dom-> clossary ...閉鎖がリリースされる前にDOMオブジェクトはリリースされません。閉鎖は、DOMオブジェクトのイベント処理機能として存在するため、DOMオブジェクトがリリースされる前に閉鎖はリリースされません。この円形の参照が存在するため、DOMオブジェクトがDOMツリーで削除されたとしても、DOMオブジェクトも閉鎖もリリースされません。このメモリリークは、次の方法を使用して回避できます。
コードコピーは次のとおりです。
var elem = document.getElementById( 'test');
elem.addeventlistener( 'click'、function(){
Alert( 'You Clicked' + this.tagname); // ELEM変数を直接参照することはもうありません
});
上記のコードでは、これはELEMの代わりに使用されます(このポインターはDOMイベント処理機能にDOM要素自体を指しています)。そのため、JSランタイムは、関数が親クラス変数を使用すると信じていないため、閉鎖を形成しなくなります。
また、閉鎖は、多くの同様のメモリリークの問題をもたらします。コードを書くときにのみ閉鎖に注意を払うことができ、そのような問題を回避しようとします。
2.4クラスコンストラクター
JavaScript関数はクラスコンストラクターとしても使用されるため、関数を宣言する限り、新しいキーワードを使用してクラスのインスタンスを作成できます。
コードコピーは次のとおりです。
function person(name){
this.name = name;
this.toString = function(){
'hello' + this.name + '!'を返します。
};
}
var p = new person( 'ghosttheaven');
アラート(P); //こんにちは、Ghosttheaven!上記の例では、個人の関数はクラスコンストラクターとして使用されます。この時点で、これは新しく作成されたインスタンスオブジェクトを指し、インスタンスにプロパティとメソッドを追加できます。詳細なオブジェクト指向のJavaScriptプログラミングについては、この記事を参照してください。ここで言いたいのは、JavaScript関数をクラスコンストラクターとして使用する際の返品値の問題です。
コードコピーは次のとおりです。
function myclass(name){
this.name = name;
返品名; //コンストラクターの返品値?
}
var obj1 = new Myclass( 'foo');
var obj2 = myclass( 'foo');
var obj3 = new Myclass({});
var obj4 = myclass({});
上記のコンストラクターは非常に特別なものであり、返品ステートメントがあるため、OBJ1〜OBJ4はどのオブジェクトを指しますか?実際の結果はこれです:
コードコピーは次のとおりです。
obj1 = myclassオブジェクト
obj2 = 'foo'
obj3 = {}
obj4 = {}
この記事では具体的な理由について説明しますが、この記事では繰り返しません。戻り値を持つコンストラクターは奇妙な結果を生成するため、コンストラクターに戻り値を持つリターンステートメントを呼び出しないでください(空の返品は実行できます)。
3。JavaScript機能モンスターレベル
モンスターレベルの機能ティーチングエリアへようこそ。ここでは、古いモンスターと冷静に自由に向かう方法が与えられます。 。 。
3.1関数クラス
JavaScriptランタイムには、関数と呼ばれる組み込みクラスがあります。関数キーワードで関数を宣言することは、実際には関数クラスオブジェクトを作成するための略語です。すべての関数には、呼び出し、適用、バインドなど、関数クラスのすべてのメソッドがあります。キーワードのインスタンスを介してこのステートメントを確認できます。
関数はクラスであるため、そのコンストラクターは関数(それ自体は関数クラスのオブジェクト)であり、関数オブジェクトは新しいキーワードを介して生成する必要があります。最初のモンスターがここにあります。これは、関数クラスを使用して関数を構築する方法です。関数の構文は次のとおりです。
コードコピーは次のとおりです。
新しい関数([arg1 [、arg2 [、... argn]]、] functionbody)
ここで、arg1、arg2、... argnはパラメーター名を表す文字列であり、functionbodyは関数本体を表す文字列でもあります。前のパラメーター名は多かれ少なかれです。関数コンストラクターは、最後のパラメーターを関数本体として、以前のパラメーターをパラメーターとして扱います。
コードコピーは次のとおりです。
var func1 = new function( 'name'、 'return "hello、 + name +"! ";');
func1( 'ghosttheaven'); //こんにちは、Ghosttheaven!
上記の方法は、関数を介して関数を構築します。これは、関数キーワードで宣言された他の関数とまったく同じです。
これを見て、多くの人がなぜそのようなモンスターが必要なのかと尋ねるかもしれません。 「存在するものは合理的」、関数クラスには独自の目的があります。これを使用して、さまざまな関数ロジックを動的に生成するか、評価関数の関数を置き換え、現在の環境が汚染されないようにすることができます*。
3.2セルフアップデート関数
多くの言語では、関数が宣言されると、同じ名前の関数を再度宣言することはできません。そうしないと、構文エラーが発生します。 JavaScriptの関数は繰り返し宣言するだけでなく、自分自身を更新することもできます。私が食べるモンスターはここにあります!
コードコピーは次のとおりです。
function selfupdate(){
window.selfupdate = function(){
アラート( 'Second Run!');
};
アラート( 'First Run!');
}
selfupdate(); //最初に実行!
selfupdate(); // 2回目の実行!この関数は、一度だけ実行されるロジックに使用でき、最初の実行後、新しいロジックに置き換えられます。
まとめ
JavaScriptの機能は非常に強力です。多くの問題を美しく解決しながら、多くの否定的な問題をもたらします。モンスターレベルの関数は、通常、あまり知られていない使用法で使用されます。特に必要でない限り、コードの読み取りの難しさを引き起こし、チーム開発の効率に影響します。
*新しいECMAScriptに厳密なモードが導入されました。厳密なモードでは、評価関数は大幅に制限されており、環境が汚染されないようにします。
ご存知ですか、それは非常に実用的です。不足している場所がある場合は、私にアドバイスをして、一緒に進歩を遂げてください。