導入
この章では、ECMAScriptの関数にパラメーターを渡す戦略について説明します。
コンピューターサイエンスでは、この戦略は一般に「評価戦略」と呼ばれます(叔父のメモ:評価戦略に翻訳され、課題戦略に翻訳される人もいます。次のコンテンツを見ると、課題戦略と呼ぶ方が適切だと思います。たとえば、プログラミング言語では、評価または計算式のルールを設定します。パラメーターを関数に渡すための戦略は特別なケースです。
http://dmitrysoshnikov.com/ecmascript/chapter-8-evaluation-strategy/
この記事を書く理由は、フォーラムの誰かがパラメーターを渡すためのいくつかの戦略を正確に説明するように求められたからです。ここでは、誰にとっても役立つことを期待して、ここで対応する定義を与えました。
多くのプログラマーは、JavaScript(他の言語でも)では、オブジェクトは参照によって渡され、元の値タイプは値で渡されると確信しています。さらに、多くの記事はこの「事実」について話していますが、多くの人がこの用語を本当に理解しており、何人が正しいですか?この記事では、1つずつ説明します。
一般理論
割り当て理論には、一般に2つの割り当て戦略があります。厳密なものは、プログラムに入る前にパラメーターが計算されることを意味します。非強制 - パラメーターの計算が計算要件に基づいて計算されることを意味します(つまり、計算を遅らせることに相当します)。
次に、ここでは、ECMAScriptの出発点から非常に重要な基本関数パラメーター転送戦略を検討します。最初に注意すべきことは、厳密なパラメーターの合格戦略がECMAScriptで使用されていることです(C、Java、Python、Rubyなどの他の言語でも)。
さらに、渡されたパラメーターの計算順序も非常に重要です - ECMAScriptでは、左から右にあり、他の言語で実装されている導入順序(右から右へ)も使用できます。
厳密な伝送戦略は、いくつかの種子戦略に分割されており、この章で最も重要な戦略について詳しく説明します。
以下で説明したすべての戦略がECMAScriptで使用されているわけではないため、これらの戦略の特定の動作を議論する際に、擬似コードを使用してそれを表示しました。
価値を渡します
価値を通過すると、多くの開発者は、パラメーターの値が発信者が渡すオブジェクト値のコピーであることを十分に認識しています。関数内のパラメーターの値を変更しても、外部オブジェクト(外部のパラメーターの値)には影響しません。一般的に、新しいメモリが再割り当てされます(割り当てられたメモリの実装方法に注意を払っていません - それはスタックまたは動的メモリの割り当てでもあります)。新しいメモリブロックの値は外部オブジェクトのコピーであり、その値は関数内で使用されます。
コードコピーは次のとおりです。
bar = 10
手順foo(bararg):
Bararg = 20;
終わり
foo(bar)
// foo内の値を変更しても、内部バーの値に影響しません
印刷(bar)// 10
ただし、関数のパラメーターが元の値ではなく、複雑な構造オブジェクトである場合、パフォーマンスの大きな問題が発生します。 C ++にはこの問題があります。構造を関数に値として渡すとき、それは完全なコピーです。
一般的な例を示しましょう。次の割り当て戦略を使用してテストします。 2つのパラメーターを受け入れる関数について考えてください。最初のパラメーターはオブジェクトの値であり、2番目はオブジェクトが完全に変更されているか(オブジェクトにオブジェクトを再割り当てするか)マークするブールマークです。または、オブジェクトの一部のプロパティのみが変更されます。
コードコピーは次のとおりです。
//注:以下はすべて擬似コードであり、JSの実装ではありません
bar = {
X:10、
Y:20
}
手順foo(bararg、isfullchange):
isfullchangeの場合:
bararg = {z:1、q:2}
出口
終わり
Bararg.x = 100
Bararg.Y = 200
終わり
foo(bar)
//値で通過すると、外部オブジェクトは変更されません
print(bar)// {x:10、y:20}
//オブジェクトを完全に変更します(新しい値を割り当てます)
foo(bar、true)
//変更もありません
print(bar)// {x:10、y:20}、{z:1、q:2}の代わりに
参照で渡されます
別のよく知られているパスごとの参照は、バリューコピーではなく、外部のオブジェクトの直接参照アドレスなど、オブジェクトへの暗黙の参照です。関数内のパラメーターの変更は、関数の外側のオブジェクトの値に影響します。どちらも同じオブジェクト、つまりパラメーターが外部オブジェクトのエイリアスに相当するためです。
pseudocode:
コードコピーは次のとおりです。
手順foo(bararg、isfullchange):
isfullchangeの場合:
bararg = {z:1、q:2}
出口
終わり
Bararg.x = 100
Bararg.Y = 200
終わり
//上記と同じオブジェクトを使用します
bar = {
X:10、
Y:20
}
//参照による呼び出しの結果は次のとおりです。
foo(bar)
//オブジェクトのプロパティ値が変更されました
print(bar)// {x:100、y:200}
//新しい値を再割り当てすると、オブジェクトにも影響します
foo(bar、true)
//このオブジェクトはすでに新しいオブジェクトです
print(bar)// {z:1、q:2}
この戦略は、属性の大きなバッチを持つ大きな構造オブジェクトなど、より効率的に複雑なオブジェクトをより効率的に渡すことができます。
共有による電話
誰もが上記の2つの戦略を知っていますが、ここで話したい戦略を知らないかもしれません(実際、それは学術戦略です)。ただし、これはまさにECMAScriptのパラメーター配信戦略で重要な役割を果たす戦略であることがすぐにわかります。
この戦略には、「オブジェクトによるパス」または「オブジェクト共有を通過する」といういくつかの同義語もあります。
この戦略は、1974年にCLUプログラミング言語のBarbara Liskovによって提案されました。
この戦略の重要なポイントは、関数がオブジェクトのコピー(コピー)を受信し、参照コピーが正式なパラメーターとその値に関連付けられていることです。
この関数によって受信されたパラメーターは直接オブジェクトエイリアスではなく、参照アドレスのコピーであるため、ここに表示される参照を「参照によるパス」と呼ぶことはできません。
最も重要な違いは、関数が内部のパラメーターに新しい値を再割り当てすることは、外部オブジェクトに影響しないことです(上記の例で参照によって渡された場合)が、パラメーターはアドレスコピーであるため、外側と内部にアクセスされる同じオブジェクト(たとえば、外部オブジェクトは値を通過するように完全なコピーではありません)。パラメーターオブジェクトの属性値を変更すると、外部オブジェクトに影響します。
コードコピーは次のとおりです。
手順foo(bararg、isfullchange):
isfullchangeの場合:
bararg = {z:1、q:2}
出口
終わり
Bararg.x = 100
Bararg.Y = 200
終わり
//このオブジェクト構造を使用してください
bar = {
X:10、
Y:20
}
//貢献を通過すると、オブジェクトに影響します
foo(bar)
//オブジェクトのプロパティが変更されました
print(bar)// {x:100、y:200}
//再割り当ては機能しません
foo(bar、true)
//まだ上記の値です
print(bar)// {x:100、y:200}
この処理の仮定は、ほとんどの言語で使用されるオブジェクトが元の値ではないということです。
パスバイシェアは価値を通過する特別なケースです
共有によって配信する戦略は、Java、Ecmascript、Python、Ruby、Visual Basicなどの多くの言語で使用されます。さらに、Pythonコミュニティはこの用語を使用しており、他の名前が人々を混乱させる傾向があるため、他の言語で使用できます。ほとんどの場合、Java、EcMascript、またはVisual Basicなど、この戦略はPass -value-意味:特別価値 - リファレンスコピー(コピー)とも呼ばれます。
一方では、このようなものです - 関数に渡されるパラメーターは、バウンド値(参照アドレス)の名前にすぎず、外部オブジェクトには影響しません。
一方、これらの用語は、多くのフォーラムがJavaScript関数にオブジェクトを渡す方法について話しているため、実際に掘り下げることなく間違っていると考えられています。
一般的な理論には価値を渡すという言葉があります。しかし、この時点では、この値はアドレスコピー(コピー)と呼ばれるものであるため、ルールを破りません。
Rubyでは、この戦略は参照によりPassと呼ばれます。もう一度言っておきましょう。それは大きな構造のコピー(たとえば、値ではない)という点で渡されず、一方で、元のオブジェクトへの参照を処理せず、変更できません。したがって、このクロス期用語の概念はより混乱する可能性があります。
価値で渡された特別なケースのように、理論的には参照によって渡された特別なケースはありません。
しかし、上記のテクノロジーでこれらの戦略を理解する必要があります(Java、Ecmascript、Python、Ruby、その他)。実際、彼らが使用する戦略は、共有して通過することです。
共有とポインターを押します
с/→+の場合、この戦略はポインター値を通過することと同じですが、重要な違いがあります。この戦略はポインターを繰り返し、オブジェクトを完全に変更する可能性があります。ただし、一般に、値(アドレス)ポインターを新しいメモリブロックに割り当てる(つまり、以前に参照されたメモリブロックは変更されていないままです)。ポインターを介してオブジェクトプロパティを変更すると、Adongの外部オブジェクトに影響します。
したがって、ポインターカテゴリでは、これがアドレス値で渡されていることが明確にわかります。この場合、共有によって通過するのは、ポインター割り当ての動作(控除ではない)のような「合成糖」または参照のようなプロパティ(控除操作は不要)のような変更であり、「安全なポインター」と名付けられることがあります。
ただし、明らかなポインターの控えめなことなくオブジェクトプロパティを参照する場合、с/с + +も特別な構文糖を持っています。
コードコピーは次のとおりです。
obj-> xの代わりに(*obj).x
C ++に最も密接に関連するこのイデオロギーは、「スマートポインター」の実装から見ることができます。たとえば、Boost :: shared_ptrでは、割り当て演算子とコピーコンストラクターが過負荷になり、オブジェクトの参照カウンターも使用されてGCを介してオブジェクトを削除します。このデータ型には、同様の名前-SHARE_PTRもあります。
ECMAScriptの実装
これで、オブジェクトをecMascriptのパラメーターとして渡すポリシーがわかります - 共有:パラメーターのプロパティを変更すると外部に影響し、値を再割り当てしても外部オブジェクトには影響しません。ただし、上で述べたように、ECMAScript開発者は一般に、次のように呼びます。値は参照アドレスのコピーであることを除きます。
JavaScript Inventor Brendan Asheは次のように書いています。したがって、フォーラムで言及した誰もが言ったことは、この説明の下でも正しいです。
より正確には、この動作は単純な割り当てとして理解できます。内部は完全に異なるオブジェクトであることがわかりますが、同じ値が参照されています。つまり、アドレスコピーです。
ecmascriptコード:
コードコピーは次のとおりです。
var foo = {x:10、y:20};
var bar = foo;
アラート(bar === foo); // 真実
bar.x = 100;
bar.y = 200;
アラート([foo.x、foo.y]); // [100、200]
つまり、2つの識別子(名前バインディング)がメモリ内の同じオブジェクトにバインドされ、このオブジェクトを共有します。
foo値:addr(0xff)=> {x:100、y:200}(アドレス0xff)<= bar値:addr(0xff)
再割り当ての場合、バインディングは、以前にバインドされていたオブジェクトに影響を与えることなく、新しいオブジェクト識別子(新しいアドレス)です。
コードコピーは次のとおりです。
bar = {z:1、q:2};
アラート([foo.x、foo.y]); // [100、200]変更なし
アラート([bar.z、bar.q]); // [1、2]しかし、今では参照は新しいオブジェクトです
つまり、fooとbarにはさまざまな値と異なるアドレスがあります。
コードコピーは次のとおりです。
foo値:addr(0xff)=> {x:100、y:200}(アドレス0xff)
バー値:addr(0xfa)=> {z:1、q:2}(アドレス0xfa)
ここで言及したオブジェクトの値は、オブジェクト構造自体ではなくアドレスであり、変数を別の変数に割り当てることであることを再度強調します - 割り当てられた値への参照。したがって、2つの変数は同じメモリアドレスを参照します。次の割り当ては、古いオブジェクトのアドレスへのバインディングを解決し、新しいオブジェクトのアドレスにバインドする新しいアドレスです。これは、参照を通過することの最も重要な違いです。
さらに、ECMA-262標準によって提供される抽象化レベルのみを考慮する場合、渡された「値」(元の値またはオブジェクトになる可能性がある)を実装するアルゴリズムに「値」の概念のみが表示されますが、上記の定義に従って、参照アドレスが値も付いているため、「値で渡される」とも完全に呼ばれます。
ただし、誤解を避けるために(外部オブジェクトのプロパティを関数内で変更できる理由)、ここで実装レベルの詳細を検討する必要があります。
用語バージョン
ECMAScriptでこのポリシーの用語バージョンを定義しましょう。
「値によるパス」と呼ぶことができます。ここで言及されている値は特別なケースです。つまり、値はアドレスコピーです。このレベルからは、例外を除くecMascriptのオブジェクトは値によって渡されます。これは実際にはecmascript抽象化のレベルです。
または、この場合、それは特に「共有によって渡された」と呼ばれます。これにより、価値による通過と参照による通過の違いを確認できます。この状況は2つの状況に分けることができます。1:元の値は価値によって渡されます。 2:オブジェクトは共有によって渡されます。
「タイプを参照することで機能する」というフレーズは、ECMAScriptとは関係ありませんが、それは間違っています。
結論は
この投稿が、ECMAScriptのマクロレベルと実装の詳細を学ぶのに役立つことを願っています。いつものように、質問がある場合は、お気軽に議論してください。