Я знаю, что это используется, чтобы сделать аргументы реальным массивом, но я не понимаю, что происходит при использовании 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);
});
}