本文所有代码都在 node v0.10.28
中测试通过,因为node用的也是v8的javascript引擎,所以理论上来说在chrome中的表现应该一致,其它引擎各位可以自己测试
我们先定义一个对象来进行比较
?
1
2
3
|
function foo() {
this
.name =
"foo"
;
}
|
再定义一个函数来比对
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
function compare(f) {
console.log(
"\ncompare with number:"
);
console.log(
"number f == 1: %s"
, f ==
1
);
console.log(
"number f == 0: %s"
, f ==
0
);
console.log(
"\ncompare with string:"
);
console.log(
"string f == \"\" : %s"
, f ==
""
);
console.log(
"string f == \"foo\" : %s"
, f ==
"foo"
);
console.log(
"\ncompare with boolean:"
);
console.log(
"boolean f == true : %s"
, f ==
true
);
console.log(
"boolean f == false : %s"
, f ==
false
);
console.log(
"\ncompare with object:"
);
console.log(
"object f == {} : %s"
, f == {});
}
|
?
1
|
compare(
new
foo());
|
输出结果:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
compare with number:
number f ==
1
:
false
number f ==
0
:
false
compare with string:
string f ==
""
:
false
string f ==
"foo"
:
false
compare with
boolean
:
boolean
f ==
true
:
false
boolean
f ==
false
:
false
compare with object:
object f == {} :
false
|
这个结果大家基本上都能理解
这次我们给这个对象添加一个方法 valueOf
然后再来进行比较看看
?
1
2
3
|
foo.prototype.valueOf = function() {
return
0
;
};
|
?
1
|
compare(
new
foo());
|
结果为:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
compare with number:
number f ==
1
:
false
number f ==
0
:
true
compare with string:
string f ==
""
:
true
string f ==
"foo"
:
false
compare with
boolean
:
boolean
f ==
true
:
false
boolean
f ==
false
:
true
compare with object:
object f == {} :
false
|
这个结果我们发现
和 0 比较
和空字符串比较
和false比较
这三种情况返回 true
这个结果让人不好理解
我们在valueOf方法中加入一些输出再来看看
?
1
2
3
4
|
foo.prototype.valueOf = function() {
console.log(
'valueOf: '
+
this
.name);
return
0
;
};
|
?
1
|
compare(
new
foo());
|
输出结果为:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
compare with number:
valueOf: foo
number f ==
1
:
false
valueOf: foo
number f ==
0
:
true
compare with string:
valueOf: foo
string f ==
""
:
true
valueOf: foo
string f ==
"foo"
:
false
compare with
boolean
:
valueOf: foo
boolean
f ==
true
:
false
valueOf: foo
boolean
f ==
false
:
true
compare with object:
object f == {} :
false
|
现在我们可以猜测一下了, javascript在进行对象和基本数据类型(暂且把string也当做一种基本数据类型,下面说基本数据类型的时候也会带上string)比较的时候,会调用对象的valueOf方法的返回值来进行比较.
这样就可以解释number比较中为什么和0比较是true了,
但是还有和空字符串比较是true,和false比较是true,这里我的理解是 javascript在数字和字符串以及boolean进行比较的时候,会转换成数字后进行比较,所以 0 == ""
和 0 == false
也是true
只有最后一个和对象比较的时候没有打印 valueOf: foo
所以也可以认为是对象比较时,只比较引用地址,理论上来说,对象比较 == 和 === 应该是一样的,例如如下代码:
?
1
2
3
4
5
|
var b1 =
new
Boolean(
false
);
var b2 =
new
Boolean(
false
);
console.log(b1 == b2);
console.log(b1 === b2);
|
输出两次false
这次我们将valueOf方法再修改一下,返回不是基本数据类型试一下,就返回自己吧
?
1
2
3
4
|
foo.prototype.valueOf = function() {
console.log(
'valueOf: '
+
this
.name);
return
this
;
};
|
?
1
|
compare(
new
foo());
|
输出结果为:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
compare with number:
valueOf: foo
number f ==
1
:
false
valueOf: foo
number f ==
0
:
false
compare with string:
valueOf: foo
string f ==
""
:
false
valueOf: foo
string f ==
"foo"
:
false
compare with
boolean
:
valueOf: foo
boolean
f ==
true
:
false
valueOf: foo
boolean
f ==
false
:
false
compare with object:
object f == {} :
false
|
这次和第一次比较没什么出入,只是打印了一些方法调用日志而已,结果也理所当然的应该这样了.
但是javascript为什么没有递归调用我们的valueOf
方法呢,按道理我们返回了自己,然后它进行比较的时候应该再次调用valueOf
的
这次我们再加入一个toString方法来看看
?
1
2
3
4
|
foo.prototype.toString = function(){
console.log(
this
.name +
" : toString"
);
return
this
.name;
}
|
?
1
|
compare(
new
foo());
|
输出结果为:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
compare with number:
valueOf: foo
foo : toString
number f ==
1
:
false
valueOf: foo
foo : toString
number f ==
0
:
false
compare with string:
valueOf: foo
foo : toString
string f ==
""
:
false
valueOf: foo
foo : toString
string f ==
"foo"
:
true
compare with
boolean
:
valueOf: foo
foo : toString
boolean
f ==
true
:
false
valueOf: foo
foo : toString
boolean
f ==
false
:
false
compare with object:
object f == {} :
false
|
我们发现每次输出valueOf
的后面都跟随了一个toString
的调用.
也就是说 javascript在调用valueOf后发现不是基本数据类型的时候,会调用toString的返回值再来进行比较
和我们观测到的结果一致,只有 f== "foo"
的结果是true
这样也可以解释为什么没有递归调用我们的valueOf
方法了
接下来我们再狠一点,toString
我们也返回自己,看看javascript会怎么处理
修改toString
方法为:
?
1
2
3
4
|
foo.prototype.toString = function(){
console.log(
this
.name +
" : toString"
);
return
this
;
}
|
?
1
|
compare(
new
foo());
|
这次的结果会在意料之外的:
结果为:
?
1
2
3
4
5
6
7
8
9
10
11
12
|
console.log(
"number f == 1: %s"
, f ==
1
);
^
TypeError: Cannot convert object to primitive value
at compare (/home/
0x0001
/Desktop/test.js:
25
:
38
)
at Object.<anonymous> (/home/
0x0001
/Desktop/test.js:
41
:
1
)
at Module._compile (module.js:
456
:
26
)
at Object.Module._extensions..js (module.js:
474
:
10
)
at Module.load (module.js:
356
:
32
)
at Function.Module._load (module.js:
312
:
12
)
at Function.Module.runMain (module.js:
497
:
10
)
at startup (node.js:
119
:
16
)
at node.js:
906
:
3
|
结果报异常了
最后我觉得,javascript在将对象和基本数据类型进行比较的时候,会先调用valueOf
的返回值来进行比较,如果valueOf
返回的不是基本数据类型,那么继续调用toString
方法的返回值来进行比较,
如果toString
的返回值还不是基本数据类型,那么就无法比较了