js数据类型
- 基本类型(原始值):Undefined Null String Number Boolean Symbol
- 引用类型(对象值):object (数组Array; 函数Function; 正则RegEXP; 日期Date)
来源
- +:运算符既可以是数字相加,也可以是字符串相加
- ==:==不同于===有多不同情况的隐式转换(以前我看一本书书尽量还是多用==,多拥抱js的隐式转换少用===,但是当我用了===,我就回不去了)
- - * / : 这一些运算符就比较友好了,他们只针对number类型,因而转化的结果只能转换成number类型
转化的方法
- 将值转为原始值,ToPrimitive()
http://www.ecma-international.org/ecma-262/#sec-toprimitive 7.1.1 中有介绍
- 将值转为数字,ToNumber()
- 将值转为字符串,ToString()
通过ToNumber将值转为数字
参数 | 结果 |
undefined | NaN |
unll | +0 |
Boolean | true转换为1; false转换为0 |
Number | 无需转换 |
String | 由js中的内置对象Number来处理 |
Object | 1.先进行 ToPrimitive(obj, Number)转换得到原始值, 2.在进行ToNumber转换为数字 |
通过ToString将值转换为字符串
参数 | 结果 |
undefined | 'undefined' // '1' + undefined = "1undefined" |
null | 'null' |
Boolean | 'true'或'false' |
Number | 数字转字符串 // (123).toString() => '123' |
String | 字符串无需转换 |
Object | 1.先进行 ToPrimitive(obj, String)转换得到原始值, 2.在进行ToString转换为字符串 |
通过ToPrimitive将值转换为原始值
ToPrimitive(input, PreferredType?)
input:是要转换的值
PreferredType:(默认值为Number)是可选参数,可以是Number或String类型。他只是一个转换标志,转化后的结果并不一定是这个参数所值的类型,但是转换结果一定是一个原始值(或者报错)。
当标志为Number的时候,ToPrimitive方法的操作流程为
- 如果输入的值已经是一个原始值,则直接返回它
- 否则,如果输入的值是一个对象,则调用该对象的valueOf()方法,如果valueOf()方法的返回值是一个原始值,则返回这个原始值。 // valueOf 方法下面会说
- 否则,调用这个对象的toString()方法,如果toString()方法返回的是一个原始值,则返回这个原始值。
- 否则,抛出TypeError异常。
当标志为String的时候,ToPrimitive方法的操作流程为
- 如果输入的值已经是一个原始值,则直接返回它
- 否则,调用这个对象的toString()方法,如果toString()方法返回的是一个原始值,则返回这个原始值。
- 否则,如果输入的值是一个对象,则调用该对象的valueOf()方法,如果valueOf()方法的返回值是一个原始值,则返回这个原始值。
- 否则,抛出TypeError异常。
valueOf方法和toSting方法解析
valueOf
对转化Number,Boolear,String这三种构造函数生成的基础值的对象形式,通过valueOf转化后变成相应原始值
运行 | 结果 |
var num = new Number('123') num.valueOf() | 123 |
var str = new String('12df') str.valueOf() | '12df' |
var bool = new Boolean('fd') bool.valueOf(); | true |
var a = new Date(); a.valueOf(); | // 1515143895500 也是除了 w Date().getTime(); 和 Date.parse( new Date()); 另一种获取时间戳的方法 |
var a = new Array(); a.valueOf() === a; | // true |
var b = new Object({}); b.valueOf() === b; | // true |
toString
用Number,Boolean,String,Array,Date,RegExp,Function这几种构造函数生成的对象,通过toString转化后变成相应的字符串形式,因为这一些构造函数都封装了自己的toString方法
Number.prototype.hasOwnProperty('toString'); // true
Boolean.prototype.hasOwnProperty('toString'); // true
String.prototype.hasOwnProperty('toString'); // true
Array.prototype.hasOwnProperty('toString'); // true
Date.prototype.hasOwnProperty('toString'); // true
RegExp.prototype.hasOwnProperty('toString'); // true
Function.prototype.hasOwnProperty('toString'); // true
var num = new Number('123sd'); // Number {NaN} num.toString(); | 'NaN' |
var str = new String('12df'); str.toString(); | '12df' |
var bool = new Boolean('fd'); bool.toString(); | 'true' |
var arr = new Array(1,2); arr.toString(); | '1,2' |
var d = new Date(); d.toString(); | "Wed Oct 11 2017 08:00:00 GMT+0800 (中国标准时间)" |
var func = function () {} func.toString(); | "function () {}" |
除了以上的对象啊和实例化对象,其他的对象返回的都是改对象的类型,都是基础Object.prototype.toString方法
var obj = new Object({});
obj.toString(); // "[object Object]
Math.toString(); // "[object Math]"
判断数据类型
export const getType = x => /^\[object (.*)\]$/.exec(Object.prototype.toString.call(x))[1]
区别
valueOf函数会将
- Number,String,Boolean基本类型转换成相对于的类型,
- Data类型转换为毫秒数,
- 其他返回对象本身
toString方法会将
- 所有对象转为字符串
ToPrimitive默认参数的原因
因为valueOf函数会将Number、String、Boolean基础类型的对象类型值转换成 基础类型,Date类型转换为毫秒数,其它的返回对象本身,而toString方法会将所有对象转换为字符串。显然对于大部分对象转换,valueOf转换更合理些,因为并没有规定转换类型,应该尽可能保持原有值,而不应该想toString方法一样,一股脑将其转换为字符串。
例子
({} + {}) = ?
两个对象的值进行+运算符,肯定要先进行隐式转换为原始类型才能进行计算。
- 进行ToPrimitive转换,由于没有指定PreferredType类型,{}会使默认值为Number,进行ToPrimitive(input, Number)运算。
- 所以会执行valueOf方法,({}).valueOf(),返回的还是{}对象,不是原始值。
- 继续执行toString方法,({}).toString(),返回"[object Object]",是原始值。
故得到最终的结果,"[object Object]" + "[object Object]" = "[object Object][object Object]"
2 * {} = ?
- 1、首先*运算符只能对number类型进行运算,故第一步就是对{}进行ToNumber类型转换。
- 2、由于{}是对象类型,故先进行原始类型转换,ToPrimitive(input, Number)运算。
- 3、所以会执行valueOf方法,({}).valueOf(),返回的还是{}对象,不是原始值。
- 4、继续执行toString方法,({}).toString(),返回"[object Object]",是原始值。
- 5、转换为原始值后再进行ToNumber运算,如上显示,字符串进行Number()所以得:"[object Object]"就转换为NaN。
故最终的结果为 2 * NaN = NaN
{} + [] // 0
[] + {} // '[object Object]'
1.如果{}
(空对象)在前面,而[]
(空数组)在后面时,前面(左边)那个运算元会被认为是区块语句而不是对象字面量。所以{} + []
相当于+[]
语句,也就是相当于强制求出数字值的Number([])
运算,相当于Number("")
运算,最后得出的是0
数字 进行ToNumber。
- [].valueOf() // 返回数组自身,不是原始数据
- 调用 [].toString() // 返回空字符串
- Number("") // 空字符串转型为数字,返回0
大家肯定会注意为什么这里是用Number(''),由于JS中如果+运算符由二元运算符变成一元运算符就会将哪个操作符变成Number,MDN
2. [] + {} ===> ToNumber('' + '[object Object]') === ''[object Object]''
注意
在 ES6 之后,还允许对象通过显式指定 @@toPrimitive Symbol 来覆盖原有的行为。
var o = {
valueOf : () => {console.log("valueOf"); return {}},
toString : () => {console.log("toString"); return {}}
}
o[Symbol.toPrimitive] = () => {console.log("toPrimitive"); return "hello"}
console.log(o + "") // toPrimitive // hello
注意:
就算以知 +号一边是字符串,那么就是字符串运算符,而不是说一定会进行ToString转换而还是进行默认ToNumber转换,只有使用字符串模板才是进行ToString转换
// An object without Symbol.toPrimitive property.
var obj1 = {};
console.log(+obj1); // NaN
console.log(`${obj1}`); // "[object Object]"
console.log(obj1 + ''); // "[object Object]"
// An object with Symbol.toPrimitive property.
var obj2 = {
[Symbol.toPrimitive](hint) {
if (hint == 'number') {
return 10;
}
if (hint == 'string') {
return 'hello';
}
return true;
}
};
console.log(+obj2); // 10 -- hint is "number"
console.log(`${obj2}`); // "hello" -- hint is "string"
console.log(obj2 + ''); // "true" -- hint is "default"