JavaScript系列-----Object之toString()和valueOf()方法 (2)
深入理解toString()和valueOf()函数
1.我们为什么要了解这两种方法
众所周知,toString()函数和valueOf函数,这两个函数是Object类的对象生来就拥有的,而且他们还可以允许我们重写,那么,这两个函数到底有什么用呢?
从名称上判断,toString()将对象转换为字符串,valueOf将对象转化为值.那么问题来了:什么时候需要将对象转换为值,又什么时候需要将对象转换为字符串呢?------这是我们这篇文章的核心。
在谈这个问题之前,我们先看一道题目:
这是一道很经典的题目,考察的就是我们对于一些基本概念的理解,当然也是一道很难的题目,甚至第一眼看起来是一道不可能实现的题目.
请用javascript语言实现, var a= add(1)(2)(3)(4)(5); //结果为5个数相加,15------------来源于http://dmitry.baranovskiy.com/post/31797647
var add = function (n) {
var result = n;
var func1 = function (m) {
result += m;
return func1;
}
func1.toString = function () {
return result.toString();
}//重写对象的toStrig函数
func1.valueOf = function () {
return result;
}//重写对象的valueOf函数
return func1;
}
var a = add(1)(2)(3)(4)(5);
console.log(a);//
console.log(a+10)//
答案
..上面的这道题目不是那么容易想到,而且一般来说,程序员也不会写这种自己看着都别扭的代码..我们只是以此来引申出一些最基本的概念,也是这篇文章存在的必要性。
从上面的举例中,我们可以看出: 在输出的时候,伴随着Function类型向String类型之间的转换。那么,这种转换是怎么发生的呢? 在第二部分,会给出令您满意的解释!
现在我们先需要知道一点: toString和valueOf这两个函数,是解释器用来帮我们自动完成类型之间的转换(一般是对象到基本类型的转换),进而输出令我们满意的结果。
2. 对象向基本数据类型的转换的规则
(1)vauleOf优先于toString()被调用的情况---------当对象作为操作数的时候(Date类型的除外)
先来看看下面操作运算时,数据类型的转换
var x="10"; var a=+x;
console.log(typeof a); //number var b=a+x;
console.log(typeof b); //string
上面只是我列举出的三种比较常见的类型转换,也是一些最基本的概念...下面花一点时间来解析这三种类型转换是怎么发生的
3-4行: "+"号作为一元运算符---- 在这个运算符下,对象向基本数据类型的转换规则:
(1).当操作数是基本数据类型的时候,调用Number()函数,将其转换为数值
(2). 当操作数是对象的时候,调用对象的toString或者valueOf函数,将对象转化为基本数据类型的值,然后再对该值调用Number()函数。
所以,根据以上的转换规则,上述的输出结果为 number 也就合理了.
但是,第二条转换规则里,蕴含了一个好大的坑啊:到底是调用toString()还是valueOf(),要是两个函数都能将对象转换为基本数据类型呢(比如Date类型的对象),你又调用谁呢?
类似下面这样:
var x = {
toString: function () {
return '0';
},
valueOf: function () {
return 1;
}
}
var a = + x; //在这一步,到底调用的是toString()还是valueOf()呢?,在 + 号作为二元运算符的部分,会给出解释
console.log(a);
1
输出结果
7-8行:“+”号作为二元运算符 ----- 在这个运算符下,对象向基本数据类型的转换规则
当“+”号作为2元运算符的时候情况就比较复杂了,因为"+"号可以当做字符串的连接,也可以当做数字的相加减。你可以查下转换规则,怎么也有个7,8条,看的人眼花撩换。我经过大量的测试,也查阅了一些的资料,总结出以下规律:
//伪代码,
a + b运算转换规则:
var Pa = toPrimitive(a);
var pb = toPrimitive(b);
try {
if ((Pa is String) || (Pb is String)) {
return contact(String(Pa), String(Pb));
} else {
return Number(Pa) + Number(Pb);
}
} catch (e) {
throw e;
}
//注: toPrimitive 是将操作数转化为基本数据类型,优先调用valueOf,若得到基本数据类型,则结束,否则继续掉用toString()。(也就是说valueOf的优先级高于toString())
熟知了这些,也就可以看出,+号作为一元运算符转换规则其实和其作为二元运算符差不多,转化为基本数据类型的时候总是优先调用valueOf()。下面来一个例子来验证:
var test = {
valueOf: function () {
return 1;
},
toString: function () {
return '0';
}
}
console.log( + test); //1
var result = test + test;
console.log(result); //2
谈完了上面这些,我们可以总结得出结论: 在操作数运算的时候(无论是一元的还是二元的运算),数据类型之间的转换总是优先调用valueOf()函数,但是“+”号在作为二元运算符时,这种优先顺序在应用于Date类型的对象时,被逆转了(toString的优先级较高)。
谨记:Date类型是一个特例,当且仅当在+号运算,且“+”号作为二元运算符时,toString()优先调用,比如下面这样,
var date=new Date(); console.log(+date); //仍然优先调用valueOf
console.log(date+"toString优先被调用");
//输出结果:
// 1421293488713
//Thu Jan 15 2015 11:44:48 GMT+0800toString优先被调用
(2):toString()优先于valueOf()被调用 的情况------当你想要输出结果是字符串的时候
当访问Object类型对象的变量,我们用[] 这种方括号访问的时候,方括号的内容总是优先转化为字符串,也就是优先调用 toString()函数。看下面这个例子:
1 var test = {
2 toString: function () {
3 return '0'
4 },
5 valueOf: function () {
6 return 1;
7 }
8 };
9
10 var object={};
11 object[test]=1000;
12 console.log(object); // 输出结果:Object { 0=1000}
此时调用的原则描述如下:
[a]以这种形式访问的时候:
var Pa=toPrimitive(a);
if(Pa is prmitive){
var str=String(Pa);
}else{
throw error;//cannot convert to string
}
[str]//str为字符串的形式
//注: toPrimitive()此时优先调用toString()函数,若结果为基本类型,返回,否则继续调用valueOf();
还有几种toString优先于valueOf()被调用的例子
var test = {
toString: function () {
return '0'
},
valueOf: function () {
return 1;
}
};
alert(test); //优先调用toString() 输出 0
对象直接输出,优先调用toString()
var test = {
toString: function () {
return '0'
},
valueOf: function () {
return 1;
}
};
var test1 = {
toString: function () {
return '0'
},
valueOf: function () {
return 1;
}
}
var array = [
test,
test1
];
console.log(array + '')
//我们分析一下输出的过程:
//1.array+" "执行操作的时候,优先调用array.valueOf()函数,发现不能输出基本类型的值
//2.然后调用array.toString()函数。这个函数被Array类重写了,重写后,对于每一个数组元素
//优先调用toString()函数,然后再调用valueOf()函数。(这是重写后的自定义的规则,并不是默认的)
二,数组转化为字符串优先选择toString
3.为什么会出现这些奇怪的现象
由上面可以看出,toString和valueOf这两个函数在转换的时候都是可能被调用的,只是在不同的环境下,调用的优先级不一样而已。这些对于我们程序员来说,可能是透明的,但是对于解释器说,它们干的活可就多了...一句话归结:解释器总是根据语境尽可能的转化为我们想要的结果。换句话说:
对象在作为操作数时,解释器总是优先调用valueOf()--(Date类型的对象在二元“+”运算时例外),而其他情况,解释器总是认为我们想要的是字符串,所以会优先调用toString()。
注:Date类型的对象之所以会在二元+运算时优先调用toString(),也是因为我们大多数情况下, 时间总是和字符串连接使用,而时间和一个数字相加的情况好少,所以Date类型中,toString()优先级才比较 高。
正是因为解释器总想完美的输出我们想要的结果,才会造成这种杂乱的现象和规则出现。天下间本没有完美的事物,矛和盾总是相依相存。
懂得规则,才能利用规则,回过头来看一下我们在最开始所出的题目吧。是不是就是对这些规则的合理利用呢。
题外话: 写这篇文章就是为了表明基础知识的重要性,并非追求一个稀奇古怪的程序,程序员追求的应该是通俗易懂的代码(大道至简)而不是这些看起来四不像的程序,切记本末倒置!
另外,看完了这些是不是有一种回头重新看书的冲动呀,如果有,那我的目的就达到了...哈哈!
备注: toLocaleString()这个函数是实现字符串的本地化的输出,一般和toString()输出的结果相同,没什么特殊的,就是一个普通的函数。在Date类型中,这个函数被重写了。
JavaScript系列-----Object之toString()和valueOf()方法 (2)的更多相关文章
- JavaScript引用类型之Array数组的toString()和valueof()方法的区别
一.转换方法 1.在JavaScript中几乎所有对象都具有toLocaleString().toString和valueof()方法,因为,所有的对象都继承自Object,而前面所说的方法都是Obj ...
- 简单说 JavaScript中的tostring( ) 与 valueOf( )方法
说明 所有的对象都继承有toString() 和 valueOf() 方法,对象到字符串,对象到数字的转换,会通过调用待转换对象的这两个方法中的一个来完成. 解释 toString( )方法的作用是: ...
- 区分javascript中的toString(),toLocaleString(),valueOf()方法
首先我们随意创建一个对象,这很简单,打开FF浏览器的Firebug切换到控制台或者打开webkit浏览器的审查元素功能. 输入以下内容: var obj1=[1,2,3,4,5] var obj2=[ ...
- JavaScript中Object.prototype.toString方法的原理
在JavaScript中,想要判断某个对象值属于哪种内置类型,最靠谱的做法就是通过Object.prototype.toString方法. ? 1 2 var arr = []; console.lo ...
- JavaScript的toString()和valueof()方法
toString()方法: 函数:函数 (function(){}).toString(); //返回"function(){}" typeof((function(){}).to ...
- javascript中toString和valueOf方法的区别
toString():将对象转为字符串 valueOf():获取对象的原始值, 1.针对基本类型的变量:如在string,number,boolean类型的变量上调用这两个方法时,直接返回原始值,即变 ...
- js中toString和valueOf方法的区别
toString 方法 返回对象的字符串表示形式. 语法:objectname.toString([radix]) objectname 必需.要为其搜索字符串表示形式的对象. radix 可选.为将 ...
- 一日一练-JS toString 和valueOf 方法的联系与区别
子曰:类型转换中toString 和valueOf 的联系与区别分析 首先是看看ES5 的规范是如何进行说明的 在这里有几个基础知识点需要了解一下: [[Class]] [[Class]] 属于Obj ...
- Java中区别.toString() ,(String),valueOf()方法
在java项目的实际开发和应用中,常常需要用到将对象转为String这一基本功能.本文将对常用的转换方法进行一个总结.常用的方法有Object.toString(),(String)要转换的对象,St ...
随机推荐
- ClassLoader类加载机制&&JVM内存管理
一.ClassLoader类加载机制 在java中类加载是遵循委派双亲加载的:通过调用loadClass方法逐级往上传递委派加载请求,当找不到父ClassLoader时调用其findClass方法尝试 ...
- Web服务器自定义错误页面
在使用Web服务器运行程序的时候,难免会出现诸如 404.500 等错误,那么如何针对不同的错误代码来自定义错误页面呢? 1.找到web项目的 web.xml 文件打开,添加以下标签代码,规则是 er ...
- yii2-swiftmailer入门
1. 安装 用yii 2.0框架,默认会有这个扩展 composer require --prefer-dist yiisoft/yii2-swiftmailer 修改composer.json,re ...
- ZOJ2402 Lenny's Lucky Lotto List 简单DP
Lenny's Lucky Lotto Lists Time Limit: 2 Seconds Memory Limit:65536 KB Lenny likes to play the g ...
- Docker入门系列(一):目标和安排
Docker入门系列(一) 这个系列的教程来源于docker的官方文档,此文档的目的在于一步一步学习docker的使用方法. 这一系列的教程有如下几篇文档: docker安装启动 构建第一个docke ...
- Vue中之nextTick函数源码分析
Vue中之nextTick函数源码分析 1. 什么是Vue.nextTick()?官方文档解释如下:在下次DOM更新循环结束之后执行的延迟回调.在修改数据之后立即使用这个方法,获取更新后的DOM. 2 ...
- 为选择屏幕的字段设置F4帮助
在没有参考 数据元素,域和搜索帮助的情况下,自定义F4 帮助 1,PARAMETERS: p_bukrs(4) TYPE C MATCHCODE OBJECT H_T001. 2,AT SELECTI ...
- 知识树杂谈Java面试(4)
一. Java集合 1. 集合分类: Collection.Map. 2. Collection: 3. Map 4. 注意点 a. List 有序.可重复:Set 无序.不可重复:Map 键值 ...
- win10 uwp 随着数字变化颜色控件
我朋友在做一个控件,是显示异常,那么异常多就变为颜色,大概就是下面的图,很简单 首先是一个Ellipse,然后把他的颜色绑定到Int,需要一个转换,UWP的转换和WPF差不多,因为我现在还不会转换,就 ...
- JFinal快速上手及注意事项
官方手册虽然写的很详细但是忽略的很多小的细节方面,不看源码,网络资料又少,很多新手找不到解决办法.所以养成出了问题,多看源码的习惯 部署helloJFinal 项目结构 - 相关代码 `package ...