前言

javasciprt是属于弱类型的语言。灵活的同时也带来一些问题,有时我们修改一些代码时,看上去是对的,结果出现了我们意想不到的结果。

隐式类型转换

在 JavaScript 中,当我们进行比较操作或者加减乘除四则运算操作时,常常会触发 JavaScript 的隐式类型转换机制;而这部分也往往是令人迷惑的地方。譬如浏览器中的 console.log 操作常常会将任何值都转化为字符串然后展示,而数学运算则会首先将值转化为数值类型(除了 Date 类型对象)然后进行操作。

关系操作符(<, >, <=, >=)

与上述操作符一样,关系操作符的操作值也可以是任意类型的,所以使用非数值类型参与比较时也需要系统进行隐式类型转换
(1)如果两个操作值都是数值,则进行数值比较
(2)如果两个操作值都是字符串,则比较字符串对应的字符编码值
(3)如果只有一个操作值是数值,则将另一个操作值转换为数值,进行数值比较
(4)如果一个操作数是对象,则调用valueOf()方法(如果对象没有valueOf()方法则调用toString()方法),得到的结果按照前面的规则执行比较
(5)如果一个操作值是布尔值,则将其转换为数值,再进行比较
注:NaN是非常特殊的值,它不和任何类型的值相等,包括它自己,同时它与任何类型的值比较大小时都返回false。

相等操作符(==)

相等操作符会对操作值进行隐式转换后进行比较:
(1)如果一个操作值为布尔值,则在比较之前先将其转换为数值
(2)如果一个操作值为字符串,另一个操作值为数值,则通过Number()函数将字符串转换为数值
(3)如果一个操作值是对象,另一个不是,则调用对象的valueOf()方法,得到的结果按照前面的规则进行比较
(4)null与undefined是相等的
(5)如果一个操作值为NaN,则相等比较返回false
(6)如果两个操作值都是对象,则比较它们是不是指向同一个对象

测试用例

function Person(){}
Person.prototype = {
  toString() { return "ooo";},
  valueOf() { return 123;}
}
var p = new Person() ;
// ️ result in chrome

demo

date类型转换出现的问题

var date = new Date("1970/1/2 8:00:00");
date.valueOf() ; //console of Chrome : 86400000
date.toString(); //console of Chrome : "Fri Jan 02 1970 08:00:00 GMT+0800 (CST)" 

// '<=' '<' '==' don't have self-consistent
date <= 86400000 //console of Chrome : true
date <  86400000 //console of Chrome : false
date == 86400000 //console of Chrome : false 
// Oh! It's awesome!

//This make sense
date == date.toString() //console of Chrome : true
date <= date.toString() //console of Chrome : false
// Oh! It really make sense because 864000 <= "string" is equal to 864000 <= 0

date 在遇到'<' '<='的时候会默认转换至.valueOf() ,
而它遇到 '==' 的时候明明有.valueOf()却还是选择转换为 .toString()

解决该疑惑可以看

显示类型转换

显示的转换数据类型主要通过JS定义的数据转换方法。

转换为布尔值:

Boolean() :将一个值转换为其对应的Boolean值

var message = “Hello world!”;
var messageAsBoolean = Boolean(message)
console.log(messageAsBoolean);   //true

各种数据类型及其对应的转换规则。

数据类型 转换为true的值 转换为false的值
Boolean true false
String 任何非空字符串 “” (空字符串)
Number 任何非零数字值(包括无穷大) 0和NaN

Object 任何对象 null

Underfined n/a undefined

转换为数字:

有3个函数可以把非数值转换为数值:Number()parseInt()parseFloat()。第一个函数,即转型函数Number()可以用于任何数据类型,而另两个函数则专门用于把字符串转换成数值。这三个函数对于同样的输入会返回不同的结果。

转换为字符串

要把一个值转换为一个字符串有两种方式。第一种是使用几乎每个值都有的toString()方法,这个方法唯一要做的就是返回相应值的字符串表现。数值、布尔值、对象、和字符串值(没错,每个字符串也都有一个toString()方法,该方法返回字符串的一个副本)都有toString()方法。但nullundefined值没有这个方法。

动态类型及鸭子类型

动态类型语言的优点是编写的代码数量更少,看起来也更加简洁,程序员可以把精力更多地放在业务逻辑上面。虽然不区分类型在某些情况下会让程序变得难以理解,但整体而言,代码量越少,越专注于逻辑表达,对阅读程序是越有帮助的。

动态类型语言的缺点是无法保证变量的类型,从而在程序的运行期有可能发生跟类型相关的错误。这好像在商店买了一包牛肉辣条,但是要真正吃到嘴里才知道是不是牛肉味。

在JavaScript中,当我们对一个变量赋值时,显然不需要考虑它的类型,因此,JavaScript是一门典型的动态类型语言

这一切都建立在鸭子类型(duck typing)的概念上,鸭子类型的通俗说法是:“如果它走起路来像鸭子,叫起来也是鸭子,那么它就是鸭子。”

鸭子类型指导我们只关注对象的行为,而不关注对象本身,也就是关注HAS-A, 而不是IS-A。

var duck = {  
    duckSinging: function(){  
        console.log( '嘎嘎嘎' );  
    }  
};  
 
var chicken = {  
    duckSinging: function(){  
        console.log( '嘎嘎嘎' );  
    }  
};  
 
var choir = [];    // 合唱团  
 
var joinChoir = function( animal ){  
    if ( animal && typeof animal.duckSinging === 'function' ){  
        choir.push( animal );  
        console.log( '恭喜加入合唱团' );  
        console.log( '合唱团已有成员数量:' + choir.length );  
    }  
};  
 
joinChoir( duck );    // 恭喜加入合唱团  
joinChoir( chicken );    // 恭喜加入合唱团 

在动态类型语言的面向对象设计中,鸭子类型的概念至关重要。利用鸭子类型的思想,我们不必借助超类型的帮助,就能轻松地在动态类型语言中实现一个原则:“面向接口编程,而不是面向实现编程”。例如,一个对象若有push和pop方法,并且这些方法提供了正确的实现,它就可以被当作栈来使用。一个对象如果有length属性,也可以依照下标来存取属性(最好还要拥有slice和splice等方法),这个对象就可以被当作数组来使用。

0%