发布于 2026-01-06 5 阅读
0

JavaScript:等式疯狂,或者 x === 1 && x === 2 挑战赛总结参考资料 DEV 的全球展示挑战赛,由 Mux 呈现:展示你的项目!

JavaScript:等式混乱,或者说 x === 1 && x === 2

挑战

撰写

结论

参考

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

JavaScript 甚至能让最优秀的人都对当前发生的事情产生怀疑。

在这篇文章中,我将向您展示使以下语句返回 true 的不同方法:

x === 1 && x === 2
Enter fullscreen mode Exit fullscreen mode

首先,我给大家出一个挑战,欢迎各位先自己尝试解答。
如果您只想知道答案,请直接跳到解答部分!

挑战

此挑战共有三个难度级别,并提供多种解决方案!

你的目标是赋予 X 所需的Flag!打印值。

请将以下代码片段放置在某个位置以便打印。Flag!

一级

// Your code here

if(x == 1 && x == 2 && x == 3) {
    console.log('Flag!');
} else {
    console.log('Wrong flag!');
}

// Your code here
Enter fullscreen mode Exit fullscreen mode

二级

让我们使用严格相等运算符来增加一些难度吧!

// Your code here

if(x === 1 && x === 2 && x === 3) {
    console.log('Flag!');
} else {
    console.log('Wrong flag!');
}

// Your code here
Enter fullscreen mode Exit fullscreen mode

3级

最后,让我们在当前作用域内打印标志!

这意味着这条语句不应该放在类或函数中,而应该单独放在脚本中。

// Your code here

if(x === 1 && x === 2 && x === 3) {
    console.log('Flag!');
} else {
    console.log('Wrong flag!');
}
Enter fullscreen mode Exit fullscreen mode

撰写

你成功打印了吗Flag!

第一部分

以下是上一个挑战的第一部分:

if(x == 1 && x == 2 && x == 3) {
Enter fullscreen mode Exit fullscreen mode

解决这部分挑战的关键在于了解 JavaScript 如何比较两个对象。

使用相等运算符 ==而非严格相等运算符 ===意味着引擎会先尝试将两个对象都转换为基本类型,然后再进行比较。您可以在 MDN 的比较运算符

页面 上找到更多关于比较的信息

这意味着,如果我们要将对象与字符串进行比较,myObject.toString()则会使用 `s` 的结果进行比较,而不是比较失败。

例如:

const myObject = {
    toString() {
        return 'My Object!';
    }
}
console.log(myObject == 'My Object!');
Enter fullscreen mode Exit fullscreen mode

返回true

在我们的场景中,当我们将 x 与原始类型进行比较时Number,后台将执行以下步骤:

如果 Type(x) 为 Object 且 Type(y) 为 String 或 Number,

则返回比较结果 ToPrimitive(x) == y。

此行为已在 EcmaScript:抽象相等性比较算法中进行了记录。

将对象转换为原始类型可以通过调用对象的 `toPrimitive`toString或 ` valueOftoPrimitive` 方法来实现,具体文档请参见:`Object [[DefaultValue]]`。

在 ES6 中,我们还可以直接重写`Symbol.toPrimitive` 方法来返回自定义值。
因此,我们可以创建一个 `toPrimitive`toString或 `toPrimitive`valueOf函数返回递增数字的对象!

解决方案

let i = 1,
  x = {
    valueOf() { // Default conversion for numbers
      return i++;
    },
    toString() { // Default conversion for strings
      return i++;
    },
    [Symbol.toPrimitive]() { // toPrimitive override
        return i++;
    }
  }

if(x == 1 && x == 2 && x == 3) {
    console.log('Flag!');
} else {
    console.log('Wrong flag!');
}
Enter fullscreen mode Exit fullscreen mode

请注意,Symbol.toPrimitive 是第一个尝试调用的函数,然后valueOf是最后一个toString,如果顺序在您未来的挑战中很重要的话。

第二部分

这个挑战的第一部分可以用对象和非严格比较来解决,但这在这里行不通。

因为我们使用的是严格相等运算符,所以x需要先进行比较1,然后进行比较2,最后进行比较3

解决这个问题需要两个技巧:
Getter 方法和一个晦涩的with语句。

该解决方案的第一部分需要创建一个对象,myObject该对象具有一个x设置为 getter 函数的属性:


let i = 1,
  myObject = {
    get x() {
        return i++;
    }
 }
Enter fullscreen mode Exit fullscreen mode

现在我们可以访问它myObject.x,它将返回一个递增的值!

myObject但这仍然不足以解决问题,因为 if 语句的比较中缺少我们的前缀。

幸运的是(或者说不幸的是),JavaScript 中有一个晦涩的语句,允许我们将作用域设置为对象的属性:with

难道你不喜欢 MDN 上关于该运算符的页面以这么大的警告开头吗?

MDN 警告

MDN文档对 with 的描述如下:

`with` 语句会在其语句体执行期间,将给定的对象添加到作用域链的头部。如果语句体中使用的非限定名称与作用域链中的某个属性匹配,则该名称将绑定到该属性以及包含该属性的对象。

因此,该解决方案的第二部分是将比较操作包装在一个with语句中,这样就可以x像访问原生属性一样访问它。

解决方案

let i = 1,
  myObject = {
    get x() {
        return i++;
    }
 }

with(myObject) {

  if(x === 1 && x === 2 && x === 3) {
    console.log('Flag!');
  } else {
    console.log('Wrong flag!');
  }

}
Enter fullscreen mode Exit fullscreen mode

第三部分

之前的解决方案只有在你能控制 if 语句的上下文时才有效,而这在查找 XSS 漏洞时很少见。

因此,我们可以调整解决方案,要求在 if 语句之前设置一个单独的入口点来打印输出结果Flag!

注意:如果你的入口点只位于比较语句下方,你可能需要查看我之前的文章:小心不必要的提升

由于我们仍然使用严格的相等性检查,因此仍然需要使用 getter 来生成 X。

此解决方案的不同之处在于,它将访问器直接添加到当前作用域(即this对象)上。

在浏览器中,this它将指向window由 DOM 模型定义的对象。

在 NodeJS 中,this它将指向global对象本身。

要修改当前对象的属性,我们将使用Object.defineProperty。

解决方案

let a = 1;
Object.defineProperty(
  window, // Object to assign the new property to: this, window, or global depending on the context 
  'x', // Name of the property to assign
  {  // Properties of the object, the getter function in our case
    get: function() { 
      return a++; 
    } 
  }
);

if(x === 1 && x === 2 && x === 3) {
  console.log('Flag!');
} else {
  console.log('Wrong flag!');
}
Enter fullscreen mode Exit fullscreen mode

结论

由于 JavaScript 的动态特性,即使是理智的开发者也能理解它x === 1 && x === 2 && x !== x的工作原理!

希望实际代码中不会有人依赖这些特性,但我很想看到这些特性在现实世界中的应用案例。

另外,这让我想到 JavaScript 中某些比较可能只返回 false 的情况。

如您所知,`if`NaN运算符在比较中总是返回 false,包括与自身比较。

我所知的唯一可能返回 true 的情况是 `if` 运算符本身Symbol(),因为每个符号都是唯一的。

现在,我们可以为 `if` 运算符创建一个动态值,使其在x比较结果x !== x为 true 时仍然有效。

你还知道其他有趣的JS特性,觉得值得写篇文章介绍吗?

请留言或在Twitter上联系我!

参考

MDN:比较运算符

EcmaScript:抽象相等性比较算法

EcmaScript:对象 [[DefaultValue]]

MDN:Getters

MDN:使用

MDN:Object.defineProperty

文章来源:https://dev.to/antogarand/javascript-equality-insanity-or-where-x--1--x--------------------------------------------2-2hig