Pregunta de la entrevista de JavaScript: ejecutar [1,2,3] .sum ()

Nov 17 2020

Javascript pregunta de la entrevista: hacer que [1,2,3].sum()el código se ejecute exacta sin utilizar Prototypey Object.defineProperty, Object.defineProperties.

Dado que era una pregunta de entrevista, ¿estoy asumiendo que hay formas de hacer que funcione?

Se agradece cualquier ayuda / dirección que señale.

Gracias

Respuestas

8 Dai Nov 17 2020 at 03:09

Prefacio: Preguntas como estas no muestran realmente que alguien sea un "buen" programador, solo significa que está familiarizado con trucos en el lenguaje que no conducen a un código más fácil de mantener . Sería cauteloso al trabajar para una empresa o equipo que utiliza habitualmente trucos como este.

(Y en mi caso personal: trabajé en el motor de JavaScript Chakra cuando era un SE en Microsoft y me gusta pensar que conozco muy bien JavaScript / ECMAScript y todavía tuve que pensar mucho tiempo en cómo se podría hacer esto sin usando prototypeo defineProperty, por eso no creo que esta sea una buena pregunta de entrevista técnica si esperaban una respuesta directa , pero si se trataba de una pregunta de entrevista destinada a inducirlo a hacer preguntas al entrevistador, entonces eso es diferente).


Opción 1: controlador de errores global:

He aquí una forma horrible :

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 ha publicado una versión simplificada que evita eval, aunque usa un 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)
}

Opción 2: anular Arrayconstructor

Si está utilizando un motor JavaScript más antiguo (hecho antes de 2010 o ECMAScript 5), entonces un script que anula el Arrayconstructor tendrá ese constructor usado cuando el script encuentre un literal de matriz, y el .summétodo podría agregarse de esa manera:

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

Opción 3: Ser astuto con la prototypepropiedad:

Como otros han mencionado en los comentarios, aún puede mutar al prototypemiembro o usar Object.definePropertysi accede a esos miembros como cadenas:

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

¿Cuánto podemos bordear las líneas aquí?

Suponiendo que queremos que la siguiente línea funcione [1, 2, 3].sum();, podemos hacer que haga algo muy fácilmente . Tenga en cuenta que debido a las reglas de inserción de punto y coma automáticas , no es necesario que lo que tiene allí sea una matriz. Podría ser un acceso a una matriz con un operador de coma en él.

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

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

O para que quede más claro, aquí está el código equivalente:

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

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

Dado que no veo una definición de lo que sumdebería hacer, lo anterior cubre todos los requisitos enumerados: sin travesuras de prototipos, sin propiedades adicionales.

Está bien, técnicamente, sumno suma nada, pero aquí hay una solución: defínalo así

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

Ahora, técnicamente es una función que suma dos números. Después de todo, no es necesario sumar la secuencia 1, 2, 3que aparece antes de llamar al sum.

2 LuisLimas Nov 17 2020 at 03:06

Esta es una forma:

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

Técnicamente no estás usando Prototype, estás usando __proto__. Que es lo mismo con un nombre diferente ... (pero solo por instancias)