Я знаю, что это используется, чтобы сделать аргументы реальным массивом, но я не понимаю, что происходит при использовании Array.prototype.slice.call(arguments)
Я знаю, что это используется, чтобы сделать аргументы реальным массивом, но я не понимаю, что происходит при использовании Array.prototype.slice.call(arguments)
Ответы:
Что происходит под капотом, так это то, что, когда .slice()
вызывается нормально,this
это массив, а затем он просто перебирает этот массив и выполняет свою работу.
Как this
в .slice()
функции массива? Потому что, когда вы делаете:
object.method();
... object
автоматически становится значением this
в method()
. Итак, с:
[1,2,3].slice()
... [1,2,3]
Массив устанавливается как значение this
в .slice()
.
Но что, если вы могли бы заменить что-то еще в качестве this
значения? Пока все, что вы заменяете, имеет числовое .length
свойство и кучу свойств, которые являются числовыми индексами, оно должно работать. Этот тип объекта часто называют массивоподобным объектом .
.call()
И .apply()
методы позволяют вручную установить значение this
в функции. Так что, если мы устанавливаем значение this
в .slice()
на массив типа объекта , .slice()
просто предположим , он работает с массивом, и сделаем свое дело.
Возьмите этот простой объект в качестве примера.
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
объект очень похож на массив, они совместимы. Это означает, что мы можем использовать методы массива с объектом arguments. А поскольку методы массивов были созданы с учетом массивов, они будут возвращать массивы, а не другие объекты аргументов.
Так зачем использовать Array.prototype
? Это Array
объект, для которого мы создаем новые массивы из ( new Array()
), и этим новым массивам передаются методы и свойства, такие как slice. Эти методы хранятся в [Class].prototype
объекте. Таким образом, для эффективности вместо доступа к методу слайса с помощью (new Array()).slice.call()
или [].slice.call()
, мы просто получаем его прямо из прототипа. Это так, нам не нужно инициализировать новый массив.
Но почему мы должны делать это в первую очередь? Ну, как вы сказали, он преобразует объект аргументов в экземпляр Array. Однако причина, по которой мы используем слайс, - это скорее взлом, чем что-либо другое. Метод среза примет, как вы уже догадались, срез массива и вернет этот срез в качестве нового массива. Передача ему без аргументов (кроме объекта arguments в качестве его контекста) заставляет метод slice взять полный кусок переданного «массива» (в данном случае объект arguments) и вернуть его как новый массив.
Обычно звонит
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
извлекает метод из «s прототипа . Но вызов его напрямую не сработает, так как это метод (а не функция)slice
Array
и поэтому ему необходим контекст (вызывающий объект this
), в противном случае он будет выброшен Uncaught TypeError: Array.prototype.slice called on null or undefined
.
call()
Метод позволяет определить контекст метода, в основном делает эти два вызова эквивалентны:
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 или оператор распространения:
let args = Array.from(arguments);
let args = [...arguments];
Это потому, что, как отмечает MDN
Объект arguments не является массивом. Он похож на массив, но не имеет никаких свойств массива, кроме длины. Например, у него нет метода pop. Однако его можно преобразовать в реальный массив:
Здесь мы обращаемся slice
к нативному объекту, Array
а не к его реализации, и поэтому дополнительный.prototype
var args = Array.prototype.slice.call(arguments);
Не забывайте, что низкоуровневые основы этого поведения - приведение типов, полностью интегрированное в JS-движок.
Slice просто берет объект (благодаря существующему свойству arguments.length) и возвращает массив-объект, приведенный после выполнения всех операций над ним.
Те же логики, которые вы можете проверить, если попытаетесь обработать String-метод значением INT:
String.prototype.bold.call(11); // returns "<b>11</b>"
И это объясняет утверждение выше.
Он использует slice
массивы методов и вызывает его this
как arguments
объект. Это означает , что она называет его , как если бы ты arguments.slice()
при условии ,arguments
у есть такой метод.
Создание среза без каких-либо аргументов просто займет все элементы - поэтому он просто копирует элементы arguments
в массив.
Предположим, у вас есть: function.apply(thisArg, argArray )
Метод apply вызывает функцию, передавая объект, который будет связан с этим, и необязательный массив аргументов.
Метод slice () выбирает часть массива и возвращает новый массив.
Поэтому, когда вы вызываете Array.prototype.slice.apply(arguments, [0])
метод среза массива, вызывается (bind) для аргументов.
Может быть, немного поздно, но ответ на весь этот беспорядок в том, что call () используется в JS для наследования. Если мы сравним это, например, с 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);
});
}