本文深入解析 JavaScript 中 Object.prototype.valueOf 方法的底层机制。通过对照 ECMA5 规范,详细阐述了该方法的执行步骤、ToObject 抽象操作的转换规则,以及 Number、String、Boolean、Array 和 Function 等内置对象对 valueOf 的不同实现。文章结合具体代码示例,揭示了基本类型与包装对象在调用 valueOf 时的返回值差异,并探讨了其在实际开发中的潜在应用场景与注意事项。
Object.prototype.valueOf 规范解析
彭老湿近期月报里提到了valueOf方法,兴致来了翻了下ECMA5里关于valueOf方法的介绍,如下: 15.2.4.4 Object.prototype.valueOf ( ) When the valueOf method is called, the following steps are taken: 1. Let O be the result of calling ToObject passing the this value as the argument. 2. If O is the result of calling the Object constructor with a host object (15.2.2.1), then a. Return either O or another value such as the host object originally passed to the constructor. The specific result that is returned is implementation-defined. 3. Return O. 规范里面的对于valueOf的解释很短,大致为:调用ToObject方法(一个抽象方法,后面会讲到),并将this的值作为参数传入。 针对调用ToObject时传入的不同参数(this),返回值分别如下: 1、this为宿主对象时,返回值取决于浏览器的实现,即不同浏览器的返回可能不同(关于宿主对象,可参考http://www.w3school.com.cn/js/pro_js_object_types.asp) 2、this不是宿主对象,则返回ToObject(this)的值
| 参数类型 | 返回结果 |
| Undefined | 抛出TypeError异常 |
| Null | 抛出TypeError异常 |
| Number | 创建一个Number对象,它内部的初始值为传入的参数值 |
| String | 创建一个String对象,它内部的初始值为传入的参数值 |
| Boolean | 创建一个Boolean对象,它内部的初始值为传入的参数值 |
| Object | 返回传入的参数(无转换) |
基于 Object.prototype.valueOf 的定义及 ToObject 抽象方法的描述,我们可以推导出不同参数下的返回值情况。
| obj类型 | Object.prototype.valueOf.call(obj)返回结果 |
| Undefined | 抛出TypeError异常 |
| Null | 抛出TypeError异常 |
| Number | Number类型的对象,值等于obj |
| String | String类型的对象,值等于obj |
| Boolean | Boolean类型的对象,值等于obj |
| Object | obj对象本身 |
具体而言,当调用 ToObject 时传入的 this 参数不同,返回值也存在差异:若 this 为宿主对象,返回值取决于浏览器的具体实现,即不同浏览器可能返回不同的结果(关于宿主对象的定义,可参考 W3School 相关文档);若 this 不是宿主对象,则返回 ToObject(this) 的值。
基本类型与对象的行为示例
为了更直观地理解,以下列举几个具体的代码示例。
var num = 123;
console.log(num.valueOf()); //输出:123
console.log(num.valueOf()); //输出:'number'
var unde = undefined;
console.log(Object.prototype.valueOf.call(unde)); //输出:'TypeError: Cannot convert null to object'
var obj = {name:'casper'};
var linkObj = obj.valueOf();
linkObj.name = 'change';
console.log(linkObj.name); //输出:'change' ...说明obj.valueOf()返回的是对象自身上述示例未涉及 Array 和 Function 对象。根据后续代码推测,当 Object.prototype.valueOf 的参数为 Array 或 Function 类型的对象时,其返回结果同样为对象自身。
代码如下:
var arr = [1, 2 ,3];
var linkArr = arr.valueOf();
linkArr[0] = ['casper'];
console.log(linkArr); //输出:['casper', 2, 3]
var foo = function(){ return 1; };
var linkFoo = foo.valueOf();
linkFoo.test = 'casper';
console.log(linkFoo.test); //输出:'casper'阅读上述描述后,是否感到豁然开朗?如果确实如此,恭喜你,这可能意味着你和我一样,尚未完全透彻理解其深层机制。
Number 类型的特殊处理机制
举一个简单的例子,当调用 Object.prototype.valueOf 的对象为数值类型时,假设该对象名为 num,num 可能通过以下两种方式声明:
代码如下:
var num = 123; //通过对象字面量声明console.log(typeof num); //输出:'number'
var num = new Number(123); //通过构造方法声明console.log(typeof num); //输出:'object'关于返回值的说明,ECMA5里面原文如下:
Create a new Number object whose [[PrimitiveValue]] internal property is set to the value of the argument. See 15.7 for a description of Number objects.
根据这段文字说明,num.valueOf() 似乎应该返回一个 Number 对象(即非字面量声明的那种包装对象),但实际情况并非如此。
代码如下:
var num = 123;
var tmp = num.valueOf();
console.log(typeof tmp); //输出: 'number'5.7.4.4 Number.prototype.valueOf ( )
Returns this Number value.
The valueOf function is not generic; it throws a TypeError exception if its this value is not a Number or a Number object. Therefore, it cannot be transferred to other kinds of objects for use as a method.
其他内置对象的 valueOf 实现
原来 Number 拥有属于自己的原型 valueOf 方法,并非直接从 Object.prototype 继承而来。同理,Boolean 和 String 也有各自的原型 valueOf 方法,归纳如下:
| 类型 | 是否有属于自己的原型valueOf方法 |
| Undefined | 无 |
| Null | 无 |
| Number | 有,Number.prototype.valueOf |
| String | 有,String.prototype.valueOf |
| Boolean | 有,Boolean.prototype.valueOf |
| Object | - |
此外,Array 和 Function 并没有属于自己的原型 valueOf 方法,规范说明如下: NOTE The Array prototype object does not have a valueOf property of its own; however, it inherits the valueOf property from the standard built-in Object prototype Object. The Function prototype object does not have a valueOf property of its own; however, it inherits the valueOf property from the Object prototype Object. 补充说明:Number.prototype.valueOf 的内部转换规则比表面看起来更为复杂,此处不再展开。 经过上述详细阐述,目前仍存在两个疑惑: 1. 关于 ToObject,当参数为 Function 对象时,返回对象的具体处理方式在规范中似乎未明确说明,目前仅能通过实验猜测(也可能是我未找到相关文档)。 2. valueOf 的实际使用场景,在实际开发中鲜见有人使用。 最后: 文中示例如有错漏,请指出;如觉得文章对您有用,可点击“推荐” :)