【前端进阶】JS中的数据类型
基本数据类型(值类型,原始数据类型)
number
let num1 = 10; // 整型
let num2 = 10.00; // 浮点型
let num3 = NaN; // 非数字值的特殊值
let num4 = Infinity; // 无穷大
string
let str1 = 'str'; // 单引号包裹
let str2 = "str"; // 双引号包裹
let str3 = `str`; // 模板字符串
boolean
let boo1 = true; // 正确
let boo2 = false; // 错误
null
let nul = null; // 对象的值未设置
undefined
typeof undefined // undefined不是常量,而是指变量未定义
symbol
let big= Symbol(1); // 唯一值,可作为对象属性的标识符
bigint
let big= 10n; // 大数
引用数据类型
object
let obj1= new Object() || {}; // 普通对象
let obj2= new Array() || []; // 数组对象
let obj3= new RegExp('^obj$') || /^obj$/; // 正则对象
let obj4= new Date(); // 日期对象
let obj5= new Set(); // set对象
let obj5= new Map(); // map对象
...
function
let fn1= function(){}; // 普通函数/构造函数
let fn2= ()=>{}; // 箭头函数
let fn3 = function*(){}; // 生成器函数
...
数据类型检测
为了方便,以下所有需打印的代码省略了console.log()
typeof
按照计算机底层储存的二进制结果进行检测,对象都以000开始的,null的二进制储存结果为000000
- 检测数据类型的运算符
- 返回类型是字符串,字符串中包含对应的数据类型
- 所有对象基于typeof的检测都是"object",因此typeof无法检测对象的具体细分
// 检测数据类型的运算符
typeof 10 // "number"
typeof 'str' // "string"
typeof true // "boolean"
typeof null // "object"=>null的二进制储存结果为000000
typeof undefined // "undefined"
typeof Symbol(1) // "symbol"
typeof 10n // "bignit"
typeof function(){} // "function"
// 返回类型是字符串
typeof typeof xxx // "string"
// 所有对象基于typeof的检测都是"object",因此typeof无法检测对象的具体细分
typeof new Object() // "object"
typeof new Array() // "object"
typeof new RegExp('^obj$') // "object"
typeof new Date() // "object"
typeof new Set() // "object"
typeof new Map() // "object"
...
instanceof
用来检测当前实例是否属于这个类
- 一般用来检测对象的具体细分
- 无法检测普通对象
- 无法检测原始数据类型
- 检测时,浏览器会调用Function原型上的Symbol.hasInstance()方法
// 无法检测普通对象
let arr = [];
arr instanceof Array; // true
arr instanceof RegExp; // false
arr instanceof Object; // true
// 无法检测原始数据类型
let n = 10,// 原始数据类型
m = new Number(10); // 引用数据类型
n instanceof Number; // false
m instanceof Number; // true
// 手动改变原型指向导致检测不正确
function Person(){}
Person.prototype = Array.prototype;
let p1 = new Person;
p1 instanceof Array // true
// 检测时,浏览器会调用Function原型上的Symbol.hasInstance()方法
// 原理:检测当前实例的原型链(__proto__)上是否存在这个类的原型(prototype)
arr instanceof Array // true
=>Array[Symbol.hasInstance](arr) // true
constructor
用来获取当前实例的构造函数
- 比instanceof好用一点
- 可以检测原始数据类型
- constructor可以随意修改,所以也不准确
// 比instanceof好用一点
let arr = [];
arr.constructor === Array; // true
arr.constructor === RegExp; // false
arr.constructor === Object; // false
// 可以检测原始数据类型
let n = 10,// 原始数据类型
m = new Number(10); // 引用数据类型
n.constructor === Number; // true
m.constructor === Number; // true
// constructor可以随意修改,所以也不准确
function Person(){}
Person.prototype.constructor = Array;
let p1 = new Person;
p1.constructor === Array // true
Object.prototype.toString.call()
专门用来检测数据类型
- Number/String/Boolean/Symbol/Bigint/Function/Array/RegExp/Date…上原型都有toString方法,用来转化为字符串
- Object原型上的toString专门用来检测数据类型
- 返回结果:"[obejct 对象[Symbol.toStringTag] || 对象,构造函数 (不受自己更改的影响)|| Object]"
// Number/String/Boolean/Symbol/Bigint/Function/Array/RegExp/Date...上原型都有toString方法,用来转化为字符串
(10).toString() // "10"
"str".toString() // "str"
true.toString() // "true"
Symbol(1).toString() // "Symbol(1)"
10n.toString() // "10"
(function(){}).toString() // "function(){}"
[1,2].toString() // "1,2"
/^test$/.toString() // "/^test$/"
new Date().toString() // "Sat Oct 17 2020 14:58:09 GMT+0800 (中国标准时间)"
...
// Object原型上的toString专门用来检测数据类型
var toString = Object.prototype.toString;
toString.call(10) // "[object Number]"
toString.call('str') // "[object String]"
toString.call(true) // "[object Boolean]"
toString.call(null) // "[object Null]"
toString.call(undefined) // "[object Undefined]"
toString.call(Symbol(1)) // "[object Symbol]"
toString.call(10n) // "[object BigInt]"
toString.call(new Object()) // "[object Object]"
toString.call(new Array()) // "[object Array]"
toString.call(new Date()) // "[object Date]"
toString.call(new RegExp()) // "[object RegExp]"
toString.call(new Set()) // "[object Set]"
toString.call(new Map()) // "[object Map]"
toString.call(function(){}) // "[object Function]"
toString.call(function*(){}) // "[object GeneratorFunction]"
...
重写instanceof
// obj:要检测的实例,不能为原始类型
// ctor:要检测的类,必须为函数
function instanceOf(obj,ctor){
// 如果obj为null或者obj不是对象或方法则返回false
if(typeof obj === null || !/^(object|function)$/i.test(typeof obj))return false;
// 如果ctor不是一个类,则报错
if(typeof ctor !== 'function') throw new TypeError('Right-hand side of "instanceof" is not an object');
// Object.getPrototypeOf(obj)===obj.__proto__
let proto = Object.getPrototypeOf(obj),
prototype = ctor.prototype ;
// 在该实例的原型链当中查找此类的原型
while(true){
// 如果一直找到Object.prototype.__proto__,则该实例不属于此类
if(proto === null ) return false;
// 该实例原型链中可以找到此类的原型,则该实例属于此类
if( proto === prototype ) return true;
// 往上逐级查找
proto = Object.getPrototypeOf(proto);
}
}
JQ中关于数据类型检测的源码封装
const class2type = {},
toString = class2type.toString, // Object.prototype.toString 用于检测数据类型
hasOwn = class2type.hasOwnProperty, // Object.prototype.hasOwnProperty 用于检测私有属性
fnToString = hasOwn.toString, // Function.prototype.toString 用于转换为字符串
ObjectFunctionString = fnToString.call(Object), // "function Object() { [native code] }"
getproto = Object.getprototypeof; // 用于获取原型链
// 类型映射表
const typeList = 'Number String Boolean Symbol Bigint Function Object Date RegExp Array Error'.split(' ').forEach((item) => {
class2type[`[object ${item}]`] = item.toLowerCase();
})
//公共判断类型方法
const toType = function toType(obj) {
// 如果为null/undefined 则返回"null"/"undefined"
if (obj == null) {
return obj + '';
}
// 如果obj为方法或者对象,则以toString.call(obj)为key在类型映射表中查找对应value,有则返回value,没有则返回'object'。
// 否则用typeof判断obj的数据类型
return typeof obj === 'function' || typeof obj === 'object' ? class2type[toString.call(obj)] || 'object' : typeof obj
}
// 是否是函数
const isFunction = function isFunction(obj) {
return typeof obj === 'function' && typeof obj.nodeType !== 'number';
}
// 是否是window
const isWindow = function isWindow(obj) {
return obj != null && obj === obj.window;
}
// 是否为数组或类数组
const isArrayLike = function isArrayLike(obj) {
const length = !!obj && 'length' in obj && obj.length,
type = toType(obj);
if (isFunction(obj) || isWindow(obj)) {
return false;
}
return type === 'array' || length === 0 || typeof length === 'number' && length > 0 && (length - 1) in obj;
}
// 是否为纯对象
const isPlainObject = function isPlainObject(obj) {
let proto, ctor, type = toType(obj);
if (!obj || type !== 'object') {
return false;
}
proto = getproto(obj)
if (!proto) {
return true
}
ctor = hasOwn.call(proto, 'constructor') && proto.constructor;
return typeof ctor === 'function' && fnToString.call(ctor) === ObjectFunctionString;
}
// 是否为空对象
const isEmptybject = function isEmptybject(obj) {
const keys = [
...Object.getOwnPropertyNames(obj),
...Object.getOwnPropertySymbols(obj)
]
return keys.length === 0
}