回答:
内部で何が起こるか.slice()
というと、通常はthis
がArrayであり、それがArrayを反復処理し、その処理を実行するだけです。
どのようthis
に.slice()
配列が機能?あなたがするとき:
object.method();
... object
自動的にの値this
になりmethod()
ます。だから:
[1,2,3].slice()
... [1,2,3]
配列はthis
in の値として設定されます.slice()
。
しかし、this
値を他のものに置き換えることができるとしたらどうでしょうか。置換するものが数値.length
プロパティを持ち、数値インデックスである一連のプロパティを持っている限り、それは機能するはずです。このタイプのオブジェクトは、配列のようなオブジェクトと呼ばれることがよくあります。
.call()
および.apply()
方法は、あなたが聞かせて手動での値を設定するthis
機能で。我々はの値を設定するのであればthis
中.slice()
に配列のようなオブジェクト、.slice()
ちょうどますと仮定し、それはArrayで働いているし、そのことを行います。
例として、この単純なオブジェクトを取り上げます。
var my_object = {
'0': 'zero',
'1': 'one',
'2': 'two',
'3': 'three',
'4': 'four',
length: 5
};
これは明らかに配列ではありませんが、のthis
値として設定できる場合は.slice()
、.slice()
正しく機能するための配列のように見えるため、機能します。
var sliced = Array.prototype.slice.call( my_object, 3 );
例: http : //jsfiddle.net/wSvkv/
コンソールで確認できるように、結果は期待どおりです。
['three','four'];
つまり、arguments
オブジェクトをのthis
値として設定すると、これが起こります.slice()
。arguments
には.length
プロパティと一連の数値インデックスがあるため.slice()
、実際の配列で作業しているかのように作業を進めます。
Array.prototype.slice
とおりです。
for-in
順序を保証しないステートメントです。で使用されるアルゴリズムは、指定されたオブジェクト(または配列など)ので.slice()
始まり、含まれ0
ない(非包括的)数値順序を定義します.length
。したがって、順序はすべての実装にわたって一貫していることが保証されています。
var obj = {2:"two", 0:"zero", 1: "one"}
。for-in
オブジェクトの列挙に使用する場合、順序は保証されません。ただし、を使用する場合for
、手動で順序を強制できますfor (var i = 0; i < 3; i++) { console.log(obj[i]); }
。これで、for
ループで定義した数値の昇順でオブジェクトのプロパティに到達することがわかりました。それが何をするか.slice()
です。実際の配列があるかどうかは関係ありません。それは単に開始し0
、昇順ループでプロパティにアクセスします。
arguments
オブジェクトは、実際には配列のインスタンスではなく、配列のいずれかの方法を持っていません。したがって、arguments.slice(...)
argumentsオブジェクトにはsliceメソッドがないため、機能しません。
配列にはこのメソッドがあり、arguments
オブジェクトは配列に非常に似ているため、2つは互換性があります。これは、argumentsオブジェクトで配列メソッドを使用できることを意味します。また、配列メソッドは配列を考慮して作成されているため、他の引数オブジェクトではなく配列を返します。
なぜ使用するのArray.prototype
ですか?Array
我々は(から新しいアレイを作成するオブジェクトであるnew Array()
)、およびこれらの新しいアレイは、スライスのように、メソッドとプロパティを渡されます。これらのメソッドは[Class].prototype
オブジェクトに格納されます。したがって、効率を上げるために、(new Array()).slice.call()
または[].slice.call()
でスライスメソッドにアクセスする代わりに、プロトタイプから直接取得します。これは、新しい配列を初期化する必要がないようにするためです。
しかし、なぜこれを最初に行う必要があるのでしょうか。さて、あなたが言ったように、それは引数オブジェクトを配列インスタンスに変換します。ただし、sliceを使用する理由は、何よりも「ハック」に近いものです。あなたが推測したとおり、sliceメソッドは配列のスライスを取り、そのスライスを新しい配列として返します。引数を渡さない場合(引数オブジェクトをコンテキストとして)、sliceメソッドは渡された "配列"(この場合は、引数オブジェクト)の完全なチャンクを取得して、新しい配列として返します。
通常、呼び出す
var b = a.slice();
配列をにコピーa
しb
ます。しかし、私たちはできません
var a = arguments.slice();
これarguments
は、実際の配列ではなくslice
、メソッドとしてもないためです。Array.prototype.slice
はslice
配列call
の関数で、にthis
設定して関数を実行しますarguments
。
prototype
ですか?slice
ネイティブArray
メソッドではないですか?
Array
はコンストラクタ関数であり、対応する「クラス」はであることに注意してくださいArray.prototype
。次も使用できます[].slice
slice
は各Array
インスタンスのメソッドですが、Array
コンストラクター関数ではありません。prototype
コンストラクターの理論上のインスタンスのメソッドにアクセスするために使用します。
まず、JavaScriptで関数呼び出しがどのように機能するかをお読みください。私はあなたの質問に答えるには一人で十分だと思います。しかし、これが起こっていることの要約です:
Array.prototype.slice
のプロトタイプからメソッドを抽出します。ただし、これはメソッド(関数ではない)であり、コンテキスト(呼び出し元のオブジェクト)が必要であるため、直接呼び出しても機能しません。それ以外の場合はをスローします。slice
Array
this
Uncaught TypeError: Array.prototype.slice called on null or undefined
call()
この方法は、基本的には、これら2つのコールが同等作り、あなたがメソッドのコンテキストを指定することができます:
someObject.slice(1, 2);
slice.call(someObject, 1, 2);
前者を除いて、slice
メソッドはsomeObject
のプロトタイプチェーンに存在する必要Array
があります(のようにsomeObject
)。後者では、コンテキスト()を手動でメソッドに渡すことができます。
また、後者は以下の略です。
var slice = Array.prototype.slice;
slice.call(someObject, 1, 2);
これは次と同じです:
Array.prototype.slice.call(someObject, 1, 2);
// We can apply `slice` from `Array.prototype`:
Array.prototype.slice.call([]); //-> []
// Since `slice` is available on an array's prototype chain,
'slice' in []; //-> true
[].slice === Array.prototype.slice; //-> true
// … we can just invoke it directly:
[].slice(); //-> []
// `arguments` has no `slice` method
'slice' in arguments; //-> false
// … but we can apply it the same way:
Array.prototype.slice.call(arguments); //-> […]
// In fact, though `slice` belongs to `Array.prototype`,
// it can operate on any array-like object:
Array.prototype.slice.call({0: 1, length: 1}); //-> [1]
Array.prototype.slice.call(arguments)は、引数を配列に変換する昔ながらの方法です。
ECMAScript 2015では、Array.fromまたはspread演算子を使用できます。
let args = Array.from(arguments);
let args = [...arguments];
その理由は、MDNが注記しているように
引数オブジェクトは配列ではありません。これは配列に似ていますが、長さ以外の配列プロパティはありません。たとえば、popメソッドはありません。ただし、実際の配列に変換できます。
ここでは、その実装ではなくslice
ネイティブオブジェクトArray
を呼び出しています。そのため、追加の.prototype
var args = Array.prototype.slice.call(arguments);
あなたが持っていると仮定しましょう: function.apply(thisArg, argArray )
applyメソッドは関数を呼び出し、これにバインドされるオブジェクトとオプションの引数の配列を渡します。
slice()メソッドは配列の一部を選択し、新しい配列を返します。
したがってArray.prototype.slice.apply(arguments, [0])
、配列スライスメソッドを呼び出すと、引数に対してメソッド(バインド)が呼び出されます。
少し遅いかもしれませんが、この混乱のすべてに対する答えは、JSで継承のためにcall()が使用されることです。これをPythonやPHPと比較すると、たとえば、callはそれぞれsuper()として使用されます。init()またはparent :: _ construct()。
これは、すべてを明確にする使用例です。
function Teacher(first, last, age, gender, interests, subject) {
Person.call(this, first, last, age, gender, interests);
this.subject = subject;
}
リファレンス:https : //developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance
.slice()が正常に呼び出された場合、これは配列であり、その配列を反復処理するだけです。
//ARGUMENTS
function func(){
console.log(arguments);//[1, 2, 3, 4]
//var arrArguments = arguments.slice();//Uncaught TypeError: undefined is not a function
var arrArguments = [].slice.call(arguments);//cp array with explicity THIS
arrArguments.push('new');
console.log(arrArguments)
}
func(1,2,3,4)//[1, 2, 3, 4, "new"]
私はこれを思い出させるためにこれを書いています...
Array.prototype.slice.call(arguments);
== Array.prototype.slice(arguments[1], arguments[2], arguments[3], ...)
== [ arguments[1], arguments[2], arguments[3], ... ]
または、この便利な関数$ Aを使用して、ほとんどのものを配列に変換します。
function hasArrayNature(a) {
return !!a && (typeof a == "object" || typeof a == "function") && "length" in a && !("setInterval" in a) && (Object.prototype.toString.call(a) === "[object Array]" || "callee" in a || "item" in a);
}
function $A(b) {
if (!hasArrayNature(b)) return [ b ];
if (b.item) {
var a = b.length, c = new Array(a);
while (a--) c[a] = b[a];
return c;
}
return Array.prototype.slice.call(b);
}
使用例...
function test() {
$A( arguments ).forEach( function(arg) {
console.log("Argument: " + arg);
});
}