Вопрос на интервью по Javascript: make [1,2,3] .sum () run
Javascript интервью вопрос: сделать [1,2,3].sum()точный код , выполняемый без использования Prototypeи Object.defineProperty, Object.defineProperties.
Поскольку это был вопрос собеседования, я предполагаю, что есть способы заставить его работать?
Любая помощь / указание приветствуется.
благодаря
Ответы
Предисловие: подобные вопросы на самом деле не показывают, что кто-то «хороший» программист, это просто означает, что он знаком с уловками на языке, которые не приводят к более удобному для сопровождения коду . Я бы опасался работать в компании или команде, которая регулярно использует подобные уловки.
(И в моем личном случае: я работал над движком Chakra JavaScript, когда был SE в Microsoft, и мне нравится думать, что я очень хорошо знаю JavaScript / ECMAScript, и мне все еще пришлось долго думать о том, как это можно было бы сделать без используя prototypeили defineProperty- вот почему я не думаю, что это хороший технический вопрос на собеседовании, если они ожидали прямого ответа - но если это был вопрос на собеседовании, который должен побудить вас задать вопросы интервьюеру, это другое).
Вариант 1: Глобальный обработчик ошибок:
Вот ужасный способ:
window.addEventListener( 'error', function( e ) {
if( e.error instanceof ErrorEvent || e.error instanceof TypeError ) {
const msg = e.error.message;
const suffixIdx = msg.indexOf( ".sum is not a function" );
if( suffixIdx > -1 ) {
const arrayStr = msg.substring( 0, suffixIdx );
const arr = eval( arrayStr ); // <-- lolno
const total = arr.reduce( ( sum, e ) => sum + e, 0 );
console.log( total ); // 6
}
}
} );
[1,2,3].sum()
@NenadVracar опубликовал упрощенную версию, которая избегает eval, хотя и использует локальный try:
try {
[1,2,3].sum()
} catch (err) {
const result = err.message
.match(/\[(.*?)\]/)[1]
.split(',')
.reduce((r, e) => r + +e, 0)
console.log(result)
}
Вариант 2: переопределить Arrayконструктор
Если вы используете более старый движок JavaScript (созданный до 2010 или ECMAScript 5), тогда сценарий, который переопределяет Arrayконструктор, будет использовать этот конструктор, когда сценарий встречает литерал массива, и .sumметод может быть добавлен таким образом:
Array = function() { // <-- THIS WILL NOT WORK IN BROWSERS MADE AFTER 2010!
this.sum = function() {
var total = 0;
for( var i = 0; i < this.length; i++ ) {
total += this[i];
}
return total;
};
};
let total = [1,2,3].sum();
console.log( total );
Вариант 3: Скрытность с prototypeсобственностью:
Как уже упоминалось в комментариях, вы все равно можете изменить prototypeчлен или использовать, Object.definePropertyесли вы обращаетесь к этим членам как к строкам:
Array[ 'proto' + 'type' ].sum = function() {
var total = 0;
for( var i = 0; i < this.length; i++ ) {
total += this[i];
}
return total;
};
let total = [1,2,3].sum();
console.log( total );
Насколько здесь можно обойти линии?
Предполагая, что мы хотим, чтобы следующая строка работала, [1, 2, 3].sum();мы можем очень легко заставить ее что- то делать . Обратите внимание, что из-за правил автоматической вставки точки с запятой не обязательно, что у вас есть массив. Это может быть доступ к массиву с оператором запятой в нем.
({3: {sum: () => console.log(6)}}) //<-- object
[1,2,3].sum(); //<-- array access
Или, чтобы было понятнее, вот эквивалентный код:
const obj = {
3: {
sum: () => console.log(6)
}
};
obj[3].sum(); //<-- array access
Поскольку я не вижу определения того, что sumследует делать, приведенное выше охватывает все перечисленные требования - никаких махинаций с прототипами, никаких дополнительных свойств.
Хорошо, технически sumничего не суммирует , но есть обходной путь: определите это так
sum: (a, b) => a + b
Технически это функция, которая суммирует два числа. В конце концов, нет необходимости суммировать последовательность, 1, 2, 3которая появляется перед вызовом sum.
Это один способ:
[1,2,3].__proto__.sum = () => { console.log('this runs!'); }
[1,2,3].sum();
Технически вы не используете Prototype, вы используете __proto__. То же самое с другим именем ... (но только для экземпляров)