Иногда нужно
распространять элементы массива, чтобы использовать их в качестве аргументов вызова функции. JavaScript позволяет вам делать это через Function.prototype.apply, но это не работает для вызовов конструктора. В этом посте рассказывается о распространении и о том, как заставить его работать в присутствии нового оператора.
распространение
Распространение означает выполнение вызова функции, вызова метода или конструктора и предоставление аргументов через массив. В Python это называется
распаковка . Для этой цели ECMAScript.next будет иметь
оператор распространения (префикс …). В текущем JavaScript вы можете выполнить эту операцию через Function.prototype.apply.
Пример: вызов функции. Math.max () возвращает максимум среди своих 0 или более числовых параметров. С оператором распространения вы можете использовать его для массивов:
Math.max(...[13, 7, 30])
Это эквивалент
Math.max(13, 7, 30)
В текущем JavaScript вы должны использовать apply ().
> Math.max.apply(null, [13, 7, 30]) 30
Объяснение: Вызов применения выглядит следующим образом:
func.apply(thisValue, [param1, param2, ...])
что эквивалентно
thisValue.func(param1, param2, ...)
Обратите внимание, что func не должен быть методом thisValue — apply временно превращает его в единицу.
Пример: вызов конструктора. Конструктор Date принимает несколько числовых параметров и создает дату. С помощью оператора распространения вы можете передать массив.
new Date(...[2011, 11, 24]) // Christmas 2011
Однако apply не работает с конструкторами, поскольку оператор new предполагает, что конечный вызов метода apply
является конструктором. Следующее является обходным путем:
new (Function.prototype.bind.apply( Date, [null].concat([2011, 11, 24])))
Что здесь происходит? Давайте посмотрим на основные компоненты:
- bind: мы используем этот метод, чтобы превратить Date в функцию с нулевыми параметрами, предварительно заполнив их. Вызов bind выглядит следующим образом:
func.bind(thisValue, [arg1], [arg2], ...)
Он превращает func в новую функцию, для которой неявным параметром this является thisValue и чьи начальные аргументы всегда соответствуют заданным Когда кто-то вызывает новую функцию, аргументы такого вызова добавляются к тому, что уже было предоставлено через bind. У MDN есть больше деталей . То, что мы хотим сделать, это следующее.
Date.bind(null, ...[2011, 11, 24])
Первый аргумент является нулевым, потому что bind превращает Date в функцию, которая не нуждается в thisValue: он вызывается только как конструктор, а new переопределяет thisValue из bind. Чтобы смоделировать вышеупомянутый оператор распространения, нам нужен метод apply.
- apply: опять же, мы используем apply, чтобы превратить массив в аргументы для вызова функции. Мы вызываем apply для функции Function.prototype.bind с двумя аргументами:
- 1-й аргумент: имеет значение Date
- 2-й аргумент: аргументы для привязки создаются путем добавления нулевого значения к массиву [2011, 11, 24]. Мы используем немного более многословный конкат вместо unshift, потому что он неразрушающий (не меняет это).
Выполненный вызов метода, таким образом,
Date.bind(null, 2011, 11, 24)
что совпадает с вышеупомянутым Date.bind (null, … [2011, 11, 24]).
- new: Вызвать функцию с нулевым аргументом, созданную bind, как конструктор. Помещая операнд в скобки, мы избегаем применения становления конструктором и вместо этого используем новый в результате применения.
Распространение конструктора через библиотечный метод
Mozilla предлагает превратить вышеуказанное решение в библиотечный метод. Ниже приведена слегка отредактированная версия
их предложения :
if (!Function.prototype.construct) { Function.prototype.construct = function(argArray) { if (! Array.isArray(argArray)) { throw new TypeError("Argument must be an array"); } var nullaryFunc = Function.prototype.bind.apply( this, [null].concat(argArray)); return new nullaryFunc(); }; }
Взаимодействие:
> Date.construct([2011, 11, 24]) Sat Dec 24 2011 00:00:00 GMT+0100 (CET)