fn = function(){ console.info('fn');};// fn を呼び出したい場合は、関数式を使用して fn に割り当てることしかできません
変数宣言が前であっても後であっても、宣言が昇格されると関数宣言が優先されることがわかりますが、宣言が昇格された後は変数の初期化を行う必要があるため、関数宣言が優先されます。は初期化されなくなりました(プロモーション中に関数型が解析された)ので、後で出力するときは String 型になります。
上記の 3 行目で関数が定義され、7 行目ですぐに呼び出されていますが、機能しません。グローバル名前空間をクリーンに保つことの重要性を理解する必要があります。そうしないと、「コード内で関数を明確に定義したのに、それを呼び出すことができない」というような問題が発生する可能性があります。もちろん、そうすることで他の人のコードを壊す危険があります。
console.info(other);// その他
}
fn('param'); 🎜>// パラメータと内部関数、内部関数が優先されます。
function gn(inner){
console.info(inner);// inner() function
console.info(inner()); // 未定義
function inner(){
return other;
var other = 'other';// inner() 関数
console.info(inner ());// その他
}
gn('param');
上記の出力により、内部関数宣言 > 関数パラメータ > 内部変数宣言の優先順位がわかります。
ここでのプロセスの 1 つは次のとおりです。まず、内部関数宣言がプロモートされ、関数名の型が関数の型に設定されます。次に、関数パラメーターが解析され、実際のパラメーター値が渡されます。 in は仮パラメータに割り当てられ、最後に内部変数宣言のプロモーションは、初期化なしで宣言のみをプロモートします。重複する名前がある場合、同じ優先順位を持つものは前のものを上書きし、異なる優先順位を持つものは上書きされません。優先度の高いものは解析され、優先度の低いものは解析されなくなります)。
説明すると、これは出力結果に基づく私の推論にすぎません。バックグラウンド実装に関しては、手順がまったく逆である可能性もあり、各手順が前の手順の結果をカバーするか、最初の手順から開始することもあります。もちろん、効率の観点から、このプロセスのほうが良いと考えられます。さらに、グローバル スコープは実際には、関数パラメーターのない関数スコープの簡略化されたバージョンです。
ここでは包括的な例は示しません。この記事を以前の基本的な文法の記事と合わせて読むことをお勧めします。より良い結果が得られる可能性があります。優先順位と対象範囲については、以下で説明する問題にもつながります。
4. 関数のオーバーロード
関数はオブジェクトであり、関数名は関数オブジェクトを指す参照型変数であるため、これは不可能です。一般的なオブジェクト指向言語と同様にオーバーロードを実装します。
関数 fn(a){
return a;
}
function fn(a,b){
return a b;
}
コンソール。 info(fn(1)); // NaN
console.info(fn(1,2));// 3
なぜ 8 行目が NaN を出力するのか不思議ではありません。関数名は単なる変数であり、2 つの関数宣言は順番に解析されます。この変数が最終的に指す関数は 2 番目の関数であり、関数内では b が 1 つのパラメーターのみを渡します。を加算し、1 を加算すると、結果は NaN になります。関数式に置き換えるとわかりやすいかもしれません。当然、後の代入によって前の代入が上書きされます:
var fn = function (a){ return a; }
fn = function (a,b){ return a b;
それでは、ECMAScript でオーバーロードを実装するにはどうすればよいでしょうか?単純なデータ型ラッパー オブジェクト (Boolean、Number、String) は、オブジェクトを作成するためのコンストラクターとして、またデータ型を変換するための変換関数として使用できることを思い出してください。これは典型的なオーバーロードです。実際、このオーバーロードについては、前の記事で説明しました。
(1) 関数に従ったオーバーロード このメソッドの一般的な形式は、
function fn(){
if(this instanceof fn)
{
/ /関数 1
}else
{
// 関数 2
}
}
この方法は実行可能ですが、その効果は明らかに限定されています。オーバーロードできるのは 2 回だけであり、オーバーロードできるのはコンストラクターのみです。もちろん、apply() や call()、あるいは ES5 の新しい binding() を組み合わせて、関数内で this 値を動的にバインドしてオーバーロードを拡張することもできますが、これはすでに関数の内部プロパティに基づいたオーバーロードを意味します。
(2) オーバーロード
function fn (){
var length = argument.length;
if(0 == length)//比較演算子が If として記述されているため、リテラルを左側に配置するのは Java からの習慣です。演算子 (0=length) が欠落していると、コンパイラによってエラーが表示されます。このメソッドに慣れていない場合はご容赦ください。
{
return 0;
}else if(1 == length)
{
return argument[0]; else{
return (arguments[0]) (arguments[1]);
}
}
console.info(fn());//0
コンソール。 info(fn(1));//1
console.info(fn(true));//1
console.info(fn(1,2));//3
コンソール。 info(fn('1','2'));//3
ここでは、関数の内部属性引数を使用してオーバーロードを実装します。もちろん、内部的にオーバーロードする方法は数多くあり、typeof や instanceof などの演算子を組み合わせて必要な機能を実現することもできます。内部プロパティ引数とは正確には何ですか?それが以下でお話しすることです。
5. 関数の内部属性の引数
簡単に言うと、関数の内部属性は関数本体内でのみアクセスできる属性です。関数が呼び出されたときにアクセスされる 関数が呼び出されたときに実行されるため、関数の内部プロパティは関数が呼び出されたときにのみ解析されます。各呼び出しには対応する解析があり、動的特性があります。このような属性には、this と引数が含まれます。まず引数を見て、それについては次の記事で説明します。
(1) 関数定義中の引数リストを仮引数、関数呼び出し時に実際に渡される引数を実引数と呼びます。一般に、C 系言語では、関数を呼び出すときに実パラメータが仮パラメータと一致している必要があります。ただし、ECMAScript では、定義時に 2 つの仮パラメータを指定して渡すことができます。 2 つの実パラメータを入力しますが、3 つの実パラメータを渡すことも、1 つの実パラメータのみを渡すことも、パラメータを渡さないこともできます。この機能は、関数の内部プロパティを使用してオーバーロードを実装するための基礎となります。
(2) 仮パラメータは同じ名前を持つこともできますが、実際に渡されると、後の値が仮パラメータの値として使用されます (この場合、引数を使用して、前の実際のパラメータ):
function gn(a ,a){
コンソール .info(a);
コンソール.info(引数[1]);
gn(1) ,2);//2, 1,2
gn(1);//未定義, 1, 未定義
これは、実際には、この記事の前半のステートメントのプロモーションに関する結論によって説明できます。 : 同じ優先順位を持つ後のものは前のものを上書きし、関数のパラメータが解析されるときに値も同時に解析されます。もちろんこのままではセキュリティ上非常に問題があるため、ES5のstrictモードでは重複した名前の仮パラメータは禁止されています。
(3) 実パラメータの値は仮パラメータで受け入れられますが、実パラメータと仮パラメータが一致しない場合はどうなりますか?答えは、引数を使用して格納することです。実際、実パラメータと仮パラメータが一致している場合でも、引数オブジェクトは依然として存在し、実パラメータを受け入れた仮パラメータと同期されます。この文を改良して理解してみましょう。
•arguments は配列のようなオブジェクトであり、arguments 要素には、arguments[0]、arguments[1] などの配列要素と同様に角括弧とインデックスを介してアクセスできます。
•arguments は、Object から継承されたプロパティとメソッド (一部のメソッドはオーバーライドされます) に加えて、長さ、呼び出し先、呼び出し元などの独自のプロパティも持ちます。実際のパラメータの数 (仮パラメータの数? つまり関数属性の長さ)、callee は現在の関数オブジェクトを表し、caller は関数属性の呼び出し元と区別するためにのみ定義されており、その値は未定義です。
•arguments は配列のようなオブジェクトですが、実際の配列オブジェクトではありません。引数に対して配列オブジェクトのメソッドを直接呼び出すには、まず Array.prototype.slice.call を使用します。 (引数) を使用してオブジェクトに変換します。
•arguments には、関数の呼び出し時に渡される実際のパラメータが格納されます。0 番目の要素は最初の実パラメータを格納し、1 番目の要素は 2 番目の実パラメータを格納します。
•引数は実際のパラメータ値を保存し、仮パラメータも実際のパラメータ値を保存します。一方が変更されると、他方もそれに応じて変更されます。
•引数と仮パラメータ間の同期は、仮パラメータが実際に実パラメータを受け取る場合にのみ存在します。実パラメータを受け取らない仮パラメータの場合、そのような同期関係はありません。
引数オブジェクトは非常に強力ですが、パフォーマンスがある程度低下するため、必要がない場合は、仮パラメータを優先して使用しないことをお勧めします。
コードをコピー コードは次のとおりです:
fn(0,-1);
function fn(para1,para2,para3,para4){
console.info(fn.length);//4、仮パラメータの数
console.info(arguments.length);//2、パラメータの実際の数
console.info(arguments.callee === fn);//true、呼び出し先オブジェクトは fn 自体を指します
console.info (arguments.caller);//未定義
console.info(arguments.constructor);//Array() ではなく Object()
try{
arguments.sort();/ /Array-like 結局配列ではないので、配列メソッドを直接呼び出すことはできません
}catch(e){
console.info(e);//TypeError
}
var arr = Array.prototype.slice .call(arguments);//まず配列に変換します
console.info(arr.sort());//[-1,0]、ソート済み
console.info( para1);//0
arguments[0] = 1;
console.info(para1);//1、arguments[0] を変更すると、同時に形式も変更されますパラメータ para1
console.info(arguments[1]);//-1
para2 = 2;//2、仮パラメータを変更しますpara2 は同時に argument[1]
console.info(para3);//未定義、実際のパラメータが渡されない仮パラメータは未定義です
arguments[2] = 3;
console。 info(arguments[2]);// 3
console.info(para3);//実パラメータを受け入れない未定義の仮パラメータには同期関係がありません
console.info(arguments[3] ]);//未定義、実際のパラメータは渡されず、値は未定義です
para4 = 4;
console.info(para4);//4
console.info(arguments[3]) ;//未定義。これは渡される実際のパラメータであり、同期されません。
}
テスト後、引数と仮パラメータ間の同期は双方向ですが、「」の 66 ページJavaScript Advanced Programming (3rd Edition) には、これは一方向であると記載されており、仮パラメータを変更しても引数は変更されません。これは、元の本の別のバグである可能性があります。あるいは、FireFox が仕様を拡張した可能性があります。ただし、これは、たとえ古典的であっても、依然としてバグの可能性があり、すべてが実際の運用に従う必要があることを示しています。
• 引数とその属性の呼び出し先と組み合わせると、関数内でそれ自体を呼び出すときに関数名から分離できるため、関数が別の変数に割り当てられている場合でも、関数名 (忘れないでください) 、変数でもあります)さらに、値を割り当てることで、正しい動作を保証することもできます。典型的な例には、階乗関数、フィボナッチ数列などの検索が含まれます。
//階乗を検索
関数階乗(num) {
if(num {
return 1;
}else{
return num *階乗(num - 1); >}
var fn = fastial;
factorial = null;
try{
fn(2);//factorial は関数内で再帰的に呼び出され、factorial には null の値が割り当てられているため、例外がスローされます
}catch(e){
console.info(e);//TypeError
}
//フィボナッチ数列
function fibonacci(num){
if(1 == num || 2 == num){
return 1;
}else{
return argument.callee(num - 1) argument.callee(num - 2);
}
}
var gn = fibonacci;
fibonacci = null;
console.info(gn(9));//34 は、arguments.callee を使用して関数オブジェクトを実現します。関数名 分離すると通常通り実行可能
再帰アルゴリズムは非常にシンプルですが、実行中のスタックを維持する必要があるため、効率はあまり良くありません。再帰的最適化に関しては、非常に充実したアルゴリズムも多数あるため、ここでは詳しく説明しません。
現時点では、arguments.callee は ES5 の厳密モードで禁止されていることに注意してください:
var fibonacci = (function f(num){ return num <= 2 ? 1 : (f(num - 1) f(num - 2)); var gn = fibonacci = null; .info(gn(9));//34、名前付き関数式の使用により、関数オブジェクトと関数名の分離が実現され、通常どおりに実行できます