首先,代码使用comma operator(另见What does a comma do in JavaScript expressions?)。逗号运算符的作用是允许您评估多个表达式。特别是在这种情况下,您有三个:
x=x=>{onerror=alert; throw 1337},toString=x,window+''
// ^______________________________^ ^________^ ^_______^
// 1 2 3
让我们一一检查:
关于隐式全局变量的简要说明
这将与以下大部分内容相关,因此我想先澄清一下。任何时候您分配给您尚未使用var、let 或const 声明的任何名称时,它都会隐式分配给全局对象上的属性。在浏览器中,全局对象是window,所以任何隐式全局对象都会去那里:
console.log("before assignment");
console.log("'foo' in window:", 'foo' in window);
console.log("window.foo:", window.foo);
foo = 42;
console.log("after assignment");
console.log("'foo' in window:", 'foo' in window);
console.log("window.foo:", window.foo);
箭头函数
x=x=>{onerror=alert; throw 1337}
定义
这将创建一个新的箭头函数并将其分配给x。由于没有变量声明,x 将是一个隐式全局,因此附加到window。这不是非常有用 - 它可能是为了保存一些字符。该函数还接受一个名为x 的参数,但它不做任何事情。同样,没有任何用处 - 保存单个字符,否则需要将其定义为 ()=>。
主体部分 1
更有趣的是该函数的作用。
onerror=alert
首先它将onerror 分配给alert。这两个都是global error handler 和global alert() function 作为引用传递。因此,任何时候发生 any 错误,它只会显示警报:
window.onerror = alert;
JSON.parse("{"); // parsing error
有关如何使用函数引用的更多信息,请参阅 What is the difference between a function call and function reference? 和 Difference of calling a function with and without parentheses in JavaScript。简而言之,如果将函数分配给处理程序,则调用该处理程序实际上会运行该函数。
身体部分 2
然后发生的事情是箭头函数抛出throw 1337 的错误。在 JavaScript 中,您可以分配要抛出的任何值,因此它是否是错误并不重要。因此,抛出一个数字是有效的。抛出的值并不真正相关,因为重要的是抛出错误。由于函数的第一部分,这个throw 语句将触发全局错误处理程序。这是一个演示(格式化):
x = x => {
onerror = alert;
throw 1337
}
x();
到目前为止,一切都很好,但这不是在代码中调用函数的方式。让我们进入下一部分。
覆盖toString
toString=x
这部分将覆盖window 上的toString 方法并将其更改为x 函数。因此,任何时候您显式或隐式将window 转换为字符串,它将改为执行x。
x = x => {
onerror = alert;
throw 1337
}
toString = x;
window.toString();
将window 转换为字符串
在 JavaScript 中,当您尝试将任何值与字符串连接时,该值也将被隐式转换为字符串。这通常通过调用toString() 方法来完成:
const value1 = { "foo": 42 };
const value2 = {
"foo": 42,
toString() {
return `foo is ${this.foo}`;
}
};
const value3 = {
"foo": 42,
toString() {
return "Hello world";
}
};
console.log(value1 + ""); //default `toString()` method of objects
console.log(value2 + "");
console.log(value3 + "");
同样的事情发生在代码的最后部分:
window+''
这将:
- 触发到字符串的转换,
- 在
window 上调用toString 方法,该方法
- 被
x使用toString=x覆盖,
- 改为调用
x 函数,该函数
- 将全局错误处理程序设置为
alert (onerror=alert) and immediately throws an error (throw 1337`),即
- 调用全局错误处理程序
为什么省略 window+'' 不会引发错误
希望从上面清楚,但要直接解决它 - 需要该代码来触发之前定义的整个反应链。
为了清楚起见,这是带有解释和格式的完整代码:
//create a function
window.x = x => {
onerror = alert; //changes the global error handler
throw 1337 //throws an error to trigger the error handler
};
//overwrite the `toString` method of `window` so it always throws an error
window.toString = window.x;
//implicitly call `window.toString()` by performing a string concatenation
window + '';