Pergunta da entrevista Javascript: make [1,2,3] .sum () run

Nov 17 2020

Javascript pergunta da entrevista: fazer [1,2,3].sum()correr código exato sem usar Prototypee Object.defineProperty, Object.defineProperties.

Já que foi uma pergunta de entrevista, estou assumindo que há maneiras de fazê-la funcionar?

Qualquer ajuda / direção apontada apreciada.

obrigado

Respostas

8 Dai Nov 17 2020 at 03:09

Prefácio: Perguntas como essas não mostram realmente que alguém é um "bom" programador, apenas significa que eles estão familiarizados com os truques da linguagem que não levam a um código mais sustentável . Eu desconfiaria de trabalhar para uma empresa ou equipe que regularmente usa truques como este.

(E no meu caso pessoal: trabalhei no motor Chakra JavaScript quando era SE na Microsoft e gosto de pensar que conheço JavaScript / ECMAScript muito bem e ainda tive que pensar muito sobre como isso poderia ser feito sem usando prototypeou defineProperty- então é por isso que eu não acho que esta é uma boa pergunta técnica para uma entrevista, se eles esperassem uma resposta direta - mas se essa foi uma pergunta de entrevista com o objetivo de levá-lo a fazer perguntas ao entrevistador, então isso é diferente).


Opção 1: manipulador de erros global:

Aqui está uma maneira horrível :

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 postou uma versão simplificada que evita eval, embora use um local try:

try {
    [1,2,3].sum()
} catch (err) {
    const result = err.message
    .match(/\[(.*?)\]/)[1]
    .split(',')
    .reduce((r, e) => r + +e, 0)
    
  console.log(result)
}

Opção 2: substituir Arrayconstrutor

Se você estiver usando um mecanismo JavaScript mais antigo (feito antes de 2010 ou ECMAScript 5) , um script que substitui o Arrayconstrutor terá esse construtor usado quando o script encontrar um literal de array e o .summétodo pode ser adicionado dessa forma:

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 );

Opção 3: ser sorrateiro com a prototypepropriedade:

Como outros mencionaram nos comentários, você ainda pode alterar o prototypemembro ou usar Object.definePropertyse acessar esses membros como strings:

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

Quanto podemos contornar as linhas aqui?

Assumindo que queremos que a linha a [1, 2, 3].sum();seguir funcione , podemos facilmente fazer com que ela faça alguma coisa . Observe que devido às regras de inserção automática de ponto-e-vírgula , não é necessário que haja um array. Pode ser o acesso à matriz com o operador vírgula .

({3: {sum: () => console.log(6)}}) //<-- object

[1,2,3].sum(); //<-- array access

Ou para deixar mais claro, aqui está o código equivalente:

const obj = {
  3: {
    sum: () => console.log(6)
  }
};

obj[3].sum(); //<-- array access

Uma vez que não vejo uma definição do que sumdeve ser feito, o acima cobre todos os requisitos listados - sem manobras protótipo, sem propriedades extras.

OK, tecnicamente, sumnão soma nada, mas aqui está uma solução alternativa: defina-o assim

sum: (a, b) => a + b

Agora, é tecnicamente uma função que soma dois números. Afinal, não há necessidade de somar a sequência 1, 2, 3que aparece antes de chamar o sum.

2 LuisLimas Nov 17 2020 at 03:06

Esta é uma maneira:

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

Tecnicamente, você não está usando Prototype, está usando __proto__. Que é o mesmo com um nome diferente ... (mas apenas para instâncias)