Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript interview question: make [1,2,3].sum() run

Tags:

javascript

Javascript interview question: make [1,2,3].sum() exact code run without using Prototype and Object.defineProperty, Object.defineProperties.

Since it was an interview question I am assuming there are ways to make it work?

Any help/pointing direction appreciated.

Thanks

like image 226
serkan Avatar asked Dec 07 '22 09:12

serkan


2 Answers

Preface: Questions like these don't really show someone is a "good" programmer, it just means they're familiar with tricks in the language that do not lead to more-maintainable code. I'd be wary of working for a company or team that regularly uses tricks like this.

(And in my personal case: I worked on the Chakra JavaScript engine when I was an SE at Microsoft and I like to think that I know JavaScript/ECMAScript very well and I still had to think a long time about how this could be done without using prototype or defineProperty - so that's why I don't think this is a good technical interview question if they expected a straight-answer - but if this was an interview question that's meant to prompt you into asking questions of the interviewer then that's different).


Option 1: Global error handler:

Here's a horrible way:

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 has posted a simplified version that avoids eval, though it uses a 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)
}

Option 2: Override Array constructor

If you're using an older JavaScript engine (made prior to 2010 or ECMAScript 5) then a script that overrides the Array constructor will have that constructor used when the script encounters an array literal, and the .sum method could be added that way:

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

Option 3: Being sneaky with the prototype property:

As others have mentioned in the comments, you could still mutate the prototype member or use Object.defineProperty if you access those members as 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 );
like image 171
Dai Avatar answered Dec 18 '22 06:12

Dai


How much can we skirt the lines here?

Assuming that we want the following line to work [1, 2, 3].sum(); then we can very easily just make it do something. Note that due to the automatic semicolon insertion rules, it's not necessary what you have there to be an array. It might be array access with comma operator in it.

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

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

Or to make it more clear, here is the equivalent code:

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

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

Since, I don't see a definition of what sum should do, the above covers all the requirements listed - no protoype shenanigans, no extra properties.

OK, technically, sum doesn't sum anything, but here is a workaround: define it like this

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

Now, it's technically a function that sums two numbers. There is no requirement to sum the sequence 1, 2, 3 that appears before calling the sum, after all.

like image 37
VLAZ Avatar answered Dec 18 '22 07:12

VLAZ