一、内置类型
JS 中有 8 种内置类型,⼜分为两⼤类型: 7 种【基本类型】 和 【对象或复杂类型】:
- 基本类型: null , undefined , boolean , number , string , symbol , bigInt .
- 对象或复杂类型: array , object , function .
- PS: undefined 派生自 null 但它们的意义是不一样的. undefined 指定义了变量却未初始化. null 指定义了当前变量且这个变量未来是用于存储 object 类型的值.
注意:对于【基本类型】来说,如果使⽤字⾯量的⽅式,那么这个变量只是个字⾯量,只有在必要的时候才会转换为对应的类型.
let num = 666; num.toString(); // 虽然 num 属于基本类型,但是它可以调用方法,并且不会报错 // 上面相当于 // 1. 字面量声明 let num = 666; // 2. 转为对应构造函数的实例, 并调用方法, 调用结束后,直接删除生成的这个实例 new Number(num).toString(); 复制代码
二、类型判断 — typeof & instanceof & Object.prototype.toString.call(obj)
typeof
直接看判断结果
// 基本类型 console.log(typeof null); // object console.log(typeof undefined); // undefined console.log(typeof true); // boolean console.log(typeof '666'); // string console.log(typeof 1); // number console.log(typeof 1n); // bigint console.log(typeof Symbol()); // symbol // 复杂类型 console.log(typeof []); // object console.log(typeof {}); // object console.log(typeof function add(){}); // function 复制代码
从以上判断结果可以看出来:
- 基本类型中除了对 null 的判断错误之外,其他的都是正常的.
- PS: 【typeof null 为 object,是因为最初的 js 版本使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,其中 000 开头的代表的是对象,然而 null 是用全 0 表示,所以将它错误的判断为 object 】
- 复杂类型中除了对 function 和 {} 类型判断正确之外,其他的都是错误的.
instanceof
用于判断一个变量是否属于某个对象的实例,既然是实例意味着该变量是通过 new 操作得到的.
var bool = new Boolean(true); var str = new String('666'); var num = new Number(1); var date = new Date(); var reg = new RegExp(/.+/); var arr = new Array(); var obj = new Object({}); var add = function () {} console.log(bool instanceof Boolean); // true console.log(str instanceof String); // true console.log(num instanceof Number); // true console.log(date instanceof Date); // true console.log(reg instanceof RegExp); // true console.log(arr instanceof Array); // true console.log([] instanceof Array); // true console.log(obj instanceof Object); // true console.log({} instanceof Object); // true console.log(add instanceof Function); // true // 自定义类 class Person{ constructor(name){ this.name = name; } } var person = new Person('zs'); console.log(person instanceof Person); // true 复制代码
- 注意:null、undefined、symbol、bigint 没有自己专属的构造函数,因此不可以进行 new 操作,所以不适用于 instanceof 判断
- 作用:
- 判断某个实例是否属于某构造函数
- 在继承关系中用来判断一个实例是否属于它的父类型或者祖先类型的实例
- 如:left instanceof right ,只要 right.prototype 存在于 left.__proto__ 的原型链上,则会返回 true , 否则返回 false .
- 缺点:
- 并不适用于所有的类型
- 由于它依赖于构造函数去判断,所以当某个实例的构造函数指向被强行修改,那么判断将出现错误
Object.prototype.toString.call(obj)
使用以上方式可以很好的区分各种类型:(无法区分自定义对象类型,自定义类型可以采用 instanceof 区分)
console.log(Object.prototype.toString.call("str")); //[object String] console.log(Object.prototype.toString.call(1)); //[object Number] console.log(Object.prototype.toString.call(true)); //[object Boolean] console.log(Object.prototype.toString.call(undefined)); //[object Undefined] console.log(Object.prototype.toString.call(null)); //[object Null] console.log(Object.prototype.toString.call({name: "zs"})); //[object Object] console.log(Object.prototype.toString.call(function(){})); //[object Function] console.log(Object.prototype.toString.call([])); //[object Array] console.log(Object.prototype.toString.call(new Date)); //[object Date] console.log(Object.prototype.toString.call(/\d/)); //[object RegExp] function Person(){}; console.log(Object.prototype.toString.call(new Person)); //[object Object] 复制代码
- 为什么不直接使用 obj.toString() ?
- 因为 toString 是 Object.prototype 上的方法,而 Array、Function 等类型虽然作为 Object 的实例,但它们都重写了 toString 方法,因此 obj.toString() 直接调用只能得到重写后的值.
console.log(({}).toString()); // [object Object] console.log("zs".toString()); // zs console.log((1).toString()); // 1 console.log([1,2].toString()); // 1,2 console.log(new Date().toString()); // Thu Oct 14 2021 22:25:11 GMT+0800 (中国标准时间) console.log(function(){}.toString()); // function (){} console.log(null.toString()); // error console.log(undefined.toString()); // error 复制代码
- 如果删除了被重写的 toString 后,可以发现结果和 Object.prototype.toString.call(obj) 是一致的
var arr = [1, 2, 3]; var func = function (){}; // 调用重写的 toString console.log(Array.prototype.hasOwnProperty("toString")); //true console.log(Function.prototype.hasOwnProperty("toString")); //true console.log(arr.toString()); // '1,2,3' console.log(func.toString()); // 'function (){}' // delete 操作符删除实例属性 delete Array.prototype.toString; delete Function.prototype.toString; // 本质上是调用 Object.prototype.toString console.log(Array.prototype.hasOwnProperty("toString")); //false console.log(Function.prototype.hasOwnProperty("toString")); //false console.log(arr.toString()); // "[object Array]" console.log(func.toString()); // "[object Function]" 复制代码
三、类型转换
转 Boolean
在条件判断时,除了undefined ,null , false , NaN , '' , 0 , -0 ,其他所有值都转为 true ,包括所有对象。
对象转基本类型
- 对象在转换基本类型时,⾸先会调⽤ valueOf ,然后调⽤ toString 。并且这两个⽅法是可以被重写的。
- 当然你也可以重写 Symbol.toPrimitive ,该⽅法在转基本类型时调⽤优先级最⾼。
四则运算符(+、-、*、/)
- 只有当加法运算时,其中⼀⽅是字符串类型,就会把另⼀个也转为字符串类型。
- 其他运算只要其中⼀⽅是数字,那么另⼀⽅就转为数字。
- 并且加法运算会触发三种类型转换:将值转换为原始值,转换为数字,转换为字符串。
1 + '1' // '11' 2 * '2' // 4 [1, 2] + [2, 1] // '1,22,1' // [1, 2].toString() -> '1,2' // [2, 1].toString() -> '2,1' // '1,2' + '2,1' = '1,22,1' 复制代码
- 对于加号需要注意这个表达式 'a' + + 'b'
'a' + + 'b' // -> "aNaN" // 因为 + 'b' -> NaN // 你也许在⼀些代码中看到过 + '1' -> 1 复制代码
== 操作符
例子:x == y
- x,y 为 number || string => 先把 string 转 number ,后进行比较
- x,y 为 number || boolean => 先把 boolean 转 number ,后进行比较
- 任何与 NaN 进行的比较都返回 false
- x,y 为 string || boolean => 先把 string 和 boolean 转 number ,后进行比较
- x,y 为 undefined || null => 返回 true
- x,y 为 number => -0 == +0 返回 true ,其他正负不相等
- x,y 为 (number || string) || object => 先把 object 转基本类型,后进行对比
- object => [Symbol.toPrimitive] () || valueOf() || toString()
- x,y 为 boolean || object => 先把 boolean 转 number ,按上述规则 5 比较
> || < || =< || => 比较运算符
- x,y 为 object || other => object 转成 基本类型,然后比较
- x,y 为 string => 通过 unicode 字符索引来⽐较