Вопрос на интервью по Javascript: make [1,2,3] .sum () run

Nov 17 2020

Javascript интервью вопрос: сделать [1,2,3].sum()точный код , выполняемый без использования Prototypeи Object.defineProperty, Object.defineProperties.

Поскольку это был вопрос собеседования, я предполагаю, что есть способы заставить его работать?

Любая помощь / указание приветствуется.

благодаря

Ответы

8 Dai Nov 17 2020 at 03:09

Предисловие: подобные вопросы на самом деле не показывают, что кто-то «хороший» программист, это просто означает, что он знаком с уловками на языке, которые не приводят к более удобному для сопровождения коду . Я бы опасался работать в компании или команде, которая регулярно использует подобные уловки.

(И в моем личном случае: я работал над движком 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 );

6 VLAZ Nov 17 2020 at 03:26

Насколько здесь можно обойти линии?

Предполагая, что мы хотим, чтобы следующая строка работала, [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.

2 LuisLimas Nov 17 2020 at 03:06

Это один способ:

 [1,2,3].__proto__.sum = () => { console.log('this runs!'); }
 
 [1,2,3].sum();

Технически вы не используете Prototype, вы используете __proto__. То же самое с другим именем ... (но только для экземпляров)