本文探讨如何在JavaScript中通过重写Object.prototype.valueOf()来阻止对象被隐式转换为字符串。当valueOf()抛出错误时,加号运算符无法完成转换,从而强制开发者显式调用toString()。文章解析了ToPrimitive抽象操作的执行顺序,并解释了为何某些转换仍会成功,帮助理解JS底层类型转换机制。
JavaScript中通过重写valueOf()强制使用toString()
原文:Enforcing toString()
译文:javascript 中强制执行 toString()
译者:singleseeker
在 JavaScript 中,引擎通常会根据方法调用或运算符的需求,自动将值转换为所需的类型。这种隐式转换有时会导致难以察觉的错误。Brian McKenna (@puffnfresh) 建议采用以下测试代码来验证这一行为:
代码如下:
Object.prototype.valueOf = function () {
throw new Error('Use an explicit toString');
};
[\s\S ]*\n
这段代码会产生什么效果?执行后,你将无法再使用加号运算符将对象隐式转换为字符串:
[code]
> var obj = {};
> 'Hello '+obj Error: Use an explicit toString
代码如下:
> String(obj)
'[object Object]'
> obj.toString()
'[object Object]'
> 'Hello '+String(obj) 'Hello [object Object]' 这又是为什么呢?要将一个对象转换为特定的基本类型 T,首先需将其值转换为基本类型,然后再转换为 T。前一步转换由以下两步组成:
1. 调用 valueOf() 方法,如果返回一个基本类型,则转换结束
2. 否则,调用 toString() 方法。如果返回一个基本类型,则转换结束
3. 若以上步骤均失败,则抛出错误
如果最终转换目标是数值,则会按照上述调用 valueOf() 与 toString() 的顺序进行。
如果最终转换目标是字符串,则 toString() 会被优先调用。加号运算符可能将值转换为数值型或字符串型,但通常根据数字运算产生基本类型。
如果不使用文章开头发布的代码片段,Object.prototype.valueOf() 会返回对象本身,这是从原生对象继承且未被重写的方法:
代码如下:
> var obj = {};
> obj.valueOf() === obj true 加号运算符最终会调用 toString()。上面的代码片段通过抛出错误阻止了该调用,使得在能调用该方法前就中断了过程。
需要注意的是,这个错误信息并不总是完全准确。
代码如下:
> Number(obj) Error: Use an explicit toString 但这一技巧仍然有用。
如果一个对象确实需要被转换为数字,它无论如何都要调用自己的 valueOf 方法。
@singleseeker 补充说明:翻译这篇文章时确实有些曲折,不过知识点总结得还不错。这是一篇由非英语母语者撰写的英文技术文章,交给我这个非英语母语者来翻译,过程确实颇具挑战。下面进行简单的总结。
1. 通常 valueOf() 指示返回一个未转换的对象,即其本身
2. 加号运算符除了 Date 对象外,几乎全是先调用 valueOf() 方法
3. 如果使 valueOf() 返回一个明确的基本数值类型,那么当一个对象与字符串相加时,toString() 将不会被调用
参考
1. 强制转换对象(objects)为原始值(primitives)
2. JavaScript 中,{}+{}等于多少?