js隐式强制类型转换

隐式强制类型转换:通过一些操作导致的,如:运算操作、判断语句、循环语句。

显式类型转换:通过一些操作符,如:Number()、String()、toString()、Boolean()等一些很显而易见、目的明确的方式去进行的类型转换。

关于显式和隐式的类型转换,也是比较偏向主观的,如果你一眼就能看到某行代码执行的时候,发生了什么事情,那么这应该也能看成是显式的类型转换。或者说你不知道String()是干嘛用的,那么你看到一行代码调用该方法时,这也可以当成主观上的隐式类型转换。本文将着重讲解隐式强制类型转换。

先看一个例子:demo1

var a = [1, 2];
var b = [3, 4];
console.log(a + b);		// 1,23,4

例子中的a和b都不是字符串类型,那为什么会输出 1,23,4这个结果呢?原因是它们都被强制转换了字符串类型然后进行了拼接。

在+运算操作符中,如果有其中一个操作数能够通过valueOf或者toString方法转换为字符串,那么最后都会进行字符串的拼接。在上面的例子中,a和b在进行+运算时,最开始都是调用了vlaueOf的方法来获取原始数据,但是数组的valueOf方法无法获得基本数据类型,于是转为调用toString方法,数组调用toString方法的话是可以返回值的字符串类型的。实际上执行的是这样的代码:

console.log(a.toString() + b.toString());

demo2:

var a = 12;
var b = a + "";
console.log(typeof b, b);    // string 12

demo2的例子中,主要是数字加字符串,变成了字符串拼接,返回字符串类型的12也比较好理解。这种隐式强制类型转换也是比较常见的一种。这里说一下String(a)的类型转换和本例子中的类型转换的区别,当调用String方法时,如果值有toString方法,则使用该方法并返回结果。而本例中的转换过程详细来说应该是:a先调用valueOf方法,然后再通过toString方法将返回值转换为字符串。如果本例中的a要使用String来转换为字符串的话,是直接调用了toString方法的。


demo3:

var a = {
	valueOf: function() {return 42},
	toString: function() {return "4"}
}

console.log(a + "");            // "42"
console.log(String(a));         // "4"

本例子重写了对象的原型方法valueOf和toString,很好的验证了demo2中的说法,当a+“”的时候,确实是先调用了valueOf方法,而使用String方法则是直接调用了toString方法,因为对象是有toString方法的。


demo4:

扫描二维码关注公众号,回复: 6030345 查看本文章
var a = "5";
var b = 2;
console.log(a - b);    // 3

减法运算,字符串a会被强制类型转换为数字。


demo5:

var a = [10];
var b = [3];
console.log(a - b);    // 7

a和b都是数组,所以最开始都会被调用toString方法,之后调用Number方法转换为数字。因为 - 运算符只适用于数字,* 和 / 的运算同样也可以将字符串转为数字,只是大多数情况下不会这样做。


demo6:

var a = true;
var b = false;
var c = undefined;
var d = null;
var e = NaN;

console.log(1 + a);    // 2    等同于1 + 1
console.log(1 + b);    // 1    等同于1 + 0

console.log(c + a);    // NaN  undefined + true或者false 都会返回NaN,不能相加。
console.log(c + b);    // NaN

console.log(d + a);    // 1    null + true或者false,null会被强制转换为0
console.log(d + b);    // 0

console.log(e + a);    // NaN  NaN加任何值都会返回NaN
console.log(e + b);    // NaN

console.log(Number(null));    // 0

true和false进行 + 运算时,会被隐式强制类型转换为数字 1 和 0 。所以:

undefined + true或者false 都会返回NaN,不能相加。

null + true或者false,null会被强制转换为0。因为当数字和字符串以外的原始数据类型进行加法运算时,都会将该值进行数字的转换,而null调用Number时又是返回0。前面说了true和false在加运算时会被转为数字,所以null + true/false 时,等同于null + 1/0,然后等同于0 + 1/0)

NaN加任何值都会返回NaN


demo7:

var a = 20;
var b = "aaa";
var c;
var d = null;
if (a) {
    console.log("代码执行到这里");
}

while (c) {
    console.log("代码不会执行到这里");
}

c = d ? a : b;
console.log(c);     // "aaa"

if ((a && b) || c) {
    console.log("代码执行到这里");
}

在if、循环、三目运算、逻辑运算等操作都会隐式地将表达式的值强制转为布尔值


demo8:

var a = 5;
var b = "5";
console.log(a == b);		// true
console.log(a === b);		// false

本例子中使用了宽松相等 == 和 严格相等 === 来对 a 和 b 进行判断,在宽松相等中a和b是相等的,但是在严格相等中a和b是不相等的。这是为什么呢?答案是这里面也存在隐式强制类型转换。一直以来,大家可能都觉得宽松相等(==)主要是判断值是否相等;严格相等(===)主要是判断值和类型是否相等。也没错,确实是这样。但是从原理上来说,也可以说是宽松相等会进行隐式强制类型转换,而严格相等却不允许。

那么从这个例子上来看,虽然知道了会发生类型转换,但是a从数字转为字符串?还是b从字符串转为数字呢?根据ES5的规范,如果Type(a)是数字,Type(b)是字符串,那么返回a == Number(b)。如果Type(a)是字符串,type(b)是数字,那么返回Number(a) == b。按这个规范来看,个人觉得最后都是字符串会转为数字来进行比较的。所以本例中的比较实际上是这样的:

console.log(a == Number(b));		// true    允许隐式强制类型转换
console.log(a === b);		        // false   不允许隐式强制类型转换

关于== 和 ===的性能,有人会觉得, == 的性能会比 === 的性能要慢。没错,== 确实是要比 === 多花一点时间,但是这只是微秒级的差别,也就是万分之一秒左右,对于程序来说可忽略不计。但是无论 == 还是 === 都会检测类型,只是检测出来之后各自的操作不一样。


demo9:

var a = '666';
var b = true;
console.log(a == b);	// false

这是一个既简单又复杂的问题,可能很多人会觉得这会返回true,原因是忽略了一个隐式类型转换。首先来看一下a,它的值为字符串666,如果把它转为布尔类型,那绝对是返回true的。所以很多人到这里看到a为true,b也为true,所以是相等的。但实际上,在a == b(’666‘ == true)这里并没有发生布尔值的比较,字符串666也没有被转换为true。而最先被隐式强制类型转换的是b,b被转换为了1,也就是变成:’666‘ == 1。前面说过,宽松相等是会发生隐式强制类型转换的,所以字符串666变成了数字类型666。也就是:666 == 1,很显然是不相等的,所以最后返回了false。如果a是等于字符串1的话,结果就会返回true。

ECMA规定:

如果Type(a)是布尔值,则返回Number(a) == b;

如果Type(b)是布尔值,则返回a == Number(b);


demo10:

var a = 10;
var b = [10];
console.log(a == b);

基本类型(字符串、数字、布尔)和对象(对象、数组、函数)之间的相等判断,ECMA也是做了规定的:

如果Type(a)是基本类型,Type(b)是对象,那么返回a == ToPrimitive(b);

如果Type(a)是对象,Type(b)是基本类型,那么返回ToPrimitive(a) == b;

在本例当中,b会调用ToPrimitive抽象操作,返回了字符串10,即:10 == ’10‘,接着变成:10 == 10,所以就返回了true。

关于ToPrimitive抽象操作的原理,在这里可以先理解为调用了内部默认的方法,如:toString()、valueOf()、Number()之类的操作。


暂时先介绍到这里,如文中有不切当的地方欢迎各位大神指点。

猜你喜欢

转载自blog.csdn.net/joyvonlee/article/details/89527235