ПРЕДУПРЕЖДЕНИЕ: использование этого метода в рабочем коде может привести к травме или смерти во время проверки кода. Используйте на свой риск.

Я несколько раз видел примерно следующий код для разброса числа в JS:

Number.prototype[Symbol.iterator] = function *range() {
  for (let i = 0; i <= this; i++) {
    yield i;
  }
}
const numbers = [...3]
console.log(numbers); // [0, 1, 2, 3]

(Если вы не понимаете, что здесь происходит, мы превращаем объект в итерируемый, давая ему метод итератора — MDN объясняет это очень хорошо)

Думаю, это круто, но диапазон всегда начинается с фиксированного значения (в данном случае с 0) — что доставляет неудобства. Было бы здорово, если бы мы могли подняться на ступеньку выше и сделать это:

console.log([3...7])

Ну, черт возьми, мы не можем. Итак, как насчет этого?

console.log(3[...7])

Опять же, нет — Uncaught SyntaxError: expected expression, got '...'.

Не совсем так красиво, но как насчет этого?

console.log(3[[...7]])

Это дает нам еще одну ошибку: Uncaught TypeError: 7 is not iterable - но, как мы видели выше, мы МОЖЕМ сделать числа повторяемыми, и запуск приведенного выше кода после первого выполнения этого приводит к... ошибкам, и в консоли регистрируется «undefined». Похоже, у нас может быть путь к решению...

Короче говоря, я придумал следующее решение (да, я ненормальный). Он сочетает в себе возможность повторения числа (но не так, как описано выше) с некоторыми идеями из Metho, Metho-Number и Turboprop. Золотые звезды всем, кто следит за тем, как это работает ⭐:

// When we 'spread' a number, set up a temporary method on the Number
// prototype that will return our final result, and get the Symbol
// that is the 'name' of this method
Number.prototype[Symbol.iterator] = function *range() {
  yield attachTempNumRangeMethod(+this)
}

// Create a preconfigured range method, and attach it to the Number
// prototype (idea from Metho)
function attachTempNumRangeMethod(rangeEnd) {
  const s = Symbol()
  Object.defineProperty(
    Number.prototype, s,
    { configurable: true, get: makeRangeMethod(rangeEnd, s) }
  )
  return s
}

// Create a function to count from or to the given end value, then
// delete itself from the Number prototype (idea from Metho-number)
function makeRangeMethod(end, sym) {
  return function range() {
    const step = this<=end ? 1 : -1
    let arr = [], i, d = end>this
    for (i=+this; d ? i<=end : i>=end; i+=step) arr.push(i)
    delete Number.prototype[sym]
    return arr
  }
}

// Set up a 'toPrimitive' method on the Array prototype so we can
// hijack it if we spot an array containing a Number method symbol
// we previously set up (idea from Turboprop)
Array.prototype[Symbol.toPrimitive] = function (hint) {
  // this is dangerous, so try to and leave all default behaviour alone:
  if (hint === 'default') return this.toString()
  if (hint === 'number') return Number(this.toString())
  if (this.length != 1 && !Number.prototype.hasOwnProperty(this[0]))
    return this.toString()
  // return our symbol that names the Number method
  return this[0]
}

Все еще со мной ? Давайте проверим это:

37[[...42]].forEach(n => console.log(n))
// 37
// 38
// 39
// 40
// 41
// 42

console.log(10[[...1]])
// Array(10) [ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]

TA-DA 🎉

Настройтесь снова в следующий раз, чтобы узнать больше о нарушениях синтаксиса JavaScript!

дальнейшее чтение







Первоначально опубликовано наhttps://dev.to/jonrandy/js-magic-making-a-range-syntax-55im