JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

时间:2024-04-11 12:37:47

这篇文章主要介绍了JavaScript中判断整字类型最简洁的实现方法,本文给出多个判断整数的方法,最后总结出一个最短、最简洁的实现方法,需要的朋友可以参考下

我们知道JavaScript提供了typeof运算符,因此最容易想到的是用typeof来判断是否是number类型。

复制代码代码如下:
1
2
3
function isNumber(obj) {
    return typeof obj === 'number'
}

  

这个函数对于整数和浮点数都没有问题,但对于NaN值也返回true这让人感到不爽,毕竟用isNumber判断通过后谁也不会用NaN去做算术运算。

那改进一下,用Object.prototype.toString试试。

复制代码代码如下:
function isNumber(obj) {
return Object.prototype.toString.call(obj) === '[object Number]'
}

和typeof判断一样,对于NaN也返回true,代码量还大了,这不是想要的结果。toString.call方式判断js数组(Array)可行,数字则力不从心了。

再改进下,NaN值用isNaN函数来对付。

复制代码代码如下:
1
2
3
function isNumber(obj) {
    return typeof obj === 'number' && !isNaN(obj)
}

  

这次,如果传入的是非数字(NaN或者可转成NaN的值)就返回false了

复制代码代码如下:
1
2
3
4
5
6
7
function isNumber(obj) {
    return typeof obj === 'number' && !isNaN(obj)
}
isNumber(1)   // true
isNumber(1.2) // true
isNumber(NaN) // false
isNumber( parseInt('a') ) // false

  

嗯,这个isNumber不错了,但还有一个等价的,用isFinite来判断

复制代码代码如下:
1
2
3
function isNumber(obj) {
    return typeof obj === 'number' && isFinite(obj)   
}

  

到现在,最短代码的数字判断是此文提到的第三个使用isNaN函数的。下面隆重推出世界上最短的数字判断网页代码

复制代码代码如下:
1
2
3
function isNumber(obj) {
    return obj === +obj
}

  

对于整数,浮点数返回true,对于NaN或可转成NaN的值返回false。

没看懂是吗? 咕~~(╯﹏╰)

园友说这还不是世界上最短判断数字代码,参数obj可以改成一个字符的。(⊙o⊙)你是对的。

举一反三,类似的利用JS动态语言特性(运算符运算时内部自动类型转换)最短的判断还有。

复制代码代码如下:
1
2
3
4
5
6
7
8
// 判断字符串
function isString(obj) {
    return obj === obj+''
}
// 判断布尔类型
function isBoolean(obj) {
    return obj === !!obj
}

 

我们都知道,JavaScript是类型松散型语言,在声明一个变量时,我们是无法明确声明其类型的,变量的类型是根据其实际值来决定的,而且在运行期间,我们可以随时改变这个变量的值和类型,另外,变量在运行期间参与运算时,在不同的运算环境中,也会进行相应的自动类型转换。

自动类型转换一般是根运行环境操作符联系在一起的,是一种隐式转换,看似难以捉摸,其实是有一定规律性的,大体可以划分为:转换为字符串类型转换为布尔类型转换为数字类型。今天我们就介绍一下这几种转换机制。

1. 转换为字符串类型(to string)

加号“+”作为二元操作符(binary)并且其中一个操作数为字符串类型时,另一个操作数将会被无条件转为字符串类型:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
// 基础类型

var foo = 3 + '';            // "3"

var foo = true + '';         // "true"

var foo = undefined + '';    // "undefined"

var foo = null + '';         // "null"

// 复合类型 var foo = [1, 2, 3] + ''; // "1,2,3" var foo = {} + ''; // "[object Object]" // 重写valueOf()和toString() var o = {
valueOf: function() {
return 3;
},
toString: function() {
return 5;
}
}; foo = o + ''; // "3" o = {
toString: function() {
return 5;
}
}; foo = o + ''; // "5"
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

从上面代码中可以看到,对于基础类型,会直接转为与字面量相一致的字符串类型,而对于复合类型,会先试图调用对象的valueOf()方法,如果此方法返回值是引用类型,则接着再调用其toString()方法,最后将返回值转为字符串类型。上面我们定义了一个对象,包含valueOf()和toString()方法,然后和一个空字符串进行运算,可以看得出来,它是调用了valueOf()方法,然后我们重写此对象,将valueOf()移除,也就是不重写object的valueOf()方法,从最后的结果来看,它最终是调用了toString()方法,然后将返回的数字类型5与空字符串进行运算,最终得到一个字符串类型的值。

2. 转为布尔类型(to boolean)

a. 数字转为布尔类型(from number)

当数字在逻辑环境中执行时,会自动转为布尔类型。0和NaN会自动转为false,其余数字都被认为是true,代码如下:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
// 0和NaN为false,其余均为true

if (0) {
console.log('true');
} else {
console.log('false'); // output: false
} if (-0) {
console.log('true');
} else {
console.log('false'); // output: false
} if (NaN) {
console.log('true');
} else {
console.log('false'); // output: false
} // 其余数字均为true if (-3) {
console.log('true'); // output: true
} else {
console.log('false');
} if (3) {
console.log('true'); // output: true
} else {
console.log('false');
}
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

从上面的代码中可以看出,非0负值也会被认为是true,这一点需要注意。

b. 字符串转为布尔类型(from string)

和数字类似,当字符串在逻辑环境中执行时,也会被转为布尔类型。空字符串会被转为false,其它字符串都会转为true,代码如下:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
// 空字符串为false

if ('') {
console.log('true');
} else {
console.log('false'); // output: false
} // 其他字符串均为true if ('0') {
console.log('true'); // output: true
} else {
console.log('false');
} if ('false') {
console.log('true'); // output: true
} else {
console.log('false');
}
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

c. undefined和null转为布尔类型(from undefined and null)

undefined和null在逻辑环境中执行时,都被认为是false,看下面代码:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
// undefined和null都为false

if (undefined) {
console.log('true');
} else {
console.log('false'); // output: false
} if (null) {
console.log('true');
} else {
console.log('false'); // output: false
}
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

d. 对象转为布尔类型(from object)

当对象在逻辑环境中执行时,只要当前引用的对象不为空,都会被认为是true。如果一个对象的引用为null,根据上面的介绍,会被转换为false。虽然使用typeof检测null为"object",但它并不是严格意义上的对象类型,只是一个对象空引用的标识。

另外,我们这里的逻辑环境不包括比较操作符(==),因为它会根据valueOf()和toString()将对象转为其他类型。

现在我们来看一下对象类型的示例:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
// 字面量对象
var o = {
valueOf: function() {
return false;
},
toString: function() {
return false;
}
}; if (o) {
console.log('true'); // output: true
} else {
console.log('false');
} // 函数
var fn = function() {
return false;
}; if (fn) {
console.log('true'); // output: true
} else {
console.log('false');
} // 数组
var ary = []; if (ary) {
console.log('true'); // output: true
} else {
console.log('false');
} // 正则表达式
var regex = /./; if (regex) {
console.log('true'); // output: true
} else {
console.log('false');
}
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

可以看到,上面的对象都被认为是true,无论内部如何定义,都不会影响最终的结果。

正是由于对象总被认为是true,使用基础类型的包装类时,要特别小心:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
// 以下包装对象都被认为是true

if (new Boolean(false)) {
console.log('true'); // output: true
} else {
console.log('false');
} if (new Number(0)) {
console.log('true'); // output: true
} else {
console.log('false');
} if (new Number(NaN)) {
console.log('true'); // output: true
} else {
console.log('false');
} if (new String('')) {
console.log('true'); // output: true
} else {
console.log('false');
}
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

根据们上面介绍的,它们对应的基础类型都会被转为false,但使用包装类实例的时候,引擎只会判断其引用是否存在,不会判断内部的值,这一点初学者需要多多注意。当然我们也可以不使用new关键字,而是显示的调用其包装类函数,将这些值转为布尔类型:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
if (Boolean(false)) {
console.log('true');
} else {
console.log('false'); // output: false
} if (Number(0)) {
console.log('true');
} else {
console.log('false'); // output: false
} if (Number(NaN)) {
console.log('true');
} else {
console.log('false'); // output: false
} if (String('')) {
console.log('true');
} else {
console.log('false'); // output: false
}
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

对于Boolean类,有一个特别需要注意的是,当传入一个字符串时,它不会去解析字符串内部的值,而是做个简单地判断,只要不是空字符串,都会被认为是true:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
if (Boolean('false')) {
console.log('true');   // output: true
} else {
console.log('false');
} if (Boolean('')) {
console.log('true');
} else {
console.log('false');  // output: false
}
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

上面介绍了这么多,还有几个例子需要提一下,那就是逻辑非、逻辑与和逻辑或操作符,连用两个逻辑非可以把一个值转为布尔类型,而使用逻辑与和逻辑或时,根据上面的规则,参与运算的值会被转换为相对应的布尔类型:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
// 下面几个转为false

var isFalse = !!0;            // false

var isFalse = !!NaN;         // false

var isFalse = !!'';           // false

var isFalse = !!undefined;    // false

var isFalse = !!null;         // false

// 下面都转为true

var isTrue = !!3;             // true

var isTrue = !!-3;            // true

var isTrue = !!'0';           // true

var isTrue = !!{};            // true

// 逻辑与

var foo = 0 && 3;             // 0

var foo = -3 && 3;            // 3

// 逻辑或

var foo = 0 || 3;             // 3

var foo = -3 || 3;            // -3
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

3. 转为数字类型(to number)

操作数在数字环境中参与运算时,会被转为相对应的数字类型值,其中的转换规则如下:

i. 字符串类型转为数字(from string): 空字符串被转为0,非空字符串中,符合数字规则的会被转换为对应的数字,否则视为NaN

ii. 布尔类型转为数字(from boolean): true被转为1,false被转为0

iii. null被转为0,undefined被转为NaN

iv. 对象类型转为数字(from object): valueOf()方法先试图被调用,如果调用返回的结果为基础类型,则再将其转为数字,如果返回结果不是基础类型,则会再试图调用toString()方法,最后试图将返回结果转为数字,如果这个返回结果是基础类型,则会得到一个数字或NaN,如果不是基础类型,则会抛出一个异常

一个其他类型的值被转换为数字,跟其参与运算的操作符有很密切的联系,下面我们就来详细介绍:

加号“+”作为一元操作符(unary)时,引擎会试图将操作数转换为数字类型,如果转型失败,则会返回NaN,代码如下所示:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
var foo = +'';            // 0

var foo = +'3';           // 3

var foo = +'3px';         // NaN

var foo = +false;         // 0

var foo = +true;          // 1

var foo = +null;          // 0

var foo = +undefined;     // NaN
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

上面代码中,对于不符合数字规则的字符串,和直接调用Number()函数效果相同,但和parseInt()有些出入:

var foo = Number('3px');      // NaN

var foo = parseInt('3px');    // 3

可以看出,parseInt对字符串参数比较宽容,只要起始位置符合数字类型标准,就逐个解析,直到遇见非数字字符为止,最后返回已解析的数字部分,转为数字类型。

加号“+”作为二元操作符时,我们上面也提到过,如果一个操作数为字符串,则加号“+”作为字符串连接符,但如果两个操作数都不是字符串类型,则会作为加法操作符,执行加法操作,这个时候,其他数据类型也会被转为数字类型:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
var foo = true + 1;          // 2

var foo = true + false;      // 1

var foo = true + null;       // 1

var foo = null + 1;          // 1

var foo = null + undefined;  // NaN

var foo = null + NaN;        // NaN
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

上面加法运算过程中都出现了类型转换,true转为1,false转为0,null转为0,undefined转为NaN,最后一个例子中,null和NaN运算时,是先转为0,然后参与运算,NaN和任何其他数字类型运算时都会返回NaN,所以最终这个结果还是NaN。

对于undefined转为NaN似乎很好理解,但为什么null会转为0呢?这里也有些历史渊源的,熟悉C的朋友都知道,空指针其实是设计为0值的:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
// 空指针的值为0

int *p = NULL;

if (p == 0) {
printf("NULL is 0"); // output: NULL is 0
}
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

编程语言的发展是有规律的,语言之间也存在着密切的关联,新的语言总是会沿用老的传统,继而添加一些新的特性。从上面的例子中,我们发现,null被转为0其实很好理解,一点也不奇怪。

另外,我们可别忘了减号“-”操作符,当减号“-”作为一元操作符(unary negation)时,也会将操作数转换为数字,只不过转换的结果与上面相反,合法的数字都被转为负值

除加号“+”以外的其他二元操作符,都会将操作数转为数字,字符串也不例外(如果转型失败,则返回NaN继续参与运算):

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
var foo = '5' - '2';          // 3

var foo = '5' * '2';          // 10

var foo = '5' / '2';           // 2.5

var foo = '5' % '2';          // 1

var foo = '5' << '1';          // 10

var foo = '5' >> '1';          // 2

var foo = '5' ** '2';          // 25

var foo = '5' * true;          // 5

var foo = '5' * null;          // 0

var foo = '5' * undefined;     // NaN

var foo = '5' * NaN;          // NaN
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

上面的操作符中,位移和求幂操作符平时用的不多,不过在某些场景下(比如算法中)还是挺实用的。我们都知道,JavaScript中的数字类型都以浮点型存储,这就意味着我们不能想C和Java那样直接求整除结果,而是通过相关的函数进一步处理实现的,如果通过位移可以简化不少,而求幂操作也可以直接通过求幂运算符算出结果,看下面代码:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
// 浮点型运算
var foo = 5 / 2; // 2.5 // 整除操作
var foo = Math.floor(5 / 2); // 2 // 向右移一位实现整除
var foo = 5 >> 1;   // 2 // 求幂函数
var foo = Math.pow(5, 2); // 25 // 求幂运算
var foo = 5 ** 2;   // 25
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

除了上面的操作符之外,递增和递减操作符也会将操作数转为数字,下面以前缀递增操作符为例:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
var foo = '';

++foo;    // foo: 1

var foo = '3';

++foo;    // foo: 4

var foo = true;

++foo;    // foo: 2

var foo = null;

++foo;    // foo: 1

var foo = undefined;

++foo;    // foo: NaN

var foo = '3px';

++foo;    // foo: NaN
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

上面就是基本数据类型在数字环境下的转换规则。对于对象类型,同样有一套转换机制,我们上面也提到了,valueOf()方法和toString()方法会在不同的时机被调用,进而得到相应的返回值,最后根据返回值再进行类型转换,将其转为目标类型。由于篇幅限制,关于自动类型转换的后续内容,博主安排在下一篇中讲解,敬请期待。

上一篇文章中,我们详细讲解了JavaScript中的自动类型转换,由于篇幅限制,没能覆盖到所有的转换规则,这次准备详细讲解一下。

上次我们提到了对象类型参与运算时转换规则:

1). 在逻辑环境中执行时,会被转换为true

2). 在字符串环境和数字环境中,它的valueOf()方法和toString()方法会依次被调用,然后根据返回值进行再次转换。首先,valueOf()方法会被调用,如果其返回值是基础类型,则将这个返回值转为目标类型,如果返回值不是基础类型,则再试图调用toString()方法,然后将返回值转型。如果最终的返回值不是基础类型,则转型会抛出一个异常,如果是基础类型,则会相应的转为字符串或数字。

接着上次的讲,当加号“+”作为一元操作符应用在对象类型上面时,valueOf()和toString()方法,将会有机会被调用,最终返回值会被转为数字类型,我们因而会得到一个数字或NaN。先来看看valueOf()和toString()的调用顺序:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
var o = {
valueOf: function() {
return '3';
},
toString: function() {
return '5';
}
}; var foo = +o; console.log(foo); // 3
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

可以看到,valueOf()方法被调用,返回了字符串类型的'3',然后被转为数字类型的3,而toString()方法并没有被调用,我们再次移除valueOf()方法:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
var o = {
toString: function() {
return '5';
}
}; var foo = +o; console.log(foo); // 5
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

这时候toString()方法就被调用了,根据其返回值'5',对象被成功转为了数字5。

估计很多初学者都会觉得,如果定义了valueOf()方法,就去调用valueOf()方法,如果没定义,就去调用toString()方法,其实是不准确的。

实际上,valueOf()方法总会在第一时间被调用,至于toString()方法的调用与否,那得看valueOf()方法的返回值了,我们上面也提到了,如果其返回值是基础类型,那么toString()方法根本没有机会被调用,而如果其返回值是引用类型,则会再试图调用toString()方法得到最终值。

通常,对象原型中的valueOf()方法返回其自身引用,拿上面的例子来讲:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
var o = {
toString: function() {
return '5';
}
}; console.log(o.valueOf() === o);  // true
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

我们用了全等(===)操作符来比较其valueOf()返回值和其自身,发现是完全相同的,证明对象原型中的valueOf()的返回值的确是其自身,上面结果等同于下面这段代码:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
// 重写实例中的valueOf()方法,其返回值是对象自身

var o = {
valueOf: function() {
return this;
},
toString: function() {
return '5';
}
}; console.log(o.valueOf() === o);  // true
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

现在我们稍加修改,就可以看出在类型转换过程中,到底发生了什么:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
var o = {};

Object.prototype.valueOf = function() {

    console.log('valueOf() called');

    return [];
}; Object.prototype.toString = function() { console.log('toString() called') return '5';
} var a = +o; // output: valueOf() called
// output: toString() called console.log(a); // 5 var b = o + ''; // output: valueOf() called
// output: toString() called console.log(b); // '5'
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

上面的代码中,我们改为修改原型方法valueOf()和toString(),分别在方法内部添加了控制台输出语句,另外,在valueOf()内部我们返回了一个数组对象。在对象参与运算时可以看到,两个方法依次被调用,不管是数字环境还是字符串环境,都先调用了valueOf()方法,由于返回值不是基础类型,所以还需再调用toString()方法,得到一个最终的返回值,然后将其转为目标类型。如果我们将valueOf()中的数组返回值替换为一个基础类型,toString()方法将不会有机会执行,大家可以亲自试一下。

上面也提到,对象原型的valueOf()方法默认是返回对象自身的,实际上,常见对象类型的valueOf()方法都会返回其自身:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
var o = {};

var fn = function(){};

var ary = [];

var regex = /./;

o.valueOf() === o;                // true

fn.valueOf() === fn;              // true

ary.valueOf() === ary;            // true

regex.valueOf() === regex;        // true
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

不过有个特殊的例外,Date类型的valueOf()会返回一个毫秒数:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
var date = new Date(2017, 1, 1);

var time = date.valueOf();

console.log(time);                      // 1485878400000

console.log(time === date.getTime());    // true
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

所以我们就会很容易明白,在Date实例上应用一元加号操作符,是如何返回一个数字的:

var date = new Date(2017, 1, 1);

var time = +date;

console.log(time);    // 1485878400000

不过Date真是个神奇的物种,如果我们直接跟拿它和一个时间毫秒数相加,并不会得到期望的结果:

var date = new Date(2017, 1, 1);

var time = date + 10000;

console.log(time);    // 'Wed Feb 01 2017 00:00:00 GMT+0800 (CST)10000'

它竟然转为了字符串,然后与数字进行了字符串连接操作!为什么会是这样的呢?原因在于,对于一元加号操作符运算,目的很明确,就是求正操作,因此引擎调用了其valueOf()方法,返回时间戳数字,而对于后者的二元加号加号操作运算,其存在加法和连接符这样的二义性,所以引擎可以有选择地将操作数转为不同的目标类型,与其他对象不同的是,Date类型更倾向于转为字符串类型,所以toString()会被先行调用。下面这段话是ECMA规范中关于Date类型转为基础类型的描述

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

大概的意思就是,对象在转为基础类型时,通常都会调用toPrimitive(hint)这样的方法,传入一个提示参数,指定其目标类型,如果不指定,其他对象的默认值都是number,而Date类型与众不同,它的默认值是string。

我们上面也提到了,一元加号操作符是求正运算,所以引擎能够识别并为其指定number目标类型,而二元加号操作符存在二义性,引擎使用了default作为提示参数,Date类型将默认值认为是string,所以我们也理解了上面的例子,即使是Date对象和数字相加,它也不会先调用valueOf()方法得到数字,而是先调用toString()得到一个字符串。

上面讲解了这么多,相信大家对于对象类型的转型规则都熟悉了,那么对于常见的对象,究竟是如何转为基础类型的呢?举个例子:

var foo = +[];            // 0 ( [] -> '' -> 0 )

var foo = +[3];           // 3 ( [3] -> '3' -> 3 )

var foo = +[3, 5];        // NaN ( [3, 5] -> '3,5' -> NaN )

从上面的代码可以看出,对于数组对象来说,要转为数字,就要遵循对象类型的转型规则,因为数组原型的valueOf()方法会返回其自身引用,所以最终会再试图调用其toString()方法,而它的toString()会返回一个字符串,这个字符串是由逗号分隔的数组元素集,那很容易理解了,对于空数组,必然返回一个空字符串,然后这个空字符串转型为数字之后就会变为0,而对于非空数组,如果只有一个元素并且元素可以转为数字,则结果第一个元素对应的数字,如果又多个元素,因为toString()返回的结果中存在逗号,所以无法转型成功,会返回一个NaN。

但如果我们尝试数组和一个数字相加,则还是会得到一个字符串的结果:

var foo = [] + 3;          // '3'

var foo = [3] + 3;       // '33'

var foo = [3, 5] + 3;     // '3,53'

你也许会说,这不是很像上面的Date类型吗?是的,结果看上去很相似,但其内部的执行过程还是有差异的,它的valueOf()会先执行,出现上面的结果,是由于valueOf()返回了this,然后再次调用toString()返回了字符串,加号操作符在这里成了字符串连接符了。

类似的还有字面量对象,看下面例子:

var foo = {} + 0;        // '[object Object]0'

var foo = {} + [];       // '[object Object]'

var foo = {} + {};       // '[object Object][object Object]'

不过如果是在命令行直接输入下面表达式,结果会有所出入:

{} + 0;        // 0

{} + [];       // 0

{} + {};       // NaN

其原因是,前面的字面量对象被解释成了代码块,没有参与运算,只有后面的一部分会返回最终的结果,后面的转换过程可以参照以上我们讲解的内容。

对象的类型转换规则,就先讲到这里,下面来讲一下比较操作符中的类型转换

比较操作符有以下几种:>, >=, <, <=, ==, ===。除了最后的全等操作符以外,其他几个在比较不同类型的数据时,均存在值的类型转换。

对于前四种来说,都遵循着以下规则:

1). 当两个操作数都为字符串类型时,不进行数据类型转换,直接比较每个字符

2). 当两个操作数不同时为字符串时,将操作数转为数字类型,然后进行比较

3). 如果操作数中存在对象类型,先将对象转为基础类型,然后再根据上面两条进行值的比较。

而对于“==”操作符,则是多了一条特殊的规则:null和undefined在比较时不进行数据转换,null和自身比较、null和undefined比较都会返回true,和其他值比较都会返回false;undefined和自身比较、undefined和null比较都会返回true,和其他值比较都会返回false。

以下比较操作不存在数据类型转换:

'a3' < 'b3';          // true

null == undefined;    // true

null == 0;            // false

需要注意的是最后一个表达式,由于我们在上一篇文章中讲到,null值在数字环境下会转型为0,很多人觉得这个表达式结果为true,但是不要忽略了上面关于null和undefined的规则,这里是不会有类型转换发生的。

在下面几个表达式中,操作数不全为字符串,所以要将操作数转为数字后再进行比较:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
3 == '3';             // true

3 < '5';             // true

0 == '0';        // true

0 == '';        // true

0 == false;        // true

1 <= true;            // true

null >= 0;            // true
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

注意,最后一个表达式中的null,在遇到>、>=、<、<=这几个操作符时会被转为数字0的,这与上面的规则有所不同。

最后,对象在参与逻辑运算时,同样会遵循前面的转型规则:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
var o = {
toString: function() {
return '3';
},
valueOf: function() {
return '5';
}
} o > 4; // true
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

特别注意的是,前面我们介绍到,对象在条件语句中是视为true的,但要避免下面这样的比较:

JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)
if ([]) {
// todo
} if ([] == true) {
// todo
}
JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

第二个条件语句块是不会执行的,原因在于空的数组对象被转为数字0了,而true被转为数字1,比较结果为false,所以里面的代码永远无法得到执行,开发时要警惕这样的写法。

写了这么多关于自动类型转换的内容,大家也可以体会到JS有多么的灵活,想要驾驭好这门语言,不是件容易的事,还需细细体会,好好研究才行。

本文完。