一、数据类型
JS中的简单数据类型可以分为五种:Number 、String 、Boolean、Undefined 、Null。
Number:数字类型 ,整型浮点型都包括;
String:字符串类型,右数字字母字符串以及标点符号组成,必须放在单引号或者双引号中;
Boolean:布尔类型,只有true和false两种值;
Undefined:未定义,一般指的是已经声明,但是没有赋值的变量,如var a;
Null:空对象类型,var a = null,和var a=""有区别;"typeof null"会返回"Object"
复杂数据类型:Object
Object:对象类型,由一组无序的名值对组成。
Date:
RegExp:
Function:
二、数据类型转换
【强制(显式)转换】——通过手动调用方法来转换
- 转换为数值类型:Number(mix)、parseInt(string,radix)、parseFloat(string)
- 转换为字符串类型:toString(radix)、String(mix)
- 转换为布尔类型:Boolean(mix)
1、Number(mix):将任意参数mix转换为数值型
(1)如果是布尔值,true和false分别被转换为1和0
(2)如果是数字值,返回本身。
(3)如果是null,返回0.
(4)如果是undefined,返回NaN。
(5)如果是字符串,遵循以下规则:
1、如果字符串中只包含数字,则将其转换为十进制(忽略前导0)
2、如果字符串中包含有效的浮点格式,将其转换为浮点数值(忽略前导0)
3、如果是空字符串,将其转换为0
4、如果字符串中包含非以上格式,则将其转换为NaN
(6)如果是对象,则调用对象的valueOf()方法,然后依据前面的规则转换返回的值。如果转换的结果是NaN,则调用对象的toString()方法,再次依照前面的规则转换返回的字符串值。
2、parseInt(string, radix)函数,将字符串转换为整数类型的数值。它有如下的规则:
(1)忽略字符串前面的空格,直至找到第一个非空字符
(2)如果第一个字符不是数字符号或者负号,返回NaN
(3)如果第一个字符是数字,则继续解析直至字符串解析完毕或者遇到一个非数字符号为止
(4)如果上步解析的结果以0开头,则将其当作八进制来解析;如果以x开头,则将其当作十六进制来解析
(5)如果指定radix参数,则以radix为基数进行解析
3、parseFloat(string)函数,将字符串转换为浮点数类型的数值。
它的规则与parseInt基本相同,但也有点区别:字符串中第一个小数点符号是有效的,另外parseFloat会忽略所有前导0,如果字符串包含一个可解析为整数的数,则返回整数值而不是浮点数值。
4、toString(radix)方法。除undefined和null之外的所有类型的值都具有toString()方法,其作用是返回相应值的字符串表示。
5、String(mix)函数,将任何类型的值转换为字符串,其规则为:
(1)如果有toString()方法,则调用该方法(不传递radix参数)并返回结果
(2)如果是null,返回”null”
(3)如果是undefined,返回”undefined”
注意:区分 String() 和 new String() ,前者返回的类型是字符串,而后者返回的类型是对象。对于 Boolean() 和 new Boolean() 也是类似的。
6、Boolean(mix)函数,将任何类型的值转换为布尔值。
以下值会被转换为false:false、”"、0、NaN、null、undefined,其余任何值都会被转换为true。
数据类型 | 转换为true的值 | 转换为false的值 |
Boolean | true | false |
String | 任何非空字符串 | “”(空字符串) |
Number | 任何非零数字值 | 0和NaN |
Object | 任何对象 | null |
Undefined | N/A | Undefined |
【隐式转换】——在某些情况的时候,即便我们不显式转换,JS也会自动转换
1、 用于检测是否为非数值的函数:isNaN(mix)
isNaN()函数,经测试发现,该函数会尝试将参数值用Number()进行转换,如果结果为“非数值”则返回true,否则返回false。
2、递增递减操作符(包括前置和后置)、一元正负符号操作符
这些操作符适用于任何数据类型的值,针对不同类型的值,该操作符遵循以下规则(经过对比发现,其规则与Number()规则基本相同):
(1)如果是包含有效数字字符的字符串,先将其转换为数字值(转换规则同Number()),在执行加减1的操作,字符串变量变为数值变量。
(2)如果是不包含有效数字字符的字符串,将变量的值设置为NaN,字符串变量变成数值变量。
(3)如果是布尔值false,先将其转换为0再执行加减1的操作,布尔值变量变成数值变量。
(4)如果是布尔值true,先将其转换为1再执行加减1的操作,布尔值变量变成数值变量。
(5)如果是浮点数值,执行加减1的操作。
(6)如果是对象,先调用对象的valueOf()方法,然后对该返回值应用前面的规则。如果结果是NaN,则调用toString()方法后再应用前面的规则。对象变量变成数值变量。
3、 加法运算操作符
加号运算操作符在Javascript也用于字符串连接符,所以加号操作符的规则分两种情况:
如果两个操作值都是数值,其规则为:
(1) 如果一个操作数为NaN,则结果为NaN
(2) 如果是Infinity+Infinity,结果是Infinity
(3) 如果是-Infinity+(-Infinity),结果是-Infinity
(4) 如果是Infinity+(-Infinity),结果是NaN
(5) 如果是+0+(+0),结果为+0
(6) 如果是(-0)+(-0),结果为-0
(7) 如果是(+0)+(-0),结果为+0
如果有一个操作值为字符串,则:
(1) 如果两个操作值都是字符串,则将它们拼接起来
(2) 如果只有一个操作值为字符串,则将另外操作值转换为字符串,然后拼接起来
(3) 如果一个操作数是对象、数值或者布尔值,则调用toString()方法取得字符串值,然后再应用前面的字符串规则。对于undefined和null,分别调用String()显式转换为字符串。
可以看出,加法运算中,如果有一个操作值为字符串类型,则将另一个操作值转换为字符串,最后连接起来。
补充:
加号作为一元运算符时,其后面的表达式将进行ToNumber操作,示例如下:
+true // 1
+false // 0
+undefined // NaN
+null // 0
+'b' // NaN
+'0x10' // 16
+{valueOf: ()=> 5} // 5
"a" + + "b" // 输出结果 "aNaN"
// JS中的一元加的优先级高于2元加(减法也类似,一元减优先级高于二元减)
// 所以上述表达式可以等价于 "a" + ( +"b")
// "a" + NaN
// "aNaN"
4、 乘除、减号运算符、取模运算符
这些操作符针对的是运算,所以他们具有共同性:如果操作值之一不是数值,则被隐式调用Number()函数进行转换。具体每一种运算的详细规则请参考ECMAScript中的定义。
5、 逻辑操作符(!、&&、||)
逻辑非(!)操作符首先通过Boolean()函数将它的操作值转换为布尔值,然后求反。
逻辑与(&&)操作符,如果一个操作值不是布尔值时,遵循以下规则进行转换:
(1)如果第一个操作数经Boolean()转换后为true,则返回第二个操作值,否则返回第一个值(不是Boolean()转换后的值)
(2)如果有一个操作值为null,返回null
(3)如果有一个操作值为NaN,返回NaN
(4)如果有一个操作值为undefined,返回undefined
逻辑或(||)操作符,如果一个操作值不是布尔值,遵循以下规则:
(1)如果第一个操作值经Boolean()转换后为false,则返回第二个操作值,否则返回第一个操作值(不是Boolean()转换后的值)(2)对于undefined、null和NaN的处理规则与逻辑与(&&)相同
6、 关系操作符(<, >, <=, >=)
与上述操作符一样,关系操作符的操作值也可以是任意类型的,所以使用非数值类型参与比较时也需要系统进行隐式类型转换:
(1)如果两个操作值都是数值,则进行数值比较
(2)如果两个操作值都是字符串,则比较字符串对应的字符编码值
(3)如果只有一个操作值是数值,则将另一个操作值转换为数值,进行数值比较
(4)如果一个操作数是对象或者数组,则调用valueOf()方法(如果是数组则调用toString()方法),得到的结果按照前面的规则执行比较
(5)如果一个操作值是布尔值,则将其转换为数值,再进行比较
注:NaN是非常特殊的值,它不和任何类型的值相等,包括它自己,同时它与任何类型的值比较大小时都返回false。
补充:
null / undefined 进行数学式或者关系操作符时,null会被转化为0, undefined会被转化为NaN。
奇怪的现象:null vs 0
null > 0 // false
null == 0 // false
null >= 0 // true
按理说,在最后一行代码显示“null 大于等于0”的前提下,前两句代码应该一定会有一个是正确的,然后事实却是结果都为false。
为什么会出现这种反常结果,这是因为相等性检测 == 和普通比较符 > < >= <= 的代码逻辑是相互独立的。进行值的比较时,null 会被转化为数字,因此它被转化为了 0。这就是为什么(3)中 null >= 0 返回值是 true,(1)中 null > 0 返回值是 false。
另一方面,undefined 和 null 在相等性检测 == 中不会进行任何的类型转换,它们有自己独立的比较规则,所以除了它们之间互等外,不会等于任何其他的值。这就解释了为什么(2)中 null == 0 会返回 false。
特立独行的undefined:undefined不应该被与其他值进行比较
undefined > 0 // false
undefined < 0 // false
undefined == 0 // false
(1)和(2)都返回false,是因为值比较时,undefined 被转化为NaN,而NaN是一个特殊的值,它与任何值相比返回都是false。而(3)返回false是因为这是一个相等性检测,而 undefined 除了与 null 值相等外,与其他值都不等。
7、 相等操作符(==)
相等操作符会对操作值进行隐式转换后进行比较:
(1)如果一个操作值为布尔值,则在比较之前先将其转换为数值
(2)如果一个操作值为字符串,另一个操作值为数值,则通过Number()函数将字符串转换为数值
(3)如果一个操作值是对象,另一个不是,则调用对象的valueOf()方法,得到的结果按照前面的规则进行比较
(4)null与undefined是相等的
(5)如果一个操作值为NaN,则相等比较返回false
(6)如果两个操作值都是对象,则比较它们是不是指向同一个对象,如 [] != [] // true
8、全等操作符(===)
全等操作符不会对两个操作数进行转换,直接进行比较,所以在比较上更加严格。日常中使用全等操作符几乎总是正确的选择。对于除了数值之外的值,全等操作符使用明确的语义进行比较:一个值只与自身全等。对于数值,全等操作符使用略加修改的语义来处理两个特殊情况:第一个情况是,浮点数 0 是不分正负的。区分 +0 和 -0 在解决一些特定的数学问题时是必要的,但是大部分情况下我们并不用关心。全等操作符认为这两个值是全等的。第二个情况是,浮点数包含了 NaN 值,用来表示某些定义不明确的数学问题的解,例如:正无穷加负无穷。全等操作符认为 NaN 与其他任何值都不全等,包括它自己。(等式 (x !== x) 成立的唯一情况是 x 的值为 NaN)
补充:
是否能实现(a==1 && a==2 && a==3) 或者(a===1 && a===2 && a===3) 为true?
(1)在==比较的情况下,会进行类型的隐式转换。前面已经说过,参数会进行类型转换,先valueOf再obj.toString() 所以,我们只要改变原生的valueOf或者tostring方法就可以达到效果:
// 每一次进行等号的比较,就会调用一次valueOf方法,自增1,所以能成立。当然,如果换个位置就不满足了。
var a = {
num: 0,
valueOf: function() {
return this.num += 1
}
};
var eq = (a==1 && a==2 && a==3);
console.log(eq); // true
(2)在===的情况下没有类型转换,还是可以的。在vue源码实现双向数据绑定中,就利用了defineProperty方法进行观察,观察到数据的变化并实时反映到视图层。每一次访问对象中的某一个属性的时候,就会调用这个方法定义的对象里面的get方法。每一次改变对象属性的值,就会访问set方法。
var b = 1;
Object.defineProperty(window, 'a', {
get: funtion() {
return b++;
}
})
var s = (a===1 && a===2 && a === 3 )
console.log(s) // true;