本文所有代码都在 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
的返回值还不是基本数据类型,那么就无法比较了