web-dev-qa-db-ja.com

引数配列を使用したJavaScript関数のパラメーター値の変更が機能しない

私はJavaScriptを学習しており、argumentsプロパティ配列についてかなり混乱しています。

単一の引数を取り、それを返す関数があります。パラメータを渡し、_arguments[0] = value_を使用して再割り当てすると、値が更新されます。

_function a(b) {
  arguments[0] = 2;
  return b;
}
console.log(a(1)); //returns 2_

しかし、パラメーターなしで同じ関数を呼び出すと、undefinedが返されます。

_function a(b) {
  arguments[0] = 2;
  return b;
}
console.log(a()); //returns undefined_

しかし、undefinedを渡しても、値は更新されます。

_function a(b) {
  arguments[0] = 2;
  return b;
}
console.log(a(undefined)); //returns 2_

JavaScript関数にパラメーターを渡さない場合、パラメーターが自動的に作成され、値がundefinedに割り当てられ、更新後、更新された値が反映されるはずだと思いました。

また、a()a(undefined)は同じものですよね?

36
Amit Das

argumentsインデックスに割り当てると、関数が少なくともn引数で呼び出された場合にのみ、関連する引数値(n番目の引数と呼びます)が変更されます。 argumentsオブジェクトの数値インデックス付きプロパティは、基本的にsetters(およびゲッター)です。

http://es5.github.io/#x10.6

以下の斜体は、プロセスが質問にどのように関連するかについての私のコメントです。

(Let)args(be)[[Call]]内部メソッドに渡される実際の引数

  1. lenをargsの要素数とします。

  2. indx = len - 1とします。

  3. indx >= 0)の間に繰り返します。したがって、関数に引数が渡されない場合、以下のループは実行されません:

作成される引数オブジェクトに割り当てます。ここではmapと呼びます)

    1. リストの要素としてnameを追加しますmappedNames
    1. gを、引数MakeArgGetterおよびnameを使用してenv抽象演算を呼び出した結果とします。
    1. pを、引数MakeArgSetterおよびnameを使用してenv抽象演算を呼び出した結果とします。
    1. mapの[[DefineOwnProperty]]内部メソッドを呼び出し、ToString(indx)、プロパティ記述子{[[Set]]:p、[[Get]]を渡します: g、[[Configurable]]:true}、およびfalseを引数として。

したがって、関数が引数なしで呼び出された場合、arguments[0]にセッターはないため、再割り当てしてもインデックス0のパラメーターは変更されません。

同じようなことが他のインデックスでも発生します。1つのパラメーターで関数を呼び出したが、関数が2つのパラメーターを受け入れた場合、arguments[1]にセッターがないため、arguments[1]に割り当てても2番目のパラメーターは変更されません。

function fn(a, b) {
  arguments[1] = 'bar';
  console.log(b);
}
fn('foo');

そう

a()a(undefined)は同じものですか?

2番目の結果は、インデックス0のセッターとゲッターを持つargumentsオブジェクトになりますが、最初のオブジェクトはそうではないためです。

argumentsと関数パラメーターの間のこの奇妙な相互作用は、ずさんなモードでのみ存在することに注意してください。 strictモードでは、argumentsを変更しても、個々の引数識別子に含まれる値には影響しません。

'use strict';
function a(b) {
  arguments[0] = 2;
  return b;
}
console.log(a(1)); //returns 1
24

ECMA 262 9.0 2018 はこの動作を 9.4.4引数で説明しますエキゾチックオブジェクト with

注1:

数値の名前の値が対応する 関数オブジェクト の仮パラメーターの数より少ない引数のエキゾチックオブジェクトの整数インデックスデータプロパティは、最初に、関数の対応する引数バインディングと値を共有します 実行コンテキスト 。つまり、プロパティを変更すると、引数バインディングの対応する値が変更され、逆も同様です。このようなプロパティが削除されてから再定義された場合、またはプロパティが アクセサプロパティ に変更された場合、この対応は壊れます。引数オブジェクトが通常のオブジェクトである場合、そのプロパティの値は関数に渡された引数の単なるコピーであり、プロパティ値と仮パラメーター値の間に動的リンクはありません。

要するに、

  • 場合は'sloppy mode'、長さが指定されたパラメーターに対応している場合、すべての引数はそれらの名前付き変数にマップされます。

  • 場合は'strict mode'の場合、引数を渡した後、バインディングは失われます。

これは、古いバージョンの ECMA 262 7.0 2016 でのみ読み取ることができます。 9.4.4 Arguments Exotic Objects でこの動作を説明します

注1:

非厳密関数の場合、数値名の値が対応する関数オブジェクトの仮パラメーターの数よりも少ない引数オブジェクトの整数のインデックス付きデータプロパティは、最初に、その値を関数の実行コンテキストの対応する引数バインディングと共有します。つまり、プロパティを変更すると、引数バインディングの対応する値が変更され、逆も同様です。このようなプロパティが削除されてから再定義された場合、またはプロパティがアクセサプロパティに変更された場合、この対応は壊れます。 strictモードの関数の場合、argumentsオブジェクトのプロパティの値は、関数に渡された引数の単なるコピーであり、プロパティ値と仮パラメーター値の間に動的なリンクはありません。

6
Nina Scholz

これは、引数が配列とは異なり、整数のインデックス付きデータキーとプロパティの長さを持つオブジェクトであるためです。また、長さがゼロの場合、引数がないことを意味します

function a(b) {
    arguments[0] = 2;
    console.log(arguments.length) 
    return b;
}
a(1); // length 1  returns 2
console.log(a());  // length 0  returns undefined
1
Vadim Hulevich

私の理解では、argumentsオブジェクトは関数に渡されたものだけを追跡します。最初は何も渡していないため、bはバインドされておらず、その時点ではargumentsは「追跡」していませんb。次に、初期化されたが空の配列のようなオブジェクトargumentsに値を割り当て、最後に未定義のbを返します。

これをさらに深く掘り下げるには:

非厳密関数に残余、デフォルト、または非構造化パラメーターが含まれていない場合、引数オブジェクトの値は引数変数の値と同期して変化します。以下のコードを参照してください。

function func(a) { 
  arguments[0] = 99; // updating arguments[0] also updates a
  console.log(a);
}
func(10); // 99

そして

function func(a) { 
  a = 99; // updating a also updates arguments[0]
  console.log(arguments[0]);
}
func(10); // 99

非厳密関数にREST、デフォルト、または非構造化パラメーターが含まれている場合、引数オブジェクトの値は引数の値を追跡しません。代わりに、関数が呼び出されたときに提供された引数を反映します。

function func(a = 55) { 
  arguments[0] = 99; // updating arguments[0] does not also update a
  console.log(a);
}
func(10); // 10

そして

function func(a = 55) { 
  a = 99; // updating a does not also update arguments[0]
  console.log(arguments[0]);
}
func(10); // 10

そして

// An untracked default parameter
function func(a = 55) { 
  console.log(arguments[0]);
}
func(); // undefined

出典: MDN Web docs

1
Barzev

パラメータを指定しない場合、arguments配列はlengthが0に等しくなります。次に、配列の存在しない要素を2に設定しようとすると、未定義が返されます。

このスニペットでこれを簡単にテストできます:

function a(b){ 
    alert(arguments.length) // It will Prompt 0 when calling a() and 1 when calling a(undefined)
    arguments[0] = 2; 
    return b; 
}
1
puffy.bun

これは、JavaScript仕様の未定義の値の定義です。

変数に値が割り当てられていないときに使用されるプリミティブ値

したがって、関数の戻り値の型を指定しないと、未定義が返されます。

したがって、a()とa(undefined)は同じではありません。undefinedを返すかどうかは、戻り値の型が定義されているかどうかに基づいています。

より明確にするために similar_problem

1
sathish kumar