Advanced-Frontend/Daily-Interview-Question

第 38 题:下面代码中 a 在什么情况下会打印 1?

zpzxgcr opened this issue · 65 comments

题目如下

var a = ?;
if(a == 1 && a == 2 && a == 3){
 	conso.log(1);
}

答案解析 因为==会进行隐式类型转换 所以我们重写toString方法就可以了

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

if( a == 1 && a == 2 && a == 3 ) {
  console.log(1);
}
let a = [1,2,3];
a.toString = a.shift;
if( a == 1 && a == 2 && a == 3 ) {
  console.log(1);
}

这题考察的应该是类型的隐式转换,考引用类型在比较运算符时候,隐式转换会调用本类型toString或valueOf方法.
解答:

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

这个题目考察==的隐式转换吧

利用toString

let a = {
  i: 1,
  toString () {
    return a.i++
  }
}

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

利用valueOf

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

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

数组这个就有点妖了

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

ES6的symbol

let a = {[Symbol.toPrimitive]: ((i) => () => ++i) (0)};
if(a == 1 && a == 2 && a == 3) {
  console.log('1');
}

从 (a==1&&a==2&&a==3) 成立中看javascript的隐式类型转换

let a = {
    gn: (function* () {
        yield 1;
        yield 2;
        yield 3;
    })(),
    valueOf() {
        return this.gn.next().value;
    }
};
Object.defineProperty(window, 'a', {
    get: function() {
        return this.value += 1;
    }
});

要改下,不然报错。你这个挺好,可以做 ===

Object.defineProperty(window, 'a', {
    get: function() {
	if (this.value) {
	    return this.value += 1
	} else {
	    return this.value = 1;
	}
    }
});
Object.defineProperty(window, 'a', {
    get: function() {
        return this.value += 1;
    }
});

要改下,不然报错。你这个挺好,可以做 ===

Object.defineProperty(window, 'a', {
    get: function() {
	if (this.value) {
	    return this.value += 1
	} else {
	    return this.value = 1;
	}
    }
});

精简一下代码:

Object.defineProperty(window, 'a', {
    get: function() {
          return this.value = this.value ? (this.value += 1) : 1;
    }
});

一开始没转过来,群里老哥提了一声才想到这个方向

var a = Object.create({
    count: 0,
    valueOf: function() {
        return ++this.count;
    }
});

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

第一反应想到的是a = console.log(1)😂😂

@XinJack 感觉你这个才是最优解啊,半天说不出一句话。

@XinJack console.log() 的返回值是 undefined

数组这个 a.join = a.shift; 没看懂啊

数组这个 a.join = a.shift; 没看懂啊

把 shift 方法的引用 ,放到 a.join 上的。覆盖原来的 join 方法

数组这个 a.join = a.shift; 没看懂啊

把 shift 方法的引用 ,放到 a.join 上的。覆盖原来的 join 方法

为啥a==1之后会执行join(shift)函数?

@seujzhang 执行 a ==1 会进行隐式转换

@seujzhang 执行 a ==1 会进行隐式转换

小白求教下,是不是这样的:在执行a==1的时候,会尝试对a进行隐式转换,此时隐式转换会调用Array的join方法,而此时join方法被shift覆盖,所以调用的实际上是shift方法,弹出1,然后相等,再弹出2相等,弹出3相等,最后console执行。

这个题目考察==的隐式转换吧

利用toString

let a = {
  i: 1,
  toString () {
    return a.i++
  }
}

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

利用valueOf

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

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

数组这个就有点妖了

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

ES6的symbol

let a = {[Symbol.toPrimitive]: ((i) => () => ++i) (0)};
if(a == 1 && a == 2 && a == 3) {
  console.log('1');
}

从 (a==1&&a==2&&a==3) 成立中看javascript的隐式类型转换

这个a.join = a.shift 是什么意思 还能这样子赋值麽。

第一反应想到的是a = console.log(1)😂😂

为什么总有一些让人眼前一亮的答案😂

对象转原始类型
Symbol.toPrimitive 优先级最高

var a = {
    i: 1,
    valueOf() {
        return a.i++;
    },
    toString() {
        return a.i++;
    },
    [Symbol.toPrimitive]() {
        return a.i++;
    }
}

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

来一手另类的👋👋

Object.prototype.toString = (function () {
  var t = 1;
  return function () {
    return t++;
  }
})()
var a = {}
a == 1 && a == 2 && a == 3  // true
var a = {
   value: 1,
   toString: function() {
     return a.value++;
   },
};
if(a == 1 && a == 2 && a == 3){
  console.log(1);
}
    

第一反应想到的是a = console.log(1)

蔡徐坤行为-_-!!!

第一反应想到的是a = console.log(1)joyjoy

为什么总有一些让人眼前一亮的答案

这操作属实有点优秀啊

var a = true;

let i = 1;
let a = new Proxy({},{
i:1,
get(){
return ()=> this.i++
}
})
console.log(a==1&&a==2&&a==3)

解释一下好吗?

var a = {
    i: 1,
    toString () {
        return this.i++
    }
}

if (a == 1 && a == 2 && a == 3) {
    console.log('真棒')
}

为什么a==true不行?

为什么a==true不行?

规范 11.9.3.6-7 是这样说的:
(1) 如果 Type(x) 是布尔类型,则返回 ToNumber(x) == y 的结果;
(2) 如果 Type(y) 是布尔类型,则返回 x == ToNumber(y) 的结果。
所以true==2;会先将true转为1,1==2即false

var a = {
	arr: [1,2,3],
	toString: function() {
		return  this.arr.shift();
	}
};

题目如下

var a = ?;
if(a == 1 && a == 2 && a == 3){
 	conso.log(1);
}

答案解析 因为==会进行隐式类型转换 所以我们重写toString方法就可以了

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

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

我想提一点

image

楼上这些答案已经很好了。我想请教一下在什么场景下才会应用到题目中的代码?

楼上这些答案已经很好了。我想请教一下在什么场景下才会应用到题目中的代码?

没有使用场景,仅仅是考察==隐式转换这个知识点。

突然发现js编程的乐趣~~~

var a = {
toString: function(){ //隐式转换,对象和数字做对比时,系统自动调用
this.index ++;
return this.index;
},
index:0
}

if(a ==1 && a==2 && a==3){
console.log(1)
}

// 使用valueOf
   var a = {
      value: 1,
      valueOf: function() {
        return this.value++
      }
    }

// 使用toString
   var a = {
      value: 1,
      toString: function() {
        return this.value++
      }
    }

但是请注意,如果两个同时存在,则valueOf优先级高于toString,而且
String(a) 调用的是toString

第一反应想到的是a = console.log(1)😂😂

JavaScript 的乐趣就在于此 💯

var a = ?;
if(a == 1 && a == 2 && a == 3){
  console.log(1);
}

看到答案瞬间懵逼...
这个东西反常识,写代码单元测试的人直接吐血...

数组这个 a.join = a.shift; 没看懂啊

数组隐式转换成字符串之前会执行一次join操作-> a.valueOf().join().toString()

现在都推===和typescript了,还在考这些垃圾特性...

现在都推===和typescript了,还在考这些垃圾特性...

哈哈哈哈

var a = {
  num: 1,
  valueOf() {
    return a.num++
  }
}
console.log(a == 1 && a == 2 && a == 3, '1-------')

var b = {
  num: 1,
  toString() {
    return b.num++
  }
}
console.log(b == 1 && b == 2 && b == 3, '2-------')

var c = {
  num: 1,
  [Symbol.toPrimitive](h) {
    return c.num++
  }
}
console.log(c == 1 && c == 2 && c == 3, '3-------')

var d = new Proxy(
  {},
  {
    i: 1,
    get: function() {
      return () => this.i++
    }
  }
)
console.log(d == 1 && d == 2 && d == 3, '4-------')

efun = {
  i: 1,
  get: function() {
    return efun.i++
  }
}
Object.defineProperty(global, 'e', efun)
console.log(e == 1 && e == 2 && e == 3, '5-------')

var f = [1, 2, 3]
f.join = f.shift
console.log(f == 1 && f == 2 && f == 3, '6-------')

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

数组这个 a.join = a.shift; 没看懂啊

如何a是Array,a==1 会自动数据类型的转换,也就是调用a.join方法,而a.shift是deleteArray的第一元素并返回delete的元素,understand?

来个*的

var a = new Number()
a.valueOf = (a=>()=>a++)(1)
if ( a == 1 && a == 2 && a == 3 ) {
  console.log('1');
}
var a = 1;
if(!(a == 1 && a == 2 && a == 3)){
 	console.log(1);
}

@XinJack console.log() 的返回值是 undefined

但也达到了打印 1 的目的啊

@seujzhang 执行 a ==1 会进行隐式转换

小白求教下,是不是这样的:在执行a==1的时候,会尝试对a进行隐式转换,此时隐式转换会调用Array的join方法,而此时join方法被shift覆盖,所以调用的实际上是shift方法,弹出1,然后相等,再弹出2相等,弹出3相等,最后console执行。

对,你打印一下a就看到了,实际上是給a添加一个join属性,这个属性就是shift方法,所以a.join是一个方法,隐士转换的时候调用a
的join()方法,而此时a自己有join属性了,就不会去Array原型链上找了,而a.join === Array.prototype.shift; 所以执行a.join()就相当于执行Array.prototype.shift方法,

let i = 0
Object.defineProperty('window', 'a', {
  get:()=>{
     return ++i
 }
}
var a = [1,2,3];
a.join = a.shift;
if(a == 1 && a == 2 && a == 3){
 	console.log(1);
}

//
var a = {
   i: 0,
   toString: function(){
        return this.i += 1;
   } 
};

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

//
var a = {
   i: 0,
   valueOf: function(){
        return this.i += 1;
   } 
};

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

//
Object.defineProperty(window, 'a', {

    get: function(){
        return this.value = this.value ? (this.value +=1  : 1;
}
});

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

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

来一手另类的👋👋

Object.prototype.toString = (function () {
  var t = 1;
  return function () {
    return t++;
  }
})()
var a = {}
a == 1 && a == 2 && a == 3  // true

覆盖原型还是不太好

var a = a ? ++a : 1;
if(a == 1 && a == 2 && a == 3){
 	console.log(1);
}

// 下面的也能输出
if(a === 1 && a === 2 && a === 3){
 	console.log(1);
}

这样不香嘛

    let times = 0;
    var a = {
      [Symbol.toPrimitive]: function () {
        return ++times;
      }
    };
    if (a == 1 && a == 2 && a == 3) {
      console.log(1);
    }
Y-J-H commented

最先想到的是get, set, 这样好像就没有var a了, 感觉有点偏题, 不过估计知识点应该是valueOf, toString, 哈哈哈

Object.defineProperty(window, 'a', {
  get: (function() {
    var i = 1
    return function () {
      return i++
    }
  })(),
  set: function(val) {
    return val
  }
})

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

var a = (function b(num){
  b.valueOf=function(){return ++num}
  return b
})(0)

为什么a==true不行?

Boolean和其他类型比较,先被转换为Number,true被转换成 1

数组这个 a.join = a.shift; 没看懂啊

如何a是Array,a==1 会自动数据类型的转换,也就是调用a.join方法,而a.shift是deleteArray的第一元素并返回delete的元素,understand?

@ihoneys 啊?隐式类型转换还会调用 join 方法,我还以为就 toString 和 valueOf 呢,还有就是那个 拆箱时ToPrimitive,还会调用哪些方法?

var a = a ? ++a : 1;
if(a == 1 && a == 2 && a == 3){
 	console.log(1);
}

// 下面的也能输出
if(a === 1 && a === 2 && a === 3){
 	console.log(1);
}

这样不香嘛

这个在那个环境运行的,我咋没打印出来1 啊

Object.defineProperty(window, 'a', {
    get: function() {
        return this.value += 1;
    }
});

要改下,不然报错。你这个挺好,可以做 ===

Object.defineProperty(window, 'a', {
    get: function() {
	if (this.value) {
	    return this.value += 1
	} else {
	    return this.value = 1;
	}
    }
});

精简一下代码:

Object.defineProperty(window, 'a', {
    get: function() {
          return this.value = this.value ? (this.value += 1) : 1;
    }
});

再精简一下:

Object.defineProperty(window, 'a', {
    get: function() {
          return this.value = (this.value || 0) + 1;
    }
});

第一反应想到的是a = console.log(1)😂😂

这都没有进 if 语句啊😂

(京东)下面代码中 a 在什么情况下会打印 1?

var a = ?;
if(a == 1 && a == 2 && a == 3){
 	console.log(1);
}
var a = {
  value: 1,
  toString() {
    return this.value++
  }
};

if(a == 1 && a == 2 && a == 3){
 	console.log(1);	// 1
}
var a = {
	arr: [1,2,3],
	toString: function() {
		return  this.arr.shift();
	}
};

if(a == 1 && a == 2 && a == 3){
 	console.log(1);	// 1
}
var a = {
	value: [1,2,3],
	toString: function() {
		return this.value.shift();
	},
  valueOf() {
    return 0
  }
};

console.log(a == 0)	// true

if(a == 1 && a == 2 && a == 3){
  console.log(1);
}
var a = {
	value: [1,2,3],
	toString: function() {
		return this.value.shift();
	},
  valueOf() {
    return {}
  }
};


if(a == 1 && a == 2 && a == 3){
  console.log(1);	// 1
}
var a = {
	value: [1,2,3],
	toString: function() {
		return {}
	},
  valueOf() {
    return {}
  }
};

// TypeError: Cannot convert object to primitive value
if(a == 1 && a == 2 && a == 3){
  console.log(1);
}
  1. 确定两个变量是否相等是编程中的一个非常重要的操作
    • 在比较字符串、数值和布尔值的相等性时,问题还比较简单;但在涉及到对象的比较时,问题就变得复杂了
    • 最早的ECMAScript中的相等和不等操作符会在执行比较之前,先将对象转换成相似的类型。后来,有人提出了这种转换到底是否合理的质疑
    • 最后,ECMAScript的解决方案就是提供两组操作符:
      • 相等不相等:先转换再比较
      • 全等不全等:仅比较而不转换
  2. 相等(==)和不相等(!=)在转换不同的数据类型时,遵循下列基本规则:
    • 如果有一个操作数是布尔值,则先将其转换为数值;(这也是为什么a = true不行的原因)
    • 如果有一个操作数是字符串,另一个操作数是数值,则先将字符串转换为数值
    • 如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较;**如果valueOf()也得不到基本类型值,则会调用toString();**如果toString()也得不到基本类型值,则会报错:TypeError: Cannot convert object to primitive value
var a = 1
if(a == 1 && ++a == 2 && ++a == 3){
  console.log(1);
}

估计设计语言的时候,也没想到被这群屌人玩的这么花。