如何让 a == 1 && a == 2 && a == 3 成立

Posted 前端筱悦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何让 a == 1 && a == 2 && a == 3 成立相关的知识,希望对你有一定的参考价值。

javascript中,让"a == 1 && a == 2 && a == 3"成立,这听起来像是一个不可能的任务。毕竟,a要么等于1、2、3中的一个,要么不等于任何一个。但是,有一个奇怪的技巧可以让这个条件成立。在这篇文章中,我们将探讨这个技巧,并深入研究背后的原理。

首先,让我们看一下条件"a == 1 && a == 2 && a == 3"的含义。它意味着什么?它意味着a必须等于1、2和3。但是,这是不可能的,因为a只能有一个值。所以,我们需要一种方法来欺骗JavaScript,让它认为a实际上等于1、2和3。下面是一个实现这个目标的方法:

var a = 
  i: 1,
  toString: function() 
    return a.i++;
  
;

if (a == 1 && a == 2 && a == 3) 
  console.log("条件成立!");

这段代码看起来很奇怪。我们定义了一个名为a的对象,该对象具有一个属性i和一个toString方法。i的初始值为1,toString方法返回i的值,并将i的值加1。所以,当我们在条件中使用a时,JavaScript会调用a.toString()方法,这会返回i的值,并将i的值加1。这意味着我们可以通过修改i的值来欺骗JavaScript,使它认为a等于1、2和3。

让我们逐步解释这个过程。首先,当我们在条件中使用a时,JavaScript会尝试将a转换为布尔值。因为a是一个对象,JavaScript会调用a.valueOf()方法,该方法返回对象本身,因为对象是真值。然后,JavaScript会调用a.toString()方法,该方法返回i的值,即1。所以,我们现在有一个等式:

if (1 == 1 && a == 2 && a == 3) 
  console.log("条件成立!");

下一步,JavaScript会再次调用a.toString()方法,该方法返回i的值,即2。现在,我们有一个等式:

if (1 == 1 && 2 == 2 && a == 3) 
  console.log("条件成立!");

最后,JavaScript再次调用a.toString()方法,该方法返回i的值,即3。现在,我们有一个等式:

if (1 == 1 && 2 == 2 && 3 == 3) 
  console.log("条件成立!");

这个等式是真的,所以条件成立了!我们成功地欺骗了JavaScript,让它认为a等于1、2和3。

但是,这个技巧背后的原理是什么?为什么我们可以通过修改toString方法来改变对象的值?这涉及到JavaScript中的类型转换和对象属性访问的机制。

首先,让我们看一下JavaScript中的类型转换。在JavaScript中,有两种类型转换:显式转换和隐式转换。显式转换是通过调用类型转换函数来执行的,如Number()、String()和Boolean()。隐式转换是在需要不同类型的值时自动发生的,例如将字符串与数字相加时,JavaScript会将字符串转换为数字。

隐式转换在我们的例子中起了关键作用。当我们将a与数字1、2和3进行比较时,JavaScript将调用a.valueOf()和a.toString()方法,这是隐式转换发生的结果。

其次,让我们看一下对象属性访问的机制。在JavaScript中,有两种访问对象属性的方法:点表示法和方括号表示法。点表示法是通过对象名和属性名之间的点号来访问属性的,例如obj.property。方括号表示法是通过对象名和属性名之间的方括号来访问属性的,例如obj['property']。这两种方法在访问属性时的行为略有不同。

点表示法要求属性名是一个有效的标识符,即它不能包含空格或运算符。如果属性名不是一个有效的标识符,则必须使用方括号表示法。方括号表示法可以使用任何字符串作为属性名,包括包含空格和运算符的字符串。

另一个重要的区别是,方括号表示法可以使用变量作为属性名。例如,如果我们有一个变量name,我们可以使用obj[name]来访问对象的属性,这是点表示法所不能做的。

在我们的例子中,我们使用了一个对象来模拟a的值。这个对象具有一个名为i的属性,它的值是1。我们还为对象定义了一个toString方法,该方法返回i的值,并将i的值加1。因为我们使用了方括号表示法来访问对象的属性,我们可以使用变量i作为属性名,这使得我们可以通过修改i的值来改变对象的属性值。

综上所述,我们成功地利用了JavaScript中的类型转换和对象属性访问机制来欺骗JavaScript,让它认为a等于1、2和3。这个技巧虽然看起来很奇怪,但它展示了JavaScript中一些有趣而强大的机制。在编写JavaScript代码时,理解这些机制可以帮助我们更好地掌握这门语言。

那么,如何将这些机制应用到我们的问题中呢?下面是一个实现这个问题的代码:

const a = 
  i: 1,
  toString: function () 
    return this.i++;
  
;

if (a == 1 && a == 2 && a == 3) 
  console.log('a == 1 && a == 2 && a == 3');

在这个代码中,我们定义了一个对象a,它有一个名为i的属性,初始值为1。我们还为这个对象定义了一个toString方法,这个方法会将i的值加1,并返回i的旧值。这就是我们在前面讨论的利用对象属性访问机制的方法。

然后,我们在if语句中比较a是否等于1、2和3。在这个比较中,JavaScript会将a转换为字符串类型。由于a是一个对象,JavaScript会调用a的toString方法来执行转换。在每次调用toString方法时,i的值会加1,因此第一次调用返回1,第二次调用返回2,第三次调用返回3。

当a与数字1、2和3进行比较时,它们都是字符串类型,因此JavaScript将它们转换为数字类型。由于字符串"1"、"2"和"3"都可以转换为数字1、2和3,所以比较结果为真,if语句的代码块被执行。

这就是我们如何使用类型转换和对象属性访问机制欺骗JavaScript,让a等于1、2和3。当然,这并不是一种好的编程实践。这个技巧容易让代码变得难以理解和维护,并且在不同的JavaScript引擎中可能会产生不同的结果。在编写实际的应用程序时,我们应该避免使用这种技巧,而是使用更清晰和易于理解的代码。

如何让 a == 1 && a == 2 && a == 3同时成立?

简述

看到这个问题是不是觉得很神奇,其实只要利用一点Java的语法特性就能解决问题。

1、解决思路:

Java专家组发现,Integer对象初始化的大部分数据介于-128~127之间,因此Integer对小数据(-128~127)预置了缓存,以此来提升大部分情况下实例化一个Integer的性能。其解决办法是,在jvm初始化的时候,数据-128~127之间的数字便被缓存到了本地内存中,如果初始化-128~127之间的数字,会直接从内存中取出,不需要新建一个对象,而这个数据被缓存在Integer的内部类IntegerCache中。所以我们通过获取Integer内部类IntegerCache,也就是缓存-128~127的类,并将索引为129(也就是1)的对象赋值给130(2)、131(3),此时当我们从Integer中去取值的时候就会发现1 == 2 == 3。


2、Integer内部类IntegerCache源码

源码示例:

/**
 *  IntegerCache缓存了-128~127
 */
private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }

    private IntegerCache() {}
}

3、具体实现

代码示例:

package com.lizba.p3;

import java.lang.reflect.Field;

/**
 * <p>
 *		a == 1 && a == 2 && a == 3示例代码
 * </p >
 *
 * @Author: Liziba
 * @Date: 2021/6/3 17:19
 */
public class IntegerTest {


    public static void main(String[] args){

        try {
            Class<?>[] declaredClasses = Integer.class.getDeclaredClasses();
            Class<?> integerCache = declaredClasses[0];
            Field f = integerCache.getDeclaredField("cache");
            f.setAccessible(true);
            Integer[] cache = (Integer[]) f.get(integerCache);

            System.out.println(cache[129]);
            System.out.println(cache[130]);
            System.out.println(cache[131]);

            cache[130] = cache[129];
            cache[131] = cache[129];
            Integer a = 1;
            
            // true
            if (a == (Integer) 1 && a == (Integer) 2 && a == (Integer) 3) {
                System.out.println(true);
            }
            
            // true
            if (a == Integer.valueOf(1) && a == Integer.valueOf(2)  && a == Integer.valueOf(3)) {
                System.out.println(true);
            }
            
            // 无输出
            if (a == new Integer(1) && a == new Integer(2) && a == new Integer(3)) {
                System.out.println(true);
            }
            System.out.println(cache[129]);
            System.out.println(cache[130]);
            System.out.println(cache[131]);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
}

查看输出结果

在这里插入图片描述

4、注意点

如下三个为什么前两个为true,最后一个为false呢?

// true
if (a == (Integer) 1 && a == (Integer) 2 && a == (Integer) 3) {
    System.out.println(true);
}

// true
if (a == Integer.valueOf(1) && a == Integer.valueOf(2)  && a == Integer.valueOf(3)) {
    System.out.println(true);
}

// 无输出
if (a == new Integer(1) && a == new Integer(2) && a == new Integer(3)) {
    System.out.println(true);
}

这个答案我们通过源码来揭晓:
当我们通过new关键字来构造一个Integer时,此时是重新在JVM中实例化的一个对象,因此对象地址是不相等的。

 /**
     * The value of the {@code Integer}.
     *
     * @serial
     */
    private final int value;

    /**
     * Constructs a newly allocated {@code Integer} object that
     * represents the specified {@code int} value.
     *
     * @param   value   the value to be represented by the
     *                  {@code Integer} object.
     */
    public Integer(int value) {
        this.value = value;
    }

而当我们使用Integer.valueOf(int),-128~127的值会从IntegerCache中获取,此时返回的是同一个对象,地址是相等的。

// -128~127从IntegerCache中获取
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

5、总结

这些知识点都是用来考验一个开发者对JDK特性的熟练程度和平时的积累,有时候要是问上了也是有用的,或者也给我们在实际开发中做数据预热来提升服务的性能,带来了一定的思考价值。


有经典,有干货,微信搜索【李子捌】关注这个傻瓜式坚持的程序员。

在这里插入图片描述

以上是关于如何让 a == 1 && a == 2 && a == 3 成立的主要内容,如果未能解决你的问题,请参考以下文章

如何让a==1&&a==2&a==3成立

如何让 a == 1 && a == 2 && a == 3同时成立?

前端面试 JavaScript— 如何让if(a == 1 && a == 2)条件成立?

干细胞&iPS-Nat Commun:揭秘基于间充质干细胞的细胞疗法如何让多种疾病患者获益?

C++ AMP 如何在 GPU 内存中返回值?

如何通过 <a href...>...</a> 链接进行提交?