在《JavaScript中数据类型转换》一文中主要学习了JavaScript中的数据类型、数据类型检测和数据类型转换。从这篇文章中了解到了通过String()
和toString()
可以将值转换为字符串。那么在JavaScript中还有很多有意思的东西,也是让我这样的生手感到困惑的东西。比如String()
和new String()
有何不同,又比如这篇文章的标题toString()
和valueOf()
又有何不同。
String() vs toString()
通过前面的学习,知道String()
和toString()
都可以转换为字符串类型,但这两者之间还是有所区别的。
在JavaScript中,可以将JavaScript中任何数据类型转换为字符串。
console.log(String(123), typeof String(123));
console.log(String('123'), typeof String('123'));
console.log(String(function fn(){return 1}), typeof String(function fn(){return 1}));
console.log(String([1,2,3]), typeof String([1,2,3]));
console.log(String({a: 1}), typeof String({a: 1}));
console.log(String(null), typeof String(null));
console.log(String(undefined), typeof String(undefined));
console.log(String(new Date()), typeof String(new Date()));
console.log(String(/^(.)*$/), typeof String(/^(.)*$/));
在JavaScript中,每个数据类型都有一个.toString()
的方法:
在JavaScript中除了null
和undefined
之外的任何数据类型都可以通过.toString()
方法转换成字符串。比如:
console.log('=========数组:array.toString()========')
var months = ['Jan', 'Feb', 'Mar'];
console.log(months.toString(), typeof months.toString());
console.log('=========布尔值:boolean.toString()========')
var bool = true;
console.log(bool.toString(), typeof bool.toString());
console.log('=========日期:date.toString()========')
var date = new Date();
console.log(date.toString(), typeof date.toString());
console.log('=========函数:function.toString()========')
var fn = function(a, b) {return a + b};
console.log(fn.toString(), typeof fn.toString());
console.log('=========数值:number.toString()========')
var count = 10;
console.log(count.toString(), typeof count.toString());
console.log('=========对象:object.toString()========')
var obj = {a: 123}
console.log(obj.toString(), typeof obj.toString());
console.log('=========正则:regExp.toString()========')
var reg = /^(.)*$/;
console.log(reg.toString(), typeof reg.toString())
console.log('=========字符串:string.toString()========')
var str = '123abc'
console.log(str.toString(), typeof str.toString());
console.log('=========symbol.toString()========')
var sym = Symbol('desc');
console.log(sym.toString(), typeof sym.toString())
前面也提到过,null
和undefined
值没有toString()
。因此在对这两个值进行了toString()
后,如果变量为null
或者undefined
的时候就会报错。
多数情况下,调用toString()
方法不必传递参数。但是,在调用数值的number.toString()
方法时,可以传递一个参数,输出数值的基数。默认情况下,number.toString()
方法以十进制格式返回数值的字符串表示。而通过传递基数,number.toString()
可以输出以二进制、八进制、十六进制,乃至其他任意有效进制格式表示的字符串。
var count = 10;
console.log(count.toString()); // => 10
console.log(count.toString(2)); // => 1010
console.log(count.toString(8)); // => 12
console.log(count.toString(10)); // => 10
console.log(count.toString(16)); // => a
通过这个例子可以看出,通过指定基数,number.toString()
方法会改变输出的值。而数值10
根据基数的不同,可以在输出时被转换为不同的数值格式。注意,默认的(没有参数的)输出值与指定基数10
时的输出值相同。
在JavaScript中,如果不知道要转换的值是不是null
或undefined
的情况下,建议使用String()
,因为这个函数能够将任何类型的值转换为字符串。如果值有toString()
方法,则调用该方法(没有参数)并返回相应的结果。
toString()
和String()
的主要区别就在于String()
还能转换null
和undefined
值,也可以说String()
是toString()
增强版,在开发中直接使用String()
似乎更好,这样能避免潜在的转换风险。
String() vs new String()
在JavaScript中有各种内建类型,通常称为原生类型,比如String()
就是一种原生类型。原生类型实际上是内建函数。
JavaScript的String()
看起来像是曾经用来创建字符串的String(...)
构造器。在很多时候可以看到这样的代码:
var str = new String('W3cplus');
console.log(str.toString()); // => 'W3cplus'
这些原生类型可以被用作一个原生类型的构造器。但是被构建出来的东西和我们想象的还是有所不同:
var str = 'W3cplus';
console.log(String(str), typeof String(str));
console.log(str.toString(), typeof str.toString());
console.log(new String(str), typeof new String(str));
上面的示例很明显看出来,它们的不同之处。String()
和toString()
转出来的是string
,但new String()
转换出来的是一个object
。而这个对象是一个对基本类型值str
的包装器对象。
var a = new String('w3cplus');
typeof a; // => object
a instanceof String; // => true
Object.prototype.toString.call(a); // => [object String]
重点是:new String('w3cplus')
为'w3cplus'
创建了一个字符串包装器对象,而不仅是基本类型值w3cplus
本身:
var a = 'w3cplus';
String(a) === a.toString(); // => true
String(a) === new String(a); // => false
a.toString() === new String(a); // => false
String
作为普通函数时会产生一个字符串(一个原始值)。作为构造函数时会产生一个String
对象的实例.后者在JavaScript中很少用到,所以基本上你可以忽略掉String
作为构造函数的用法,但一定要记得它是个转换函数.
另外有趣的是:
var a = 'w3cplus';
console.log(new String(a));
console.log(typeof new String(a));
console.log(new String(a).toString());
console.log(String(new String(a)));
从上面的示例中,使用toString()
或String()
将字符串对象转换为non-object
字符串。可能很多人都从没有想过调用String.toString()
可能是有用的。从这个示例中,也让初学者感到困惑:为什么在JavaScript中有两种字符串?
JavaScript中有两种类型的字符串:字面字符串和String对象。他们的行为有所不同。两者之间的主要区别在于你可以向
String
对象添加其他方法和属性。
来看个简单的示例:
var strObj = new String('object mode');
strObj.string_mode = 'object';
strObj.get_string_mode = function () {
return this.string_mode
}
str = strObj.toString(); // => 'object_mode'
这个时候,字符串文字只是临时转换为String
对象来执行任何核心方法。 同样的概念也适用于其他数据类型。
另外,new String()
转换出来的是一个String
对象。使用typeof
来判断后,其结果为object
,其实这个时候它就打上了一个内部的标签属性[[Class]]
。这个也常常称为内部属性。这个内部属性不能直接被访问,但通常可以间接地通过在这个值上借用默认的Object.prototype.toString(...)
方法调用来展示:
console.log(Object.prototype.toString.call([1,2,3])) // => [object Array]
console.log(Object.prototype.toString.call(true)) // => [object Boolean]
console.log(Object.prototype.toString.call(42)) // => [object Number]
所以对于上面的示例而言,数组内部的[[Class]]
值是'Array'
,布尔值它是'Boolean'
,数值是'Number'
。在大多数情况下,这个内部的[[Class]]
值对应于关联这个值的内建的原生类型构造器,但事实却不总是这样。
基本类型呢?首先,null
和undefined
:
Object.prototype.toString.call( null ); // => "[object Null]"
Object.prototype.toString.call( undefined ); // => "[object Undefined]"
你会注意到,不存在Null()
和Undefined()
原生类型构造器,但不管怎样"Null"
和"Undefined"
是被暴露出来的内部[[Class]]
值。
但是对于像string
,number
,和boolean
这样的简单基本类型,实际上会启动另一种行为,自动包装成一个封装对象,这种行为通常也被称为封箱。
Object.prototype.toString.call( "abc" ); // => "[object String]"
Object.prototype.toString.call( 42 ); // => "[object Number]"
Object.prototype.toString.call( true ); // => "[object Boolean]"
另外在JavaScript中,基本类型没有 .length
和.toString()
等这样的属性和方法,需要封装对象才可使用,此时JavaScript会为基本类型值自动包装成一个封装对象,浏览器对此也进行了优化,因此应优先考虑用基本类型值,而不是new String("abc")
新对象。
var str = 'w3cplus';
str.length; // => 7
一般来说,基本上没有理由直接使用对象形式。让封箱在需要的地方隐式地发生会更好。换句话说,永远也不要做new String("abc")
,new Number(42)
这样的事情——应当总是偏向于使用基本类型字面量"abc"
和42
。
在实际的使用中,一般不推荐直接使用封装对象,如果想要自行封装基本类型值,可以使用Object(...)
函数,而不带new
关键字:
var a = 'w3cplus';
var b = new String(a);
var c = Object(a);
console.log(typeof a); // => string
console.log(typeof b); // => object
console.log(typeof c); // => object
console.log(a instanceof String); // => false
console.log(b instanceof String); // => true
console.log(c instanceof String); // => true
console.log(Object.prototype.toString.call(a)); // => [object String]
console.log(Object.prototype.toString.call(b)); // => [object String]
console.log(Object.prototype.toString.call(c)); // => [object String]
开箱 valueOf()
有封装就有开箱,如果你有一个包装对象,但你又想取出底层的基本类型值,这个时候就可以使用valueOf()
方法。
var a = new String( "abc" );
var b = new Number( 42 );
var c = new Boolean( true );
a.valueOf(); // => "abc"
b.valueOf(); // => 42
c.valueOf(); // => true
在JavaScript中,valueOf()
函数算是一个非常少用的内建函数,但这个valueOf()
却十分重要。许多封装的对象都有这个valueOf()
函数,比如Boolean.valueOf()
、Number.valueOf()
和String.valueOf()
等。
console.log('=====boolean.valueOf()=====');
var bool = new Boolean();
console.log(typeof bool);
console.log(bool.valueOf());
console.log('=====number.valueOf()=====')
var count = new Number(100);
console.log(typeof count);
console.log(count.valueOf());
console.log('=====string.valueOf()=====')
var str = new String('w3cplus');
console.log(typeof str);
console.log(str.valueOf());
console.log('=====date.valueOf()=====')
var date = new Date()
console.log(typeof date);
console.log(date.valueOf());
console.log('=====object.valueOf()=====')
var obj = new Object();
obj.name = 'w3cplus';
obj.age = 7;
console.log(obj);
console.log(typeof obj);
console.log(obj.valueOf());
console.log('=====regExp.valueOf()=====')
var reg = new RegExp('^a*b+', 'g');
console.log(typeof reg);
console.log(reg.valueOf());
console.log('=====function.valueOf()=====')
var fn = function (a, b) {return a + b};
console.log(typeof fn);
console.log(fn.valueOf());
console.log('=====Symbol.valueOf()=====')
var sym = Symbol('foo');
console.log(typeof sym);
console.log(sym.valueOf());
前面也提到过了,null
和undefined
是属于特殊用途的,他们并没有new Null()
和new Undefined()
这样的封装器,也就是说他们是没有对应的封装对象。从而没有对应的valueOf()
。
在JavaScript中,当我们比较两个变量时,使用==
比较的是两个变量的值,那么像下面这样其值是true
:
var str1 = 'w3cplus';
var str2 = new String('w3cplus');
typeof(str1); // => string
typeof(str2); // => object
str1 == str2 // => true
这个结果令我感到困惑,str1
是一个String
类型,而str2
却是一个Object
类型,但用==
比较两个变量时,结果却为true
。后来才知道,在JavaScript中会帮我们自动转换类型,然后再进行比对。
事实上,当JavaScript任意东西在进行比较运算时,比如==
、!=
、<
、<=
、>
、>=
等等,都会先执行valueOf()
或toString()
函数,取回其原始类型的值。当拿到了原始类型的值之后,再进行比较。
除了原始类型之外,还有其他类型存在。而这些类型在默认情况下是无法进行比较的。比如:
var obj1 = {};
var obj2 = {};
obj1 == obj2; // => false
obj1 < obj2; // => false
obj1 > obj2; // => false
这个时候,就算我们采用了valueOf()
或toString()
方法,比较出来的结果依旧是false
。
Object.prototype.valueOf = function() {
return this.name.length;
}
Object.prototype.toString = function() {
return this.name
}
var a1 = {'name': 'w3cplus'}
var a2 = {'name': 'w3cplus'}
a1 == a2; // => false
a1 > a2; // => false
a1 < a2; // => false
所以,在JavaScript中,所有的对象都是不相等的,每一个都是独立的对象实例(Object Instance),这是一个非常重要的特性,即便你的操作中使用了valueOf()
和toString()
方法,依旧是无法对自定义对象进行任何相等比较运算。
为了在JavaScript中进行比较运算,一定要先将对象通过valueOf()
或toString()
方法转换成原始类型的值。比如下面这个小示例:
var obj1 = {'name': 'W3cplus'}
var obj2 = {'name': 'Edison'};
obj1.valueOf();
obj2.valueOf();
obj1.toString();
obj2.toString();
obj1 == obj2;
obj1 > obj2;
obj1 < obj2;
Object.prototype.valueOf= function (){
return this.name.length;
}
obj1 > obj2;
obj1 < obj2;
obj1.valueOf();
obj2.valueOf();
Object.prototype.toString = function() {
return this.name
}
obj1 > obj2;
obj1 < obj2;
delete Object.prototype.valueOf
obj1 > obj2;
obj1 < obj2;
总结
这篇文章是学习JavaScript中String()
、new String()
、toString()
和valueOf()
的相关知识。整理的有点乱,但相对而言,对这几个东东有了更深一层的了解和理解。我想在今后的使用中,应该会更易了。因为只有了解了才能更好的使用,当然在实际使用的时候肯定还会碰到坑,只有填坑是最好的验证方法。
扩展阅读
- JavaScript中数据类型转换
- 你不懂JS:类型与文法 第三章:原生类型
- 前端工程研究:關於 JavaScript 中物件的 valueOf 方法
- JavaScript中,如何将一个值转换为字符串
- JavaScript学习笔记01
- String cast VS toString() VS String.valueOf()
如需转载,烦请注明出处:https://www.w3cplus.com/javascript/toString-vs-String-vs-valueOf.html