Pergunta da entrevista Javascript: make [1,2,3] .sum () run
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
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 );
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.
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)