ПРЕДУПРЕЖДЕНИЕ: использование этого метода в рабочем коде может привести к травме или смерти во время проверки кода. Используйте на свой риск.
Я несколько раз видел примерно следующий код для разброса числа в 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