js中类型转换和类型判断

744 阅读4分钟

js中类型转换分为显示转换和隐式转换,下面我总结一下都有哪些情况会发生类型转换。

一.显示转换

1.通过调用 parseIntparseFloatnew Number()等都会发生显示转换。在转换中的规则如下:

  • 第一个字符是有效数字,就会将有效数字部分转换为数值;

    console.log(parseInt('123abc')); // 123
    
  • 第一个字符是非数字,就会转换为NaN;

    console.log(parseInt('abc123')); // NaN
    

那么请看一下下面这道显式转换题:

var str = 'abc123',
    num = parseFloat(str);
if (num === NaN) {
    alert(NaN);
} else if (num === 123) {
    alert(123);
} else if (typeof num === 'number') {
    alert('number');
} else {
    alert('str');
}
// 结果是: 'number'

最终输出'number',原因是str第一个字符非数字,会转换成NaN,众所周知的NaN是不全等与NaN的。而typeof NaNnumber,所以输出结果是'number'

二、隐式转换

众所周知,js中当遇到+ - * / > < ==等运算符的时候,会发生隐式转换。而+和其他运算符有一些不同。除了+,其他都会转换为数值型。

1.+操作符

  • 对于+,遇到字符串那么就会按照字符串拼接去计算,而不是简单的加运算。

    console.log('123'+ 456); // 123456
    
  • 当非数字类型遇到+的时候,也会发生隐式类型转换。

    // Boolean类型遇到数值,会转换成数值型。 true=1; false=0
    console.log(true+1); // 2
    console.log(false+1); // 1
    console.log(false + false); // 0
    console.log(true + true); // 2
    
    // 当Boolean遇到字符串,会转换成字符串
    console.log(true+'1'); // true1
    
  • +作为***一元操作符***的时候,其位于操作树的前面。如果操作数不是一个数值,会尝试将其转换成一个数值。这时候就发生隐式转换了。尽管一元负号也能转换非数值类型,但是一元正号是转换其他对象到数值的***最快方法***,也是***最推荐***的做法,因为它不会对数值执行任何多余操作。遇到不能转换的返回NaN。会将布尔类型、null、字符串转换为数值型。

    +3     // 3
    +"3"   // 3
    +true  // 1
    +false // 0
    +null  // 0
    +function(val){ return val;} //NaN
    
    

看如下一道面试题:

var a = 'abc' + 123 + 456;
alert(a); // abc123456
var b = '456' - '123'; 
alert(b); // 333
var c = 1,
    d = '1';
var f = c > d ? (c < d ? c : d) : (c == d ? c : d);
alert(f); // 输出1

2.其他的隐式转换

  • 当作为对象属性的key值时,所有类型都会被toString()

    // 数值类型作为属性名的时候,会被转换为字符串
    var a = {};
    b = 123;
    a[b] = 'hello';
    console.log(Object.keys(a)) // ["123"]
    
    // 对象作为一个对象的属性名的时候,也会被转换为string。
    var a={}, 
    b={key:'123'};
    a[b] = 'hello';
    console.log(Object.keys(a)); // ["[object Object]"]
    
    // 而Symbol作为对象的属性名的时候,不会被转换.
    var a={}, b=Symbol('123'), c=Symbol('123');  
    a[b]='b';
    a[c]='c';  
    console.log(a[b]); // 输出 'b'
    

所以看如下一道面试题:

// 题一
var a={}, b='123', c=123;  
a[b]='b';
a[c]='c';  
console.log(a[b]); // 都会转换为字符串123,所以 a[c]会覆盖a[b],输出 c
-----------------------------
// 题二
var a={}, b=Symbol('123'), c=Symbol('123');  
a[b]='b';
a[c]='c';  
console.log(a[b]); // Symbol是生成唯一值的。 所以b不等于c,输出 'b'
------------------------------
// 题三
var a={}, b={key:'123'}, c={key:'456'};  
a[b]='b';
a[c]='c';  
console.log(a[b]); // 对象类型作为属性名,都会被转换为string,所以a[b]会被a[c]覆盖掉。输出'c'

三、类型判断方法

类型判断的方法有typeof、instanceof、Object.prototype.toString.call(xxx) === '[object yyy]'。他们都有哪些优缺点呢?

1.typeof

它只能判断基本数据类型有:number、string、boolean、symbol、undefined。对于对象类型和null,统一都返回object.

console.log(typeof '123'); // string
console.log(typeof 123); // number
console.log(typeof false); // boolean
console.log(typeof Symbol(123)); // symbol
console.log(typeof undefined); // undefined

console.log(typeof null); // object
console.log(typeof new Date()); // object

2.instanceof

它用来检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。就是检测某个实例对象是不是该函数的实例

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
const auto = new Car('Honda', 'Accord', 1998);

console.log(auto instanceof Car); //  true

console.log(auto instanceof Object); // true

3.Object.prototype.toString.call

最严谨的判断是通过Object.prototype.toString.call来判断类型。

console.log(Object.prototype.toString.call('123') === '[object String]'); // true

console.log(Object.prototype.toString.call(123) === '[object Number]'); // true

console.log(Object.prototype.toString.call(Symbol(123)) === '[object Symbol]'); // true

console.log(Object.prototype.toString.call(new Date()) === '[object Date]'); // true