Skip to content

如何让 (a == 1 && a == 2 && a == 3) 的值为true? #9

@YvetteLau

Description

@YvetteLau
Owner
No description provided.

Activity

toyxz

toyxz commented on May 23, 2019

@toyxz
var i = 1
Number.prototype.valueOf = function() {
    return i++
}
var a = new Number(1)
if (a == 1 && a == 2 && a == 3) {
    console.log('here')
}
shenanheng

shenanheng commented on May 23, 2019

@shenanheng

{[Symbol.toPrimitive]: ((i) => () => ++i) (0)}

dashengzi66

dashengzi66 commented on May 23, 2019

@dashengzi66

var aᅠ = 1;
var a = 2;
var ᅠa = 3;
console.log(aᅠ==1 && a== 2 &&ᅠa==3)

jizx

jizx commented on May 23, 2019

@jizx

var a = { value : 0 };
a.valueOf = function() {
return this.value += 1;
};
console.log(a==1 && a==2 && a==3);

tpxiang

tpxiang commented on May 23, 2019

@tpxiang

const a = {
i: 1,
toString: function () {
return a.i++;
}
}
if(a == 1 && a == 2 && a == 3) {
console.log('Hello World!');
}

chang229

chang229 commented on May 23, 2019

@chang229

var value = 0;
Object.defineProperty(window,'a',{
get:function(){
return this.value += 1;
}
})
console.log(a== 1 && a==2 && a==3);//true

bytebit8

bytebit8 commented on May 23, 2019

@bytebit8
!(a == 1 && a == 2 && a == 3) 
Web3TomCat

Web3TomCat commented on May 23, 2019

@Web3TomCat

let a = {
num:0,
valueOf:function(){
return this.num += 1
}
}
let aa = (a == 1 && a == 2 && a == 3)
console.log(aa)

taoyaoyaoa

taoyaoyaoa commented on May 23, 2019

@taoyaoyaoa

1.const a = {
i: 1,
toString: function () {
return a.i++;
}
}
if(a == 1 && a == 2 && a == 3) {
console.log('Hello World!');
}
2.var aᅠ = 1;
var a = 2;
var ᅠa = 3;
if(aᅠ==1 && a== 2 &&ᅠa==3) {
console.log("Why hello there!")
}
3.var val = 0;
Object.defineProperty(window, 'a', {
get: function() {
return ++val;
}
});
if (a == 1 && a == 2 && a == 3) {
console.log('yay');
}
参考了http://web.jobbole.com/93874/

AILINGANGEL

AILINGANGEL commented on May 23, 2019

@AILINGANGEL

== 最大的特点在于允许进行类型转换,对于对象的转换会进行toPrimitive操作,也就是先调用valueOf方法如果不能返回基本类型的值就调用toString方法

var a = {
    valueOf: (function() {
        var temp = 1; // 使用闭包来保存这个变量
        return function() {
            return temp++;
        }
    }())
}
tianyuandsb

tianyuandsb commented on May 23, 2019

@tianyuandsb

var a = {
i: 0,
valueOf: function() {
return this.i += 1;
}
}
if(a==1 && a==2 && a==3){
console.log('nice, 马飞~~~')
}

yelin1994

yelin1994 commented on May 23, 2019

@yelin1994

var a = [1, 2, 3]
a.join = a.shift
console.log(a==1 && a==2 && a==3)
利用数组转化为字符串都会调用,Array.join

或者
var a = {
value: 1,
toString: function () {
return this.value ++ }

wanglinyj

wanglinyj commented on May 23, 2019

@wanglinyj

let a = {
value: 0,
valueOf: function() {
return this.value += 1;
}
}
console.log(aᅠ==1 && a== 2 &&ᅠa==3)
JS对象有toString() 和valueOf()两个方法,toString()将该对象的原始值以字符串的形式返回,valueOf()返回最适合该对象的原始值,这两个方法一般是交由JS去隐式调用,以满足不同的运算情况。
在数值运算里,会优先调用valueOf(),在字符串运算里,会优先调用toString()。
1.用运算符对对象进行转换的时候valueOf()的优先级高于toString()
2.对对象进行强字符串转换时会优先调用toString()
3.toString()方法不能对null和undefined进行字符串转换,可以用String()方法代替

darlingyz

darlingyz commented on May 23, 2019

@darlingyz

不太会,所以百度了下,
有几种方法实现
1、重写Object的toString或者valueOf
2、定义"a"属性,并重写它的getter方法
3、字符编码
4、数组toString隐含调用join()方法
参考了 https://majing.io/posts/10000006051204
https://www.codercto.com/a/41494.html

--感谢作者,感谢小姐姐

lqzo

lqzo commented on May 23, 2019

@lqzo

== 运算符会进行隐式转换。

  • Object
    ==操作符会尝试通过方法valueOf和toString将对象转换为其原始值(一个字符串或数字类型的值)。
  const a = {
    i: 1,
    // valueOf 也可达到相同效果
    toString: function () {
      return a.i++;
    }
  }
  a == 1 && a == 2 && a == 3; // true
  • Array
    对于数组对象,toString 方法返回一个字符串,该字符串由数组中的每个元素的 toString() 返回值经调用 join() 方法连接(由逗号隔开)组成。
  var a = [1,2,3];
  a.join = a.shift;
  a == 1 && a == 2 && a == 3; // true
  • Symbol
    Symbol对象被转为原始类型的值时,会调用 toPrimitive 方法,返回该对象对应的原始类型值。
  let a = {
    [Symbol.toPrimitive]: ((i) => () => ++i) (0)
  };
  a == 1 && a == 2 && a == 3; // true

  • 修改window的get方法
  var val = 0;
  Object.defineProperty(window, 'a', {
    get: function() {
      return ++val;
    }
  });
  a == 1 && a == 2 && a == 3; // ture
  • 利用with关键字
    emmm... with关键字好像很少用到
  var i = 0;
  with({
    get a() {
      return ++i;
    }
  }) {
    a == 1 && a == 2 && a == 3; // true
  }

参考:

  1. 比较操作符
  2. Object​.prototype​.valueOf
  3. 从 (a==1&&a==2&&a==3) 成立中看javascript的隐式类型转换
  4. 第 38 题:下面代码中 a 在什么情况下会打印 1?

33 remaining items

MissWXiang

MissWXiang commented on May 24, 2019

@MissWXiang

!(a == 1 && a == 2 && a == 3) 取反
(微信名:RUN)

Timoeser

Timoeser commented on May 24, 2019

@Timoeser

let a = {
value: 0,
valueOf: function() {
return this.value += 1;
}
}

YvetteLau

YvetteLau commented on May 24, 2019

@YvetteLau
OwnerAuthor

首先,在JS中,宽松匹配 ==会先将左右两边的值转化成相同的原始类型,然后再去比较他们是否相等。
即:相等和不相等——先转换再比较 (==) 全等和不全等——仅比较而不转换 (===)
基本转换规则:
①、如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false转换为0,而true转换为1; ②、如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值 ③、如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较
注意:!(取反)的时候可将变量转换成boolean类型,null、undefined、NaN以及空字符串('')取反都为true,其余都为false
回到本题:
a == 1 && a == 2 && a == 3 值为true 即 a 是个复杂数据类型:object。
// 如果是个对象

var a = { value: 0 }
 a.valueOf = function () {
       return this.value += 1
   }
   if (a == 1 && a == 2 && a == 3) {
       console.log("艳姐,你好!")
   } 
// 如果是个数组 
  var b = [1, 2, 3];
    b.join = b.shift;
    if (b == 1 && b == 2 && b == 3) {
        console.log("艳姐,我知道你不用百雀羚!")
    }
// 通过proxy 进行拦截操作
var c = new Proxy({ i: 0 }, {
      get(target) {
          return () => target.i+=1;
      }
  })
  if (c == 1 && c == 2 && c == 3) {
      console.log("艳姐,今天又让我长知识了")
  } 
// 方法四 也适合严格相等   ===
// 重新定义 window 对象中的d 属性
var value = 0; //window.value
  Object.defineProperty(window, "d", {
      get: function () {
          return this.value += 1
      }
  })
  if (d == 1 && d == 2 && d == 3) {
      console.log("艳姐,每天进步一点点,做不一样的自己!")
  } 

O(∩_∩)O哈哈一起每天进步一点点

YvetteLau

YvetteLau commented on May 24, 2019

@YvetteLau
OwnerAuthor

!(a == 1 && a == 2 && a == 3) 取反
(微信名:RUN)

偷懒啦~~~被我发现了~

DazhiFe

DazhiFe commented on May 24, 2019

@DazhiFe

我觉得这道题主要考验的是==运算符的工作机制和类型转换的一些知识。==的工作机制自己是明白,也知道对象在转换为原始类型的时候会调用valueOf()toString(),但是却一直没有想到要重写这两个方法,所以这道题自己并没有解答出来,以下是参考其他小伙伴的回答加上自己的理解整理出来的,感谢其他小伙伴的解答。

首先要解答这道题,要先搞懂==的工作机制。

==运算符的工作机制

对于==来说,如果对比双方类型不一样的话,就会先进行类型转换。

假设我们需要对比xy是否相同,就会进行如下判断流程:

1.首先会判断两者类型是否相同,相同的话就比较大小了

2.类型不相同的话,那么就进行类型转换

3.判断两者类型是否为stringnumber,是的话就将字符串转换为number

1 == '1';
比较过程如下:
1 == 1; // true

4.判断其中一方是否为boolean,是的话就会把boolean转换为number再进行判断

'1' == true;
比较过程如下:
'1' == 1;
1 == 1; // true

5.判断其中一方是否为object,另一方为stringnumber、或symbol,是的话就会把object转换为原始类型再进行判断

'1' == { name: 'dazhi' }
比较过程如下:
'1' == '[object object]'

6.会先判断是否在对比nullundefined,是的话就返回true

7.要比较相等性之前,不能将nullundefined转换成其他任何值

8.如果有其中一方是NaN,则相等操作符返回false,而不相等操作符返回true

重要提示:即使两个操作数都是NaN,相等操作符也返回false了;因为按照规则,NaN不等于NaN

弄懂了==的工作机制,我们再回到题目:

(a == 1 && a == 2 && a == 3) == true

根据题目,我们可以推断a不可能是一个基本数据类型,因为a如果是nullundefined或者boolean,这个等式根本不会成立。所以a肯定是一个复杂数据类型:object,有可能是一个对象{}或者是数组[]

当一个对象object和数值做==比较的时候,会先把object转换为原始类型再进行比较。

所以,我们还需明白object到原始类型转换的一个过程:

  • 如果部署了[Symbol.toPrimitive]接口,那么调用此接口,若返回的不是基本数据类型,抛出错误。
  • 如果没有部署[Symbol.toPrimitive]接口,那么调用valueOf接口,若返回的不是基本数据类型,那么调用toString接口,若返回的还不是基本数据类型,那么抛出异常。
let obj = {
  [Symbol.toPrimitive]() {
    return 100;
  },

  valueOf() {
    return 200;
  }
}

let obj2 = {
  valueOf() {
    return 200;
  },

  toString() {
    return 300;
  }
}

console.log(obj == 100); // true
console.log(obj2 == 200); // true

上面代码说明,它们之间的一个的优先调用顺序是:[Symbol.toPrimitive] > valueOf > toString

a是一个{}

var a = {
  [Symbol.toPrimitive]: (function () {
    let i = 1;
    //闭包的特性之一:i 不会被回收
    return function () {
      return i++;
    }
  })()
}

console.log(a == 1 && a == 2 && a == 3); // true

如果没有部署[Symbol.toPrimitive]接口,则会调用valueOf接口,所以下面的代码也是可以的:

var a = {
  valueOf: (function () {
    let i = 1;
    //闭包的特性之一:i 不会被回收
    return function () {
      return i++;
    }
  })()
}

console.log(a == 1 && a == 2 && a == 3); // true

a是一个[]数组时

var a = [1,2,3];
a.join = a.shift;
console.log(a == 1 && a == 2 && a == 3); // true

不得不说,此方法真的是很巧妙。
数组也是一个对象,所以也遵循对象到原始类型的转化过程,然后又利用数组的Array.prototype.toString()内部调用的是Array.prototype.join(),所以把join重写为shift,这样当比较a == 1时,相当于执行了

a.shift();

由于shift()方法会改变原数组,并删除数组的第一个元素,然后把该元素返回回去。所以第一次比较删除的就是1,并把1返回回去,a == 1就为true了,然后原数组a就变成了[2,3],以此类推。

根据这个原理,下面的代码也是等价的:

var a = [3,2,1];
a.join = a.pop;
console.log(a == 1 && a == 2 && a == 3); // true

利用数据劫持

数据劫持还是第一次听到,过后需要好好补下这块相关的知识。

重写a属性的getter方法

使用Object.defineProperty定义的属性,在获取属性时,会调用get方法。利用这个特性,我们在window对象上定义a属性,如下:

let i = 1;
Object.defineProperty(window, 'a', {
  get: function() {
    return i++;
  }
})
console.log(a == 1 && a == 2 && a == 3); // true

Proxy形式实现

这个也是盲点,过后也需要好好学习下。

利用ES6新增的Proxy来实现:

const a = new Proxy({}, {
  v: 1,
  get: function() {
    return () => this.v++;
  }
});
console.log(a == 1 && a == 2 && a == 3); // true

最后

感谢各位小伙伴,学到很多知识点。希望大家能一直坚持下去,向前端专家进阶,加油!

KRISACHAN

KRISACHAN commented on May 24, 2019

@KRISACHAN

此题目为网上经典面试题的严谨形态(意思就是判断的条件,a就是a,没有各种奇奇怪怪的符号)

此题目的答案可以分为三大类:

1. 类型转换时的劫持

首先我们要知道,在 JS 中类型转换只有三种情况,分别是:

  • 转换为布尔值
  • 转换为数字
  • 转换为字符串

转换为原始类型

对象在转换类型的时候,会执行原生方法ToPrimitive

其算法如下:

  1. 如果已经是 原始类型,则返回当前值;
  2. 如果需要转 字符串 则先调用toSting方法,如果此时是 原始类型 则直接返回,否则再调用valueOf方法并返回结果;
  3. 如果不是 字符串,则先调用valueOf方法,如果此时是 原始类型 则直接返回,否则再调用toString方法并返回结果;
  4. 如果都没有 原始类型 返回,则抛出 TypeError类型错误。

当然,我们可以通过重写Symbol.toPrimitive来制定转换规则,此方法在转原始类型时调用优先级最高。

所以以此定义我们可以有以下四种答案:

var a = {
    arr: [3, 2, 1],
    valueOf () {
        console.group('valueOf')
        console.log(this.arr)
        console.groupEnd('valueOf')
        return this.arr.pop()
    }
}
if (a == 1 && a == 2 && a == 3) {
    console.log('biu')
}

var b = {
    arr: [3, 2, 1],
    toString () {
        console.group('toString')
        console.log(this.arr)
        console.groupEnd('toString')
        return this.arr.pop()
    }
}
if (b == 1 && b == 2 && b == 3) {
    console.log('biu')
}

var c = {
    arr: [3, 2, 1],
    [Symbol.toPrimitive] () {
        console.group('Symbol.toPrimitive')
        console.log(this.arr)
        console.groupEnd('Symbol.toPrimitive')
        return this.arr.pop()
    }
}
if (c == 1 && c == 2 && c == 3) {
    console.log('biu')
}

var d = [1, 2, 3]
d.join = d.shift
if (d == 1 && d == 2 && d == 3) {
    console.log('biu')
}

鱼头注:事实上,这四种可以算是同一种。关于最后一种,我们可以来看看ECMA中的 Array.prototype.toString ( ) 定义:

  1. 定义 arrayToObject(this value)(原生方法,将当前数组转换成对象);
  2. 定义 funcGet(array, 'join')(原生方法,在这一步调用 join 方法);
  3. 如果 IsCallble(func) (原生方法,判断是否有内部可调用的函数)为 false,则 设置 func 原生函数 %ObjProto_toString%(原生函数,toString 的具体实现);
  4. 返回 Call(func, array)

2. 对 getter 的劫持

所谓的 getter 就是对象属性在进行查询时会被调用的方法 get,利用此函数也可以实现题目功能。

代码如下:

window.val = 0
Object.defineProperty(window, 'd', {
    get () {
        return ++this.val
    }
})
if (d == 1 && d == 2 && d == 3) {
    console.log('biu')
}

const e = new Proxy({}, {
  val: 1,
  get ()  {
    return () => this.val++;
  }
});
if (e == 1 && e == 2 && e == 3) {
    console.log('biu')
}

3. 正则表达式

JS 中的 RegExp.prototype.exec() 作用是在一个指定字符串中执行一个搜索匹配,返回一个结果数组或 null

当正则表达式使用 "g" 标志时,可以多次执行 exec 方法来查找同一个字符串中的成功匹配。当你这样做时,查找将从正则表达式的 lastIndex 属性指定的位置开始。(test() 也会更新 lastIndex 属性)。

lastIndex 是正则表达式的一个可读可写的整型属性,用来指定下一次匹配的起始索引。只有正则表达式使用了表示全局检索的 "g" 标志时,该属性才会起作用。

鱼头注:只有正则表达式使用了表示全局检索的 "g" 标志时,该属性才会起作用。

综上所述,我们可以有方案如下:

var f = {
    reg: /\d/g,
    valueOf () {
        return this.reg.exec(123)[0]
    }
}
if (f == 1 && f == 2 && f == 3) {
    console.log('biu')
}

鱼头注:上述方法其实也利用了类型转换的特点。

ivan0525

ivan0525 commented on May 24, 2019

@ivan0525
let num = 1
    Object.defineProperty(window, 'a', {
      get() {
        return num++
      }
    })
ivan0525

ivan0525 commented on May 24, 2019

@ivan0525
qinmeishuying

qinmeishuying commented on May 24, 2019

@qinmeishuying

const a = { value: 0 }
a.valueOf = function () {
return this.value += 1
}
console.log(a==1 && a==2 && a==3)

== 在js 中 会发生类型转换 ===全等择不会发生强转
== 会把左右两边的值 转化为相同的原始数据类型然后在去比较他们是否相当
如果输入的是个基本类型就直接返回这个类型值
如果输入是Object类型 那就先调用输入对象的valueOf()如果是基本类型的话就直接返回 如果不是在调用toSting() 是基本类型 就返回他
js 在解析a==1 时 1 是基本数据类型 所以会把a 转换为Nubmer
a.valueof 覆盖的原来的valueof 被调用时 会返回1 自增+1返回自身

=== 全等下不会执行 valueof 此时就可以 用到Object.defineProperty 因为get 和 set是可以通过"."操作符调用的方法
var v = 1;
Object.defineProperty(window,'a',{
get(){
return v++;
}
})
a == 1 && a == 2 && a==3

ZhangWeiC

ZhangWeiC commented on May 24, 2019

@ZhangWeiC

var aᅠ = 1;
var a = 2;
var ᅠa = 3;
console.log(aᅠ==1 && a== 2 &&ᅠa==3)

954545647

954545647 commented on May 26, 2019

@954545647

let a = {
[Symbol.toPrimitive]: (function() {
let i = 1;
return function() {
return i++;
}
})()
}

EcSunshine

EcSunshine commented on Jun 9, 2019

@EcSunshine

let a = {
valueOf : (fnction(){
let i = 1;
return function(){
return i++
}
})()
}

riluocanyang

riluocanyang commented on Jun 13, 2019

@riluocanyang

如何让a === 1 && a === 2 && a === 3的值为true

toString

let obj = {
  a:1,
  toString() {
    this.a ++;
  }
}
if(a === 1 && a === 2 && a === 3) {
  console.log('success')
}

valueOf

let obj = {
  a:1,
  valueOf() {
    this.a ++;
  }
}
if(a === 1 && a === 2 && a === 3) {
  console.log('success')
}

[Symbol.toPrimitive]

let obj = {
  a:1,
  [Symbol.toPrimitive]() {
    this.a ++;
  }
}
if(a === 1 && a === 2 && a === 3) {
  console.log('success')
}

Object.defineProperty

let a= 1
Object.defineProperty(window, 'a', {
   get() {
        return a++
   }
})
if(a === 1 && a === 2 && a === 3) {
  console.log('success')
}

修改数组join方法

var a = [1,2,3];
a.join = a.shift;
if(a === 1 && a === 2 && a === 3) {
  console.log('success')
}
index-swf

index-swf commented on Jan 11, 2021

@index-swf

var aᅠ = 1;
var a = 2;
var ᅠa = 3;
console.log(aᅠ==1 && a== 2 &&ᅠa==3)

这个答案用了不可见字符 \uffa0 作为变量名,过于取巧了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @ccvaille@DazhiFe@0uzu0@xtgcs@wubetter

        Issue actions

          如何让 (a == 1 && a == 2 && a == 3) 的值为true? · Issue #9 · YvetteLau/Step-By-Step