【问题标题】:Javascript interview question: make [1,2,3].sum() runJavascript 面试题:make [1,2,3].sum() run
【发布时间】:2020-11-16 19:54:25
【问题描述】:

Javascript 面试题:在不使用PrototypeObject.definePropertyObject.defineProperties 的情况下让[1,2,3].sum() 运行精确的代码。

既然这是一个面试问题,我假设有办法让它发挥作用?

感谢任何帮助/指点方向。

谢谢

【问题讨论】:

  • @Dai 不会真正起作用 - 数组文字不会只是在作用域链中查找 Array(这就是 new Array() 会做的) - 它实际上会初始化一个原生数组,不管window.Array.
  • 您是否签署了保密协议并同意不分享面试问题?
  • @VLAZ 以前(如 2010 年之前)浏览器在遇到文字时会使用用户提供的 Array 构造函数。 JavaScript 数组字面量不是由解析器/编译器初始化,而是在遇到时初始化。
  • 实际的限制是什么?你不能用原型做任何事情,或者你不能使用prototype作为属性,或者你不能使用“原型”这个词?还是什么?
  • 我的意思是.. 他们正在寻找的正确答案可能是“没有(好的)方法可以做到这一点”也许是看你是否愿意给出一些 hacky 而不是你的脚向下

标签: javascript


【解决方案1】:

前言:像这样的问题并不能真正表明某人是一个“好”的程序员,这只是意味着他们熟悉语言中的技巧,不会导致更多 -可维护的代码。我会警惕为经常使用此类技巧的公司或团队工作。

(就我个人而言:我在 Microsoft 担任 SE 时从事 Chakra JavaScript 引擎的工作,我喜欢认为我非常了解 JavaScript/ECMAScript,但我仍然需要思考关于如何在不使用 prototypedefineProperty 的情况下做到这一点已经很长时间了 - 这就是为什么我不认为这是一个很好的技术面试问题如果他们期望一个直接的答案 - 但是如果这是一个旨在提示你向面试官提问的面试问题,那就不同了)。


选项 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 构造函数

If you're using an older JavaScript engine (made prior to 2010 or 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 属性:

正如其他人在 cmets 中提到的,如果您以字符串的形式访问这些成员,您仍然可以改变 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 );

【讨论】:

  • 可怕。我喜欢它!
  • 哦,天哪!
  • @RinkeshGolwala 永远不要在任何真正的代码库中使用它。不管你多么讨厌你的同事。
【解决方案2】:

我们可以在这里绕开多少?

假设我们希望下面的代码行 [1, 2, 3].sum(); 可以正常工作,那么我们可以很容易地让它做一些事情。请注意,由于automatic semicolon insertion rules,它不是必要你有一个数组。它可能是数组access,其中包含comma operator

({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

现在,从技术上讲,它是一个将两个数字相加的函数。毕竟,不需要对出现在调用sum 之前的序列1, 2, 3 求和。

【讨论】:

  • 哦,是的,好的旧 ASI ;) 如果允许在代码前放置行,那么这是一个可行的 hack!
  • @FZs 我假设之前允许添加行。否则我什至不确定你是否能够实施任何实际的黑客攻击。这是他们的错,他们没有这样做;[1, 2, 3]:P
  • 那是真的......(只要他们不要求它应该是一个单独的声明)。无论如何都很棒的答案!
  • @FZs 嘿,我只能满足这里给出的要求。不过,我们可能无法真正回答这个问题 - 也许它是为了促使人们讨论什么的目标到底是什么。也许预期的结果是说“不,这不可能”。谁知道。我个人不太喜欢这个作为面试问题,但是嘿。我认为面试中的任务应该是实用的,并且与你通常遇到的类似。 FizzBu​​zz 有充分的理由受到抨击,但它仍然比像这样的开放式问题要好。
  • 我喜欢你的回答,不想批评,只是这些都是我看了之后想到的……
【解决方案3】:

使用披萨,不需要Prototype ??

var knife = (s) => { return s.replace(/,/g, '?') }, result, slice = 'codePointAt', bite = 'toString', mix = 'substring';
var pizza = '?', peppers = knife('?,?,?'), avocados = knife('?,?,?,?,?,?,?,?,?'), cheese = String.fromCharCode;
[+pizza[slice](0)[bite](16)[mix](2,3)][avocados.split('?').map((v) => +(v[slice](0)[bite](16).substring(2,5))).map((v) => +(v > 600 ? (v + '')[mix](0,2) : v - 440 )).reduce((a,b,i) => (i == 1 ? cheese(a) + cheese(b) : a + cheese(b)))][peppers.split('?').map((v) => +(v[slice](0)[bite](16)[mix](2,5))).map((v, i) => (i == 0 ? v - 221 : i == 1 ? v - 219 : v - 227)).reduce((a,b,i) => (i == 1 ? cheese(a) + cheese(b) : a + cheese(b)))] = () => { return pizza[slice](0)[bite](8)[mix](2,4).split('').reduce((a,b) => +a+ +b);};
result = [1,2,3].sum();
// Show the result 
console.log(result);

将此作为披萨/表情符号示例展示给您的目的是您可以解决此类测试 (similar to this)。

当然,前面的代码可以简化为另一个更严重的sn-p,这也达到了使用__proto__提出问题的目的。这个答案类似于@VLAZ's 答案,但没有他使用的; hack-thing。

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

/**
 * Here am using `__proto__` instead 
 * of `Prototype`, they are both almost the same 
 * but `__proto__` works for instances only.
 *
 **/

【讨论】:

猜你喜欢
  • 2020-09-29
  • 2011-01-13
  • 1970-01-01
  • 1970-01-01
  • 2021-12-03
  • 1970-01-01
  • 2022-11-09
  • 2021-11-12
  • 2021-12-05
相关资源
最近更新 更多