web-dev-qa-db-fra.com

Comment fonctionne Array.prototype.slice.call ()?

Je sais qu'il est utilisé pour transformer les arguments en un véritable tableau, mais je ne comprends pas ce qui se passe lors de l'utilisation de Array.prototype.slice.call(arguments)

446
ilyo

Ce qui se passe sous le capot est que lorsque .slice() est appelée normalement, this est un tableau, puis il itère simplement sur ce tableau et fait son travail.

Comment this dans .slice() fonctionne-t-il avec un tableau? Parce que quand tu fais:

object.method();

... la object devient automatiquement la valeur de this dans la method(). Donc avec:

[1,2,3].slice()

... le [1,2,3] Array est défini avec la valeur de this dans .slice().


Mais que se passerait-il si vous pouviez remplacer la valeur this par quelque chose d'autre? Tant que ce que vous substituez aura une propriété numérique .length et un ensemble de propriétés qui sont des index numériques, cela devrait fonctionner. Ce type d'objet est souvent appelé objet de type tableau .

Les méthodes .call() et .apply() vous permettent de définir manuellement la valeur de this dans une fonction. Donc, si nous définissons la valeur de this dans .slice() sur un objet semblable à un tableau , .slice() sera juste suppose que cela fonctionne avec un tableau, et fera son travail.

Prenez cet objet simple à titre d'exemple.

var my_object = {
    '0': 'zero',
    '1': 'one',
    '2': 'two',
    '3': 'three',
    '4': 'four',
    length: 5
};

Ce n'est évidemment pas un tableau, mais si vous pouvez le définir comme la valeur this de .slice(), alors cela fonctionnera, car il ressemble assez à un tableau pour que .slice() fonctionne correctement .

var sliced = Array.prototype.slice.call( my_object, 3 );

Exemple: http://jsfiddle.net/wSvkv/

Comme vous pouvez le voir dans la console, le résultat est ce que nous attendons:

['three','four'];

C'est donc ce qui se produit lorsque vous définissez un objet arguments comme valeur this de .slice(). Étant donné que arguments a une propriété .length et de nombreux index numériques, .slice() poursuit son travail comme s'il travaillait sur un véritable tableau.

839
user113716

L'objet arguments n'est pas réellement une instance d'un tableau et ne possède aucune des méthodes de tableau. Donc, arguments.slice(...) ne fonctionnera pas car l'objet arguments n'a pas la méthode slice.

Les tableaux ont cette méthode, et comme l'objet arguments est très similaire à un tableau, les deux sont compatibles. Cela signifie que nous pouvons utiliser des méthodes de tableau avec l'objet arguments. Et puisque les méthodes de tableau ont été construites avec les tableaux à l'esprit, elles renverront des tableaux plutôt que d'autres objets d'argument.

Alors pourquoi utiliser Array.prototype? La Array est l'objet à partir duquel nous créons de nouveaux tableaux (new Array()), et ces nouveaux tableaux sont des méthodes et des propriétés transmises, comme slice. Ces méthodes sont stockées dans l'objet [Class].prototype. Ainsi, par souci d'efficacité, au lieu d'accéder à la méthode slice par (new Array()).slice.call() ou [].slice.call(), nous l'obtenons directement à partir du prototype. Cela évite d'initialiser un nouveau tableau.

Mais pourquoi devons-nous faire cela en premier lieu? Comme vous l'avez dit, il convertit un objet arguments en instance Array. La raison pour laquelle nous utilisons slice, cependant, est plus un "hack" qu'autre chose. La méthode slice prendra une tranche d'un tableau, vous l'aurez deviné, et retournera cette tranche sous la forme d'un nouveau tableau. En ne lui passant aucun argument (hormis l'objet arguments en tant que contexte), la méthode slice prend un bloc complet du "tableau" passé (dans ce cas, l'objet arguments) et le renvoie sous la forme d'un nouveau tableau.

81
Ben Fleming

Normalement, en appelant

var b = a.slice();

va copier le tableau a dans b. Cependant, nous ne pouvons pas faire

var a = arguments.slice();

parce que arguments n'est pas un vrai tableau et n'a pas slice comme méthode. Array.prototype.slice est la fonction slice pour les tableaux et call exécute la fonction avec this réglé sur arguments.

42
Delan Azabani
// 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]
21
sam

Tout d’abord, vous devriez lire comment l’appel de fonction fonctionne en JavaScript . Je suppose que cela suffit à répondre à votre question. Mais voici un résumé de ce qui se passe:

Array.prototype.slice extrait le sliceméthode à partir de Array's prototype . Mais l'appeler directement ne fonctionnera pas, comme c'est une méthode (pas une fonction) et nécessite donc un contexte (un objet appelant, this), sinon il lancerait Uncaught TypeError: Array.prototype.slice called on null or undefined.

La méthode call() vous permet de spécifier le contexte d'une méthode, en rendant fondamentalement ces deux appels équivalents:

someObject.slice(1, 2);
slice.call(someObject, 1, 2);

Sauf que le premier nécessite que la méthode slice existe dans la chaîne de prototypes de someObject (comme pour Array), tandis que le dernier permet au contexte (someObject) d'être passé manuellement à la méthode.

En outre, ce dernier est court pour:

var slice = Array.prototype.slice;
slice.call(someObject, 1, 2);

Quel est le même que:

Array.prototype.slice.call(someObject, 1, 2);
18
Marco Roy

Array.prototype.slice.call (arguments) est la méthode traditionnelle pour convertir un argument en tableau.

Dans ECMAScript 2015, vous pouvez utiliser Array.from ou l'opérateur de propagation:

let args = Array.from(arguments);

let args = [...arguments];
12
Alexander Moiseyev

C'est parce que, comme notes MDN

L'objet arguments n'est pas un tableau. Il est similaire à un tableau, mais ne possède aucune propriété de tableau à l'exception de la longueur. Par exemple, il n'a pas la méthode pop. Cependant, il peut être converti en un tableau réel:

Ici, nous appelons slice sur l’objet natif Array et non sur son implémentation et c’est pourquoi l’extra .prototype

var args = Array.prototype.slice.call(arguments);
9
naveen

N'oubliez pas que l'un des fondements basiques de ce comportement est la transposition de caractères intégrée entièrement dans JS-engine.

Slice ne prend que des objets (grâce à la propriété arguments.length existante) et retourne un objet tableau après que toutes les opérations aient été effectuées.

Les mêmes logiques que vous pouvez tester si vous essayez de traiter String-method avec une valeur INT:

String.prototype.bold.call(11);  // returns "<b>11</b>"

Et cela explique la déclaration ci-dessus.

4
Andrey

Il utilise la méthode slice dont disposent les tableaux et l'appelle avec son objet this étant l'objet arguments. Cela signifie qu’il l’appelle comme si vous aviez arguments.slice() en supposant que arguments avait une telle méthode.

La création d'une tranche sans aucun argument prendra simplement tous les éléments. Elle sera donc simplement copiée des éléments de arguments dans un tableau.

1
ThiefMaster

Supposons que vous avez: function.apply(thisArg, argArray )

La méthode apply appelle une fonction en transmettant l'objet qui sera lié à celle-ci ainsi qu'un tableau facultatif d'arguments.

La méthode slice () sélectionne une partie d'un tableau et renvoie le nouveau tableau.

Ainsi, lorsque vous appelez Array.prototype.slice.apply(arguments, [0]), la méthode de division en tableau est invoquée (bind) sur des arguments.

1
user278064

Peut-être un peu tard, mais la réponse à tout ce désordre est que call () est utilisé dans JS pour l'héritage. Si nous comparons ceci à Python ou à PHP, par exemple, call est utilisé respectivement comme super (). init () ou parent ::_construction().

Ceci est un exemple de son utilisation qui clarifie tout:

function Teacher(first, last, age, gender, interests, subject) {
  Person.call(this, first, last, age, gender, interests);

  this.subject = subject;
}

Référence: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance

1
Davide Pugliese

lorsque .slice () est appelé normalement, il s’agit d’un tableau, puis il itère sur ce tableau et fait son travail.

 //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"]
0
Pablo