ループは、すべてのプログラミング言語で最も重要なメカニズムの1つであり、ループは、実用的な重要性(並べ替え、クエリなど)のほぼすべてのコンピュータープログラムでは開かれていません。ループは、プログラムの最適化の非常に厄介な部分でもあります。多くの場合、プログラムの複雑さを常に最適化する必要がありますが、ループによる時間の複雑さと空間の複雑さの選択に絡み合っています。
JavaScriptには、(){} for(){} for(){}およびdo {} while()の3つのネイティブループがあり、最も一般的に使用されるものは(){}です。
ただし、JavaScriptエンジニアがプログラムを最適化するときに無視する最も可能性の高いループです。
まず、基本的な知識を確認しましょう。
JavaScriptの構文はC言語から継承されており、forループの基本的な構文を使用する2つの方法があります。
1。ループ配列
forループの基本的な構文
コードコピーは次のとおりです。
for( / *初期化 * /2 / *判断条件 * /2 / *ループ処理 * /){
// ...論理コード
}
インスタンスコードの一部で詳細に説明します。
コードコピーは次のとおりです。
var array = [1、2、3、4、5];
var sum = 0;
for(var i = 0、len = array.length; i <len; ++ i){
sum += array [i];
}
console.log( '配列/'の項目の合計は%d。 '、sum);
// =>配列のアイテムの合計は15です。
このコードでは、まず、蓄積されるアイテムを保存する配列と合計シェーピング変数を定義および初期化します。次に、ループを開始します。ループのこの初期化コードでは、2つの変数を定義および初期化します:i(counter)とlen(ループアレイ長のエイリアス)。私がLENよりも少ない場合、ループ条件が確立され、論理コードが実行されます。各論理コードが実行された後、私は1で増加します。
ループの論理コードでは、現在のループの配列項を合計変数に追加します。
このサイクルは、次のようにフローチャートで表されます。
このフローチャートから、プログラム内の実際のループ本体に論理コードが含まれているだけでなく、ループ自体を実装する実行判断とループ処理も含まれることを見つけることは難しくありません。
このようにして、私たちの最適化のアイデアは明確になり、4つの側面から最適化できます。
1。ループ本体の前の初期化コード
2。ループ本体の実行判断条件
3。論理コード
4.論理コード後のコードの処理
PS:最初のポイントと2番目のポイントの間には重要な関係があります。
1.1初期化コードと実行判断条件を最適化します
まず、誰もがよく知っているコードを見てみましょう。
コードコピーは次のとおりです。
// 間違っている!
for(var i = 02 i <list.length2 ++ i){
// ...論理コード
}
JavaScriptを書くほとんどのエンジニアは、この一見普通のループメソッドをまだ使用していると信じていますが、なぜここで間違っていると言うのですか?
このループのすべてを分解して、見てみましょう。
1。コードの初期化 - このループは、カウンター変数のみを定義および初期化します。
2。実行判断条件 - カウンターがリストの長さよりも低い場合に当てはまります。
3。処理コード - カウンターは1で増加します。
上のフローチャートを確認して、何か問題があるかどうかを確認しましょう。
実際のループ本体には、論理コードがあるだけでなく、ループ自体を実装する実行判断と処理コードも含まれています。つまり、判断条件I <list.lengthは、各ループの前に実行する必要があります。 JavaScriptでは、オブジェクトのプロパティまたはメソッドを読み取るときにクエリが必要です。
あなたは何かを理解しているようですね?この判断条件には2つの操作があります。1。リスト配列の長さ属性をクエリします。 2。iとlist.lengthのサイズを比較します。
リスト配列にn要素が含まれていると仮定すると、プログラムはこのループの実行判断で2N操作を実行する必要があります。
これにコードを変更した場合:
コードコピーは次のとおりです。
// 良い
for(var i = 0、len = list.length; i <len; ++ i){
// ...
}
この改良されたコードでは、定義を追加し、LEN変数を初期化して、LOOPボディの実行前にLINGの値を格納するための初期化コードに長さを保存します(変数、式、ポインター、値に関する関連コンテンツについて説明します)。このようにして、ループ本体の実行判断でリストアレイを再度照会する必要はなく、オペランドは元のものの半分です。
上記の手順では、アルゴリズムの時間の複雑さを改善しました。スペースの複雑さを最適化し続けたい場合は、どのように行う必要がありますか?ロジックコードがループの順序で制限されていない場合は、次の最適化方法を試すことができます。
コードコピーは次のとおりです。
for(var i = list.length -1; i> = 0; -i){
// ...
}
このコードは、最後の要素サブスクリプト(list.length -1)から始まるループ順序を反転させることにより、転送されます。ループに必要な変数の数を1に減らし、実行判断では、変数クエリの数が減り、CPU命令を実行する前に費やされる時間が減少します。
1.2ロジックコードの最適化
ループでは、ループの現在の配列要素を取得します。当然、それについていくつかの操作を行うか、それを使用して、要素へのいくつかの呼び出しにつながります。
コードコピーは次のとおりです。
var array = [
{name: 'Will Wen Gunn'、Type: 'Hentai'}、
{name: 'Vill Lin'、Type: 'Moegril'}
];
for(var i = array.length -1; i> = 0; -i){
console.log( 'name:%s'、array [i] .name);
console.log( '彼/彼女は(n)%s'、array [i] .type);
console.log( '/r/n');
}
/*=>
名前:ヴィルリン
彼/彼女は(n)モーグルです
名前:Will Wen Gunn
彼/彼女は(n)変態です
*/
このコードでは、プログラムは各配列要素の名前とタイプの属性を照会する必要があります。配列にn要素がある場合、プログラムは4Nオブジェクトクエリを実行します。
コードコピーは次のとおりです。
1。配列[i]
2。配列[i] .Name
3。配列[i]
4。配列[i] .type
この時点で解決策を考えたに違いないと思います。つまり、現在の配列要素の値を変数に割り当ててから、論理コードで使用します。
コードコピーは次のとおりです。
var array = [
{name: 'Will Wen Gunn'、Type: 'Hentai'}、
{name: 'Vill Lin'、Type: 'Moegril'}
];
var person = null;
for(var i = array.length -1; i> = 0 &&(person = array [i]); -i){
console.log( 'name:%s'、person.name);
console.log( '彼/彼女は(n)%s'、person.type);
console.log( '/r/n');
}
人= null;
これはもっと美しく見えます。
コードコピーは次のとおりです。
1。配列[i] => var人
2。person.name
3。Person.Type
EMCAScript5のForeachに少し似ていますが、2つの違いは巨大なので、ここでは説明しません。
PS:修正ありがとうございます。実験の後、配列内の要素が値を直接渡すことによって定義される場合、ループで得られる値はポインターではなく値でなければならないことがわかりました。したがって、式または変数を定義するかどうかにかかわらず、追加のメモリスペースリクエストがあります。
1.3処理コードを最適化します
実際、ループ本体の処理コードを最適化することはあまりありません。Iカウンターは、1を単独で増やすのに十分です。
PS:良い提案や方法がある場合は、それらを提供してください。 :)
2。円形オブジェクト(オブジェクト)
JavaScriptでは、オブジェクトのプロパティとメソッドを通過することもできます。 forループは、オブジェクトが属するラッピングタイプまたはコンストラクターのプロトタイプのプロパティとメソッドを通過できないことに注意する必要があります。
構文はループ配列よりもシンプルです。
コードコピーは次のとおりです。
for(/* initialize*/ var key in object){
// ...論理コード
}
この方法を使用して、オブジェクトを操作します。
コードコピーは次のとおりです。
var person = {
「名前」:「ウィル・ガン」、
「タイプ」:「ヘンタイ」、
「スキル」:[「プログラミング」、「写真撮影」、「スピーキング」、「など」]
};
for(var key in fore){
value = person [key];
//値が配列の場合は、文字列に変換します
if(Arrayの値インスタンス){
value = value.join( '、');
}
console.log( '%s:%s'、key、value);
}
/*=>
名前:Will Wen Gunn
タイプ:ヘンタイ
スキル:プログラミング、写真、スピーキングなど
*/
MongoDBを使用した場合、そのクエリメカニズムに間違いなく精通しているでしょう。 MongodbのクエリメカニズムはAPIの魂のようなものであるため、柔軟なカード操作方法はMongodbを獲得し、多くの人気と開発の勢いを獲得しています。
NANODBのMongo API実装では、クエリ実装では大規模にループオブジェクトを使用します。
コードコピーは次のとおりです。
var mydb = nano.db( 'mydb');
var mycoll = mydb.collection( 'mycoll');
var _cursor = mycoll.find({
タイプ:「レポ」、
言語:「JavaScript」
});
_カーソル
。選別({
星:1
})
.toarray(function(err、rows){
if(err)
return Console.Error(err);
console.log(行);
});
最適化する必要があるのは、ループ自体ではなく、通過する必要があるオブジェクトの最適化です。
たとえば、NanoDBのNanocollectionクラスは、すべての要素またはオブジェクトを含む配列のように見え、要素のIDをキーとして使用してから要素を保存します。
しかし、そうではありません。アンダースコアを使用した学生は、_.invertメソッドを知っている必要があります。これは、渡されるオブジェクトのキーと値を逆転させる非常に興味深い方法です。
コードコピーは次のとおりです。
var person = {
「名前」:「ウィル・ガン」、
「タイプ」:「ヘンタイ」
};
var _inverted = _.invert(person);
console.log(_inverted);
/*=>
{
「ウィル・ガン」:「名前」、
「ヘンタイ」:「タイプ」
}
*/
ループオブジェクトを使用してオブジェクトの特定のプロパティの値をクエリする必要がある場合は、次の方法を試すことができます。
コードコピーは次のとおりです。
var person = {
「名前」:「ウィル・ガン」、
「タイプ」:「ヘンタイ」
};
var name = 'will wen gunn';
var _inverted = _.invert(person);
if(_inverted [name] === 'name'){
console.log( 'catched!');
}
// =>キャッチ!
ただし、オブジェクトクエリに使用する最適化はあまりありません。すべてが実際のニーズに基づいている必要があります。 :p
次に、他の2つのループを見ています。コンピューターサイエンスコースを受けた友人は、これら2つのサイクルに精通していると思います。それらの唯一の違いは、ループ本体の実行の論理的な順序です。
while(){}の実行順序は、for(){}の順序と似ています。実行判断は論理コードの前に実行されますが、初期化と処理コードは省略されています。
条件が与えられると、条件が保持されなくなるまで論理コードが実行されます。
コードコピーは次のとおりです。
var sum = 0;
while(sum <10){
sum + = sum + 1;
}
console.log(sum);
// => 15
{} while()は、論理コードの後に実行判断を行います。これは、「最初に死んでからプレイ」を意味します。
コードコピーは次のとおりです。
var sum = 0;
する {
sum + = sum + 1;
} while(sum <10);
console.log(sum);
// => 15
while(){}およびdo {} while()もカウンターを必要としませんが、特定の条件を使用して、実行コードを実行するか実行し続けるかを決定します。
3。while(){}およびdo {}
while(){}およびdo {} while()は主にビジネスロジックで使用され、タスクキューなどの特定の目的を達成するために一連の操作が継続的に実行されます。
しかし、これらの2つのループは、デフォルトで実行条件によってのみ制御されるため、危険です。論理コードの実行判断に影響がない場合、デッドループが発生します。
コードコピーは次のとおりです。
var sum = 02
// 警告!
while(sum <10){
sum = 1 + 12
}
このようなコードは、(true){}と違いはないため、使用前に実行条件と実行条件に影響を与える方法を明確にする必要があります。
4.ループ制御ステートメントを適切に使用します
すべてのJavaScriptエンジニアはブレイクステートメントを使用していると思いますが、継続的なステートメントは比較的まれには使用されません。実際、見つけることができる多くの優れたJavaScriptオープンソースプロジェクトがあります。
継続ステートメントの機能を解決するために、最初にサンプルコードを見てみましょう
コードコピーは次のとおりです。
// node.jsブロードキャストサーバー
var net = require( 'net');
var util = require( 'util');
var broadcastserver = net.createserver();
//クライアントストア
broadcastserver.clients = [];
//クライアントブロードキャスト方法
net.socket.prototype.broadcast = function(msg){
var clients = broadcastserver.clients;
//集中化されたブロードキャストクライアントの添え字を取得します
var index = clients.indexof(this);
for(var i = client.length -1; i> = 0; -i){
if(i === index){
//ブロードキャストクライアントの場合、現在のループ本体は終了します
続く;
}
currclient = clients [i];
if(!currclient.destroyed){
currclient.write(
util.format(
'/r [エコークライアント%s:%d]%s/ninput:'、
Currclient.RemoteadDress、Currclient.RemotePort、MSG)
);
}
}
};
//接続された新しいクライアント
broadcastserver.on( 'connection'、function(client){
broadcastserver.clients.push(client);
// いらっしゃいませ
client.write( '[ブロードキャストサーバー] Welcome!/ninput:');
client.broadcast(client、 'inted!');
//メッセージハンドル
client.on( 'data'、function(msg){
client.broadcast(msg);
client.write( '/rinput:');
});
//ハンドルを切断します
client.on( 'end'、function(){
client.broadcast( 'left!');
})
});
//バインド
broadcastserver.listen(8080、function(){
console.log( 'ブロードキャストサーバーバウンド');
});
このコードは、node.jsネットモジュールに基づいてブロードキャストサーバーを実装します。ブロードキャスト方法では、継続ステートメントを使用して、ブロードキャストクライアントを除く接続を確立したすべての接続クライアントを実装します。
コードコンテンツは非常に簡単です。クライアントが他のクライアントにブロードキャストする必要がある場合、クライアントに対応するクライアントオブジェクトのブロードキャスト方法が呼び出されます。ブロードキャスト方法では、プログラムはまず、キャッシュされたクライアントソケットコレクションの現在のクライアントの位置添え付けを取得し、次にすべてのクライアントソケットをループします。ループカウンターが以前に取得した位置の添え字に到達すると、現在のループ本体の論理コードがスキップされ、次のループが続きます。
C/C ++言語を学んだエンジニアは、さまざまな場所からこのアドバイスを受け取ると信じています。「GOTOステートメントを使用しないでください」。
この「悪名高い」GOTOステートメントは実際にはコードフローコントローラーであり、GOTOステートメントの詳細についてはここで詳しく説明しません。ただし、JavaScriptには明らかなGOTOステートメントはありませんが、ブレークステートメントと継続的なステートメントから、JavaScriptでGOTOの影を見つけることは難しくありません。
これは、ブレークステートメントと継続的なステートメントにより、コードリダイレクトの定義されたラベル名を受け入れることができるためです。
MDNが提供するサンプルコードを見てみましょう。
コードコピーは次のとおりです。
var i、j;
ループ1:
for(i = 0; i <3; i ++){//ステートメントの最初のものは「loop1」とラベル付けされています
ループ2:
for(j = 0; j <3; j ++){//ステートメントの2番目は「loop2」とラベル付けされています
if(i == 1 && j == 1){
loop1を続行します。
} それ以外 {
console.log( "i =" + i + "、j =" + j);
}
}
}
//出力は次のとおりです。
// "i = 0、j = 0"
// "i = 0、j = 1"
// "i = 0、j = 2"
// "i = 1、j = 0"
// "i = 2、j = 0"
// "i = 2、j = 1"
// "i = 2、j = 2"
//「i = 1、j = 1」と「i = 1、j = 2」の両方をスキップする方法に注意してください
この例コードでは、2層ループが実装され、各ループの外側にラベルが定義されています。これは、後続の継続ステートメントを呼び出すために使用されます。
ループの最初のレイヤーは、loop1のラベルにあります。つまり、その後のプログラムでは、loop1ラベルが継続ステートメントまたはブレークステートメントで選択されている場合、最も外側のループが壊れます。
2番目のレイヤーループは、トップレベルループのLOOP2のラベルにあります。 loop2ラベルが継続ステートメントまたはブレークステートメントで選択されている場合、トップレベルループのループ本体に戻ります。
ループ制御ステートメントを使用することにより、元のループ実行判断に干渉して、非常に複雑なロジックシステムを構築できるようにすることができます。率直に言って、Linuxカーネルには多くのGOTOステートメントがあります。 Gotoの声明のような発言をまだよく聞く理由については、自分でグーグルで検索してください。
5。高度なループ
5.1ループを展開します
まず2つのコードを見て、どちらがパフォーマンスが優れているかを推測しましょう。
コードコピーは次のとおりです。
// 設定
var array = [
["data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"]、
["data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"]、
["data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"]、
["data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"]、
["data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"]、
["data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"]、
["data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"]、
["data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"、 "data"]]
];
関数プロセス(item){
//アイテムで何かをします
}
//ケース1
for(var i = array.length-1; i> = 0; i-){
for(var j = array [i] .length-1; j> = 0; i-){
プロセス(配列[i] [j]);
}
}
//ケース2
for(var i = array.length -1; i> = 0; i = i -4){
for(var j = array [i] .length -1; j> = 0; j = j -6){
プロセス(配列[i] [j]);
プロセス(配列[i] [j -1]);
プロセス(配列[i] [j -2]);
プロセス(配列[i] [j -3]);
プロセス(配列[i] [j -4]);
プロセス(配列[i] [j -5]);
}
for(var j = array [i -1] .length -1; j> = 0; j = j -6){
プロセス(配列[i] [j]);
プロセス(配列[i] [j -1]);
プロセス(配列[i] [j -2]);
プロセス(配列[i] [j -3]);
プロセス(配列[i] [j -4]);
プロセス(配列[i] [j -5]);
}
for(var j = array [i -2] .length -1; j> = 0; j = j -6){
プロセス(配列[i] [j]);
プロセス(配列[i] [j -1]);
プロセス(配列[i] [j -2]);
プロセス(配列[i] [j -3]);
プロセス(配列[i] [j -4]);
プロセス(配列[i] [j -5]);
}
for(var j = array [i -3] .length -1; j> = 0; j = j -6){
プロセス(配列[i] [j]);
プロセス(配列[i] [j -1]);
プロセス(配列[i] [j -2]);
プロセス(配列[i] [j -3]);
プロセス(配列[i] [j -4]);
プロセス(配列[i] [j -5]);
}
}
アレイ内のサブアレイのすべての要素を実行する必要があります。 2つのソリューションがあります。1つは通常使用する方法で、もう1つはループタスクを拡張することです。答えは、ケース2のパフォーマンスが向上するということです。これは、6つの要素ごとのすべての実行判断が削除され、通常よりも自然に高速であるためです。
ここでは、より強力なソリューションをご覧ください。ビジネスリンクを大規模なデータセットで繰り返し処理する必要があり、データボリュームが反復の開始から変更されない場合は、Duffデバイスと呼ばれるテクノロジーの使用を検討できます。このテクノロジーは、最初にC言語で実装された作成者Tom Duffにちなんで名付けられました。その後、ジェフ・グリーンバーグはそれをJavaScriptに移植し、Andrew Bを通じて修正しました。キングとより効率的なバージョンを提案しました。
コードコピーは次のとおりです。
//クレジット:サイトをスピードアップします(新しいライダー、2003)
var iterations = math.floor(values.length / 8);
var leftover = values.length%8;
var i = 0;
if(leftover> 0){
する {
プロセス(値[i ++]);
} while(-leftover> 0);
}
する {
プロセス(値[i ++]);
プロセス(値[i ++]);
プロセス(値[i ++]);
プロセス(値[i ++]);
プロセス(値[i ++]);
プロセス(値[i ++]);
プロセス(値[i ++]);
プロセス(値[i ++]);
} while(-iterations> 0);
この手法の実用的な原則は、値の長さを8で割って反復する必要がある反復回数を取得し、Math.floor()関数を使用して、結果が整数であることを確認し、次にこれらの要素を別々に処理することができず、8は単一の拡張を計算することです。
このデバイスをパッケージ化し、非同期風味のAPIを手に入れました。
コードコピーは次のとおりです。
function duff(配列、マッパー){
var n = math.floor(array.length / 8);
var l = array.length%8;
var i = 0;
if(l> 0){
する {
Mapper(array [i ++]);
} while(-i> 0);
}
する {
Mapper(array [i ++]);
Mapper(array [i ++]);
Mapper(array [i ++]);
Mapper(array [i ++]);
Mapper(array [i ++]);
Mapper(array [i ++]);
Mapper(array [i ++]);
Mapper(array [i ++]);
} while(-n> 0);
}
duff([...]、function(item){
// ...
});
上記の3つの反復ソリューションの一連のパフォーマンステストと結果を次に示します。 http://jsperf.com/spreaded-loop
5.2非ネイティブループ
どのプログラミング言語でも、ループは他の方法だけでなく、他の方法でも間接的に実装できます。
まず、高校の数学のいくつかの内容 - シーケンスの一般的な公式を見てみましょう。
コードコピーは次のとおりです。
基本
a [1] = 1
a [n] = 2 * a [n -1] + 1
それで
a [n] + 1 = 2 * a [n -1] + 2
= 2 *(a [n -1] + 1)
(a [n] + 1) /(a [n -1] + 1)= 2
それから
a [n] + 1 =(a [n] + 1) /(a [n -1] + 1) *(a [n -1] + 1) /(a [n -2] + 1) * ...(a [2] + 1) /(a [1] + 1) *((a [i] + 1) *(
a [n] + 1 = 2 * 2 * ... * 2 * 2
a [n] + 1 = 2^n
a [n] = 2^n -1
ファイナル
a [n] = 2^n -1
上記の簡単な計算を読んだ後、おそらく私たちが議論することを推測するでしょう。はい、再帰を使用してループを実装することもできます。
再帰は、数学とコンピューターサイエンスにおける非常に重要なアプリケーション方法であり、使用されたときにそれ自体を呼び出す関数を指します。
node.jsコミュニティでは、非常に重要なテクノロジーであるミドルウェアテクノロジーを実装するために再帰が使用されます。これは、まだ公開されていないWebJSのミドルウェア実装コードの新しいバージョンです。
コードコピーは次のとおりです。
/**
* MiddleWares Run Method
* @param {string} url current request url
* @param {object}要求オブジェクトをreq
* @param {object} res応答オブジェクト
* @param {function}完全なコールバック
* @return {function}サーバー
*/
server.runmiddlewares = function(url、req、res、out){
var index = -1;
var middlewares = this._usingmiddlewares;
//次のミドルウェアが存在する場合は実行します
function next(err){
インデックス++;
//現在のミドルウェア
var curr = middlewares [index];
if(curr){
var check = new regexp(curr.route);
//ルートを確認します
if(check.test(url)){
試す {
function late(){
Debug( 'ミドルウェアは、%s'、urlで後である必要があると言っています);
//依存関係は今ではありません
if(middlewares.indexof(curr)!== middlewares.length -1){
_later(curr);
索引 - ;
次();
} それ以外 {
デバッグ( 'ミドルウェアの依存関係が間違っている');
//このミドルウェアは実行できません
外();
}
}
//ミドルウェアを実行します
if(utils.isfunc(curr.handler)){
//通常のミドルウェア関数
Curr.Handler(req、res、next、hanter);
} else if(utils.isobject(curr.handler)&& utils.ispunc(curr.handler.emit)){
//サーバーオブジェクト
curr.handler.emit( 'request'、req、res、next、late);
} それ以外 {
//ミドルウェアには何か問題があります
次();
}
} catch(err){
次();
}
} それ以外 {
次();
}
} それ以外 {
//パイプラインの次のステップに出ます
外();
}
}
//ミドルウェアが他のミドルウェアに依存している場合、
//後で実行することができます
function _later(curr){
var i = middlewares.indexof(curr);
var _tmp1 = middlewares.slice(0、i);
_tmp1.push(middlewares [i + 1]、curr);
var _tmp2 = middlewares.slice(i + 2);
[] .push.apply(_tmp1、_tmp2);
MiddleWares = _tmp1;
}
//最初のミドルウェア
次();
これを返します。
};
このコードは厳しく複雑に見えますが、単純化すればはるかに明確になります。
コードコピーは次のとおりです。
server.runmiddlewares = function(url、req、res、out){
var index = -1;
var middlewares = this._usingmiddlewares;
//次のミドルウェアが存在する場合は実行します
function next(err){
インデックス++;
//現在のミドルウェア
var curr = middlewares [index];
if(curr){
var check = new regexp(curr.route);
//ルートを確認します
if(check.test(url)){
//現在のミドルウェアを実行します
Curr.Handler(req、res、next);
} それ以外 {
次();
}
} それ以外 {
//パイプラインの次のステップに出ます
外();
}
}
//最初のミドルウェア
次();
これを返します。
};
ミドルウェアシステムの実装で再帰を使用できる理由は、再帰がnode.jsのプログラムフロー応答の最も適切な方法であるためです。
このミドルウェアの実装コードでは、this._usingmiddlewaresはループアレイであり、function next()はループボディであり、check.test(url)は実行判断条件であり、ループ処理コードはループボディの最初のインデックスカウンターであり、次の関数自体を増やします。