Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
YvetteLau opened this issue May 23, 2019 · 60 comments
Open

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

YvetteLau opened this issue May 23, 2019 · 60 comments

Comments

@YvetteLau
Copy link
Owner

No description provided.

@toyxz
Copy link

toyxz commented May 23, 2019

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
Copy link

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

@dashengzi66
Copy link

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

@jizx
Copy link

jizx commented May 23, 2019

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

@tpxiang
Copy link

tpxiang commented May 23, 2019

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

@chang229
Copy link

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

@full-stop
Copy link

full-stop commented May 23, 2019

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

@Web3TomCat
Copy link

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

@taoyaoyaoa
Copy link

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
Copy link

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

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

@tianyuandsb
Copy link

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

@yelin1994
Copy link

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
Copy link

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
Copy link

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

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

@lqzo
Copy link

lqzo commented May 23, 2019

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

  • 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?

@YvetteLau YvetteLau reopened this May 23, 2019
@YvetteLau
Copy link
Owner Author

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

你这个也是一道经典的面试题
但是跟本题不是同一题呢~
注意审题哦~

@TTTTTTTZL
Copy link

``
var a = new Number();

var i = 1;

a.valueOf = function(){

return i++;
}

console.log(a == 1 && a == 2 && a == 3) ---> true

``
复杂数据类型与原始数据比较时,会隐式调用toString()和valueOf(),将对象转换成原始值;
所以object用来比较时,改写toString()和valueOf()方法即可;

@kaiking01
Copy link

let num=0;
Object.definedProperty(this, 'a', {
get(){
return ++num
}
})
console.log(a == 1 && a == 2 && a == 3) // true

@zyq503454535
Copy link

  var i = 1
  var a = new Number(1)
  Number.prototype.valueOf =function() {
    return i++
  }
  console.log(a == 1 && a == 2 && a == 3);

@wubetter
Copy link

对象形式

这种是通过 隐式转换时调用 toString 或者 valueOf 或者 Symbol.toPrimitive (es6)的形式来转换

  • toString
var a = {
    v:1,
    toString(){
        console.log('string');
        return this.v++
    },
}
if(a == 1 && a == 2 && a==3){
    console.log('成功')
}
  • valueOf
var a = {
    v:1,
    valueOf(){
        console.log('string');
        return this.v++
    },
}
if(a == 1 && a == 2 && a==3){
    console.log('成功')
}
  • Symbol.toPrimitive
var a = {
    v:1,
    //该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值
    //被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式(number,string,default)
    [Symbol.toPrimitive](h){
    	console.log(h);
        return this.v++
    }
}
if(a == 1 && a == 2 && a==3){
    console.log('成功')
}

注意:对象隐式转换成基本类型时,如果 Symbol.toPrimitivetoString 或者 valueOf 同时存在时 会优先调用 Symbol.toPrimitive
如果 Symbol.toPrimitive 不存在时 会先调用 valueOf,如果valueOf的返回值还是一个对象时(默认情况 会返回对象本身)则再调用
toString,如果toString 返回值还是一个对象(人为定义覆盖,默认会返回[object Object]) 则抛出错误

getter 拦截

var v = 1;
Object.defineProperty(window,'a',{
	get(){
		return v++;
	}
})
if(a == 1 && a == 2 && a==3){
    console.log('成功')
}

数组对象

var a = [1,2,3];
//方法1
a[Symbol.toPrimitive] = function(){return this.shift()};
//方法2
// arr.valueOf = function(){return this.shift()}
//方法3
//a.toString = function(){return this.shift()}

//方法4
a.join = a.shift;
if(a == 1 && a == 2 && a==3){
    console.log('成功')
}

对于方法4理解:a隐式转换会调用toString方法 而toString方法内部会调用 join方法

333

函数形式

var a = (()=>{
	let n=0;
	let s = ()=>1;
	s.toString = ()=>++n; //这里还可以用valueOf ,Symbol.toPrimitive
	return s;
})();
if(a == 1 && a == 2 && a==3){
    console.log('成功')
}

总结

这里除了 getter 其他的方法 都是隐式调用了 toStringvalueOfSymbol.toPrimitive 来完成转换

@Diamondjcx
Copy link

Diamondjcx commented May 23, 2019

== 会将左右两边的值转化成相同的原始类型,然后再去比较他们是否相等。

  1. 如果a是一个对象Object,在执行a==的时候首先会执行valueOf方法,如果没有valueOf方法,会执行toString方法。
    const a = { value: 0 }
    a.valueOf = function () {
    return this.value += 1
    }
    console.log( a == 1 && a == 2 && a == 3 );

  2. 如果a是一个数组Array,在数组转换成字符串的时候,数组toString会隐含调用join()方法
    const a = [1, 2, 3];
    a.join = a.shift;
    console.log( a == 1 && a == 2 && a ==3 );

@zxh008
Copy link

zxh008 commented May 23, 2019

!( a == 1 && a == 2 && a ==3 )
偷个懒~o( ̄︶ ̄)o

补充一种(今天刚刚get到的):
var a
console.log( (a == 1 && a == 2 && a ==3) || ture )
持续偷懒(^o^)/~

@chongyangwang
Copy link

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

@13775906336
Copy link

var num= 1
Object.defineProperty("window", "a", {
get(){
return num++
}
})

@Talljack
Copy link

var a = {
val: 0,
valueOf: function() {
return this.val += 1;
}
}
if (a == 1 && a == 2 && a == 3)
{console.log('xiaoxiao');}
// == 号会发生隐士类型转换,如果发现所比较的数类型不同的话就会进行转换
如果两个数一个为undefined另一个为null则直接返回true
如果一个为数字另一个为string则将string转换为Number
如果一个为Boolean另一个为Number则将Boolean转换为Number
如果一个为对象另一个为Number则将object转换为原始类型
1、首先会调用自身的valueOf,当然也会去查找原型上有没有,如果有则直接转
2、其次才会调用自身的toString方法,当然也会去原型链上查找是否存在,然后进行相应的转换
3、如果上面两个方法都没有找到那么就返回false
本题则是在考==号的隐士转换,其次是因为a要等于三个不同的数,所以自身改写函数返回不同值即可得到结果

@zhangxianhui
Copy link

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

@ccvaille
Copy link

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

// == 的时候会隐形调用了 valueOf() 或者 toString() 
console.log(a == 1 && a == 2 && a == 3); // true

@zhmfe
Copy link

zhmfe commented May 23, 2019

const a = {
   num: 0,
   valueOf: function() {
         return this.num += 1
   }
};
console.log(a==1 && a==2 && a==3); // true
  1. 使用相等操作符,js会做强制类型转化
  2. 我们的对象每次调用valueOf()它的值会增加1
  3. (a==1 && a==2 && a==3) 当他们比较时,JavaScript会企图将对象转化成数字的类型,进行比较。
  4. 当要转化的是一个Object的时候,JavaScript会调用本身的valueOf()方法,这个时候就可以实现
    this.num += 1

@YvetteLau
Copy link
Owner Author

1. 利用隐式转换规则

== 操作符在左右数据类型不一致时,会先进行隐式转换。

a == 1 && a == 2 && a == 3 的值意味着其不可能是基本数据类型。因为如果 a 是 null 或者是 undefined bool类型,都不可能返回true。

因此可以推测 a 是复杂数据类型,JS 中复杂数据类型只有 object,回忆一下,Object 转换为原始类型会调用什么方法?

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

如以下代码,我们可以清楚的看出优先调用的是 [Symbol.toPrimitive] 接口,其它规则大家可以自行编写代码进行验证。

let obj = {
    [Symbol.toPrimitive]() {
        return 100;
    },
    valueOf() {
        return 200;
    },
}
console.log(obj == 100);//true

那么对于这道题,只要 [Symbol.toPrimitive] 接口,第一次返回的值是 1,然后递增,即成功成立。

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

前面我们说了在没有部署 [Symbol.toPrimitive] 接口的情况下,隐式转换会调用 valueOf 接口,那么以下代码也是等价的:

let a = {
    valueOf: (function() {
            let i = 1;
            //闭包的特性之一:i 不会被回收
            return function() {
                return i++;
            }
    })()
}
console.log(a == 1 && a == 2 && a == 3); //true

另外一种调用 toString 接口的情况,相信无需我再说明。

2. 利用数据劫持

重写 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

ES6 新增了 Proxy ,在 Vue 中也得到了应用,大家可以思考一下,此处是否使用 Proxy 实现。

3. 重写数组的 join 方法。

数组的 toString 接口默认调用数组的 join 方法,

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

@web-data-MrLi
Copy link

对于这道题的理解和考察的知识点如下;
1.对二元运算的考察,
(1)‘==’和‘===’的理解,前者是隐式转化的比较,后者是类型和值得二者都相等的比较。
2.对隐式类型转化的考察,
对于对象的转化自身携带的两个原声的方法 toString()和valueOf()
let a={
a:1,
toSting(){
return a+=1;
}

}
if(a==1&&a==2&&a==3){
console.log("加油")
}
注意:a被隐式的转化成了数字,或者转化成了字符串或者原对象,进行比较

@xtgcs
Copy link

xtgcs commented May 23, 2019

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

@wangzhijun-github
Copy link

var a = {
n:1,
toString(){
return this.n++
},
valueOf(){
return this.n++
}
}
if(a == 1 && a == 2 && a == 3){
console.log('重写隐式调用')
}

@jodiezhang
Copy link

首先这一题涉及的知识点1:==和===的区别
===不需要类型转换,只有类型相同并且值相同,才返回true
==如果两者类型不同,首先需要进行类型转换
转换流程:
1.首先判断两者类型是否相同,如果相等,则判断值是否相等
2.如果类型不同,则进行类型转换
3.判断比较的是否是null或者undefined,如果是,则返回true
console.log(null==undefined);//true
4.判断两者类型是否为string和number,如果是把string转换成number
console.log('123'=123)//true
5.判断其中一方是否为boolean,如果是,将boolean转化为number在进行判断
console.log(false==0)//true
console.log(true==1)//true
6.判断其中一方是否为object且另一方为string,number或者symbol,如果是,将object转为原始类型再进行判断
let person1={
age:25
}
let person2=person1;
person2.age=20;
console.log(person1==person2)//true

法一:
const a = {
i: 1,
toString: function () {
return a.i++;
}
}

if(a == 1 && a == 2 && a == 3) {
console.log('Hello World!');
}

对象a和原始类型比较,对象会转为原始类型,再进行比较。
对象转换成原始类型的值,算法先调用valueOf()方法;
如果返回的还是对象,再接着调用toString方法
所以每次比较一次,都会返回数字,在自增。

法二:
利用数组的特性
var a=[1,2,3];
a.join=a.shift;
console.log(a==1&&a==2&&a==3);
Array对象的toString方法,会返回一个字符串,该字符串由数组的每个元素的toString返回值经调用
join()方法连接组成。
这里把join方法改为shift方法,每次返回第一个元素,原数组删除第一个值

法三:
var i=0;
with({
get a(){
return ++i;
}
}){
if (a==1&&a==2&&a==3)
console.log('Hello');
}

法四:修改了window的get方法
var val=0;
Object.defineProperty(window,'a',{
get: function(){
return ++val;
}});
if(a==1&&a==2&&a==3){
console.log('Hello');
}

法四: ES6的symbol特性,闭包返回函数
let a={[Symbol.toPrimitive]:((i)=>()=>++i)(0)}
console.log(a==1&&a==2&&a==3);//true

@luohong123
Copy link

luohong123 commented May 23, 2019

一、利用隐式转换原则

const a = {
  i:1,
  toString: function() {
    return a.i++;
  }
}
console.log(a==1 && a==2 &&a==3); // true

JavaScript的数据类型包括两种,基础数据类型(原始值)和复杂数据类型(对象值)

  • 基础数据类型
    String、Boolean、Number、Null、Undefined、Symbol
  • 复杂数据类型
    object

==两个等号时,当==左右两边数据类型不一致时,会发生隐式转换,所以基础数据类型肯定无法实现 console.log(a==1 && a==2 &&a==3); // true,考虑利用复杂数据类型 object
上述代码中一是利用隐式原则,二是利用闭包的变量不会回收的机制,使得a的值能继续累加

@0uzu0
Copy link

0uzu0 commented May 23, 2019

引用了这篇文章
说实话,没看懂~看来要仔细研究一下了

  1. 对象类型转换
    var a = { i:1, toString:function(){ return a.i++; } }

  2. 数组类型转换
    var a = [1,2,3]; a.join = a.shift; console.log(a == 1 && a == 2 && a == 3);

  3. 定义a的get
    var val = 0; Object.defineProperty(window, 'a', { get: function() { return ++val; } }); console.log(a == 1 && a == 2 && a == 3);

@tanxchen
Copy link

Proxy 形式实现:

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

@MissNanLan
Copy link

这种题目好像没碰到过,这下长见识了
toString()/ValueOf()
他们的作用是获取数字的基本类型

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

我看了小伙伴其他的方法还是不理解

@YvetteLau
Copy link
Owner Author

这种题目好像没碰到过,这下长见识了
toString()/ValueOf()
他们的作用是获取数字的基本类型

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

我看了小伙伴其他的方法还是不理解

可以看看我的那条答案,写得比较详细了~

@YvetteLau
Copy link
Owner Author

Proxy 形式实现:

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

手动点赞~

@0xBrooke
Copy link

首先,在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("艳姐,每天进步一点点,做不一样的自己!")
  } 

@0xBrooke
Copy link

当对象进行类型转换时,先执行valueOf() 函数,如果没有则执行toString()函数进行类型转换。

var arr =[2,3,"f"];
    arr.valueOf = function(){
        console.log("valueOf")
    }
    arr.toString = function(){
        console.log("toString")
    }
    console.log(arr==2)

艳姐,不知道这次是否能明白 可以试着把 valueOf 函数注释掉,在执行看一下效果

@muyiweibo
Copy link

muyiweibo commented May 24, 2019

想用proxy结果实验了好久没成功,只能换个方式了
var c = 0
window.defineGetter('a',function(){return ++c})//__竟然不能显示
a == 1 && a == 2 && a == 3

@YvetteLau
Copy link
Owner Author

想用proxy结果实验了好久没成功,只能换个方式了
var c = 0
window.defineGetter('a',function(){return ++c})//__竟然不能显示
a == 1 && a == 2 && a == 3

可以写在代码块中,不然会默认变成加粗。

@MissWXiang
Copy link

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

@Timoeser
Copy link

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

@YvetteLau
Copy link
Owner Author

首先,在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
Copy link
Owner Author

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

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

@DazhiFe
Copy link

DazhiFe commented May 24, 2019

我觉得这道题主要考验的是==运算符的工作机制和类型转换的一些知识。==的工作机制自己是明白,也知道对象在转换为原始类型的时候会调用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
Copy link

KRISACHAN commented May 24, 2019

此题目为网上经典面试题的严谨形态(意思就是判断的条件,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
Copy link

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

@ivan0525
Copy link

@qinmeishuying
Copy link

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
Copy link

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

@954545647
Copy link

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

@EcSunshine
Copy link

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

@riluocanyang
Copy link

如何让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
Copy link

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
Labels
None yet
Projects
None yet
Development

No branches or pull requests