Wiem, że służy on do przekształcenia argumentów w prawdziwą tablicę, ale nie rozumiem, co się dzieje podczas używania Array.prototype.slice.call(arguments)
Wiem, że służy on do przekształcenia argumentów w prawdziwą tablicę, ale nie rozumiem, co się dzieje podczas używania Array.prototype.slice.call(arguments)
Odpowiedzi:
To, co dzieje się pod maską, polega na tym, że gdy .slice()
jest wywoływane normalnie, this
jest tablicą, a następnie po prostu iteruje po tej tablicy i wykonuje swoją pracę.
Jak this
w tej .slice()
funkcji jest tablica? Ponieważ kiedy to zrobisz:
object.method();
... object
automatycznie staje się wartością this
w method()
. Więc z:
[1,2,3].slice()
... [1,2,3]
tablica jest ustawiona jako wartość this
in .slice()
.
Ale co, jeśli możesz podstawić coś innego jako this
wartość? Tak długo, jak cokolwiek, co podstawisz, ma .length
właściwość numeryczną i kilka właściwości, które są indeksami numerycznymi, powinno działać. Ten typ obiektu jest często nazywany obiektem tablicowym .
.call()
I .apply()
metody pozwala ręcznie ustawić wartość this
w funkcji. Więc jeśli ustawimy wartość this
in .slice()
na obiekt podobny do tablicy , .slice()
po prostu założymy, że działa on z tablicą i zrobi swoje.
Weź ten prosty obiekt jako przykład.
var my_object = {
'0': 'zero',
'1': 'one',
'2': 'two',
'3': 'three',
'4': 'four',
length: 5
};
Oczywiście nie jest to tablica, ale jeśli możesz ustawić ją jako this
wartość .slice()
, to po prostu będzie działać, ponieważ wygląda wystarczająco podobnie do tablicy, .slice()
aby działała poprawnie.
var sliced = Array.prototype.slice.call( my_object, 3 );
Przykład: http://jsfiddle.net/wSvkv/
Jak widać w konsoli, wynik jest taki, jakiego oczekujemy:
['three','four'];
Tak dzieje się, gdy ustawisz arguments
obiekt jako this
wartość .slice()
. Ponieważ arguments
ma .length
właściwość i kilka indeksów liczbowych, .slice()
po prostu działa tak, jakby działał na prawdziwej macierzy.
Array.prototype.slice
opisu metody.
for-in
stwierdzenie nie gwarantuje porządku. Algorytm używany przez .slice()
definiuje porządek numeryczny rozpoczynający się 0
i kończący (nie włącznie) z .length
danym obiektem (lub tablicą lub czymkolwiek). Dzięki temu kolejność jest gwarantowana we wszystkich implementacjach.
var obj = {2:"two", 0:"zero", 1: "one"}
. Jeśli użyjemy for-in
do wyliczenia obiektu, nie ma gwarancji porządku. Ale jeśli używamy for
, możemy ręcznie wymusić kolejność: for (var i = 0; i < 3; i++) { console.log(obj[i]); }
. Teraz wiemy, że właściwości obiektu zostaną osiągnięte w rosnącej kolejności numerycznej, którą zdefiniowaliśmy w naszej for
pętli. Właśnie to .slice()
robi. Nie ma znaczenia, czy ma on rzeczywistą macierz. Rozpoczyna się od 0
i uzyskuje dostęp do właściwości w rosnącej pętli.
arguments
Obiekt nie jest faktycznie instancja Array, a nie ma żadnej z metod Array. Więc arguments.slice(...)
nie będzie działać, ponieważ obiekt argumentów nie ma metody plastra.
Tablice mają tę metodę, a ponieważ arguments
obiekt jest bardzo podobny do tablicy, dwie są kompatybilne. Oznacza to, że możemy korzystać z metod tablicowych z argumentami sprzeciwu. A ponieważ metody tablicowe zostały zbudowane z myślą o tablicach, zwracają tablice zamiast innych obiektów argumentów.
Dlaczego więc korzystać Array.prototype
? Jest Array
to obiekt, z którego tworzymy nowe tablice z ( new Array()
), a te nowe tablice są przekazywane metodami i właściwościami, takimi jak plasterek. Te metody są przechowywane w [Class].prototype
obiekcie. Tak więc, ze względu na wydajność, zamiast uzyskiwać dostęp do metody wycinania za pomocą (new Array()).slice.call()
lub [].slice.call()
, po prostu pobieramy ją prosto z prototypu. Dzieje się tak, dlatego nie musimy inicjować nowej tablicy.
Ale dlaczego musimy to zrobić w pierwszej kolejności? Cóż, jak powiedziałeś, przekształca obiekt argumentów w instancję Array. Powodem, dla którego używamy wycinka, jest bardziej „hack” niż cokolwiek innego. Metoda wycinania weźmie, jak zgadłeś, wycinek tablicy i zwróci ten wycinek jako nową tablicę. Nie przekazanie do niego żadnych argumentów (oprócz obiektu argumentów jako kontekstu) powoduje, że metoda slice pobiera pełny fragment przekazanej „tablicy” (w tym przypadku obiekt argumentów) i zwraca ją jako nową tablicę.
Zwykle dzwonienie
var b = a.slice();
skopiuje tablicę a
do b
. Jednak nie możemy tego zrobić
var a = arguments.slice();
ponieważ arguments
nie jest prawdziwą tablicą i nie ma slice
jako metody. Array.prototype.slice
jest slice
funkcją dla tablic i call
uruchamia funkcję z this
ustawioną na arguments
.
prototype
? nie slice
jest rodzimą Array
metodą?
Array
jest to funkcja konstruktora, a odpowiednia „klasa” to Array.prototype
. Możesz także użyć[].slice
slice
to metoda każdej Array
instancji, ale nie Array
funkcja konstruktora. Służysz prototype
do uzyskiwania dostępu do metod teoretycznych wystąpień konstruktora.
Najpierw powinieneś przeczytać, jak działa wywoływanie funkcji w JavaScript . Podejrzewam, że sam wystarczy, aby odpowiedzieć na twoje pytanie. Ale oto podsumowanie tego, co się dzieje:
Array.prototype.slice
wyodrębnia sposób z „S prototypu . Ale bezpośrednie wywołanie nie zadziała, ponieważ jest to metoda (a nie funkcja) i dlatego wymaga kontekstu (obiektu wywołującego ), w przeciwnym razie zostałby rzucony .slice
Array
this
Uncaught TypeError: Array.prototype.slice called on null or undefined
call()
Metoda pozwala określić kontekst sposobu, w zasadzie czyni te dwa połączenia równoważne:
someObject.slice(1, 2);
slice.call(someObject, 1, 2);
Poza tym pierwszym wymaga, aby slice
metoda istniała w someObject
łańcuchu prototypów (tak jak ma to miejsce w przypadku Array
), podczas gdy ten drugi umożliwia someObject
ręczne przekazanie kontekstu ( ) do metody.
To drugie jest również skrótem od:
var slice = Array.prototype.slice;
slice.call(someObject, 1, 2);
Który jest taki sam jak:
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) to staromodny sposób konwersji argumentów na tablicę.
W ECMAScript 2015 możesz użyć Array.from lub operatora rozprzestrzeniania:
let args = Array.from(arguments);
let args = [...arguments];
Jest tak, ponieważ, jak zauważa MDN
Obiekt argumentów nie jest tablicą. Jest podobny do tablicy, ale nie ma żadnych właściwości tablicy poza długością. Na przykład nie ma metody pop. Można go jednak przekonwertować na prawdziwą tablicę:
Wzywamy tutaj slice
natywny obiekt, Array
a nie jego implementację, i dlatego dodatkowe.prototype
var args = Array.prototype.slice.call(arguments);
Nie zapominaj, że niskim poziomem podstaw tego zachowania jest rzutowanie typu, które całkowicie zintegrowało się z silnikiem JS.
Slice po prostu pobiera obiekt (dzięki istniejącej właściwości arguments.length) i zwraca rzutowany obiekt tablicowy po wykonaniu wszystkich operacji na tym.
Te same logiki, które możesz przetestować, jeśli spróbujesz traktować metodę String za pomocą wartości INT:
String.prototype.bold.call(11); // returns "<b>11</b>"
I to wyjaśnia powyższe stwierdzenie.
Wykorzystuje slice
tablice metod i wywołuje je, this
będąc arguments
obiektem. Oznacza to, że nazywa go jako jeśli nie arguments.slice()
zakładając arguments
miał taką metodę.
Utworzenie wycinka bez żadnych argumentów po prostu zabierze wszystkie elementy - więc po prostu skopiuje je z arguments
tablicy do tablicy.
Załóżmy, że masz: function.apply(thisArg, argArray )
Metoda Apply wywołuje funkcję, przekazując obiekt, który będzie z nią związany, oraz opcjonalną tablicę argumentów.
Metoda slice () wybiera część tablicy i zwraca nową tablicę.
Więc kiedy wywołujesz Array.prototype.slice.apply(arguments, [0])
tablicę, wywoływana jest metoda argumentu (bind).
Może trochę późno, ale odpowiedzią na cały ten bałagan jest to, że call () jest używane w JS do dziedziczenia. Jeśli porównamy to na przykład do Pythona lub PHP, wywołanie zostanie użyte odpowiednio jako super (). init () lub parent :: _ construct ().
To jest przykład użycia, który wyjaśnia wszystkie:
function Teacher(first, last, age, gender, interests, subject) {
Person.call(this, first, last, age, gender, interests);
this.subject = subject;
}
Odniesienie: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance
gdy .slice () jest wywoływane normalnie, jest to Array, a następnie po prostu iteruje tę tablicę i wykonuje swoją pracę.
//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"]
Piszę to, żeby sobie przypomnieć ...
Array.prototype.slice.call(arguments);
== Array.prototype.slice(arguments[1], arguments[2], arguments[3], ...)
== [ arguments[1], arguments[2], arguments[3], ... ]
Lub po prostu użyj tej przydatnej funkcji $ A, aby zamienić większość rzeczy w tablicę.
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);
}
przykładowe użycie ...
function test() {
$A( arguments ).forEach( function(arg) {
console.log("Argument: " + arg);
});
}