前置知识
javascript类型
- 值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol。
- 引用数据类型(对象类型):对象(Object)、数组(Array)、函数(Function),还有两个特殊的对象:正则(RegExp)和日期(Date)。
类型判断的方法
掌握类型判断:typeof、instanceof、Object.prototype.toString.call、constructor
typeof
typeof 运算符返回一个字符串,表示操作数的类型。
| 类型 | 结果 |
|---|---|
| String | "string" |
| Number | "number" |
| Boolean | "boolean" |
| Undefined | "undefined" |
| Symbol | "symbol" |
| BigInt | "bigint" |
| Null | "null" |
| Function | "function" |
| Object | "object" |
| Array | "object" |
| Date | "object" |
| RegExp | "object" |
示例
typeof 37 === 'number';
typeof 3.14 === 'number';
typeof(42) === 'number';
typeof Math.LN2 === 'number';
typeof Infinity === 'number';
typeof NaN === 'number'; // 尽管它是 "Not-A-Number" (非数值) 的缩写
typeof Number(1) === 'number'; // Number 会尝试把参数解析成数值
typeof Number("shoe") === 'number'; // 包括不能将类型强制转换为数字的值
typeof 42n === 'bigint';
// 字符串
typeof '' === 'string';
typeof 'bla' === 'string';
typeof `template literal` === 'string';
typeof '1' === 'string'; // 注意内容为数字的字符串仍是字符串
typeof (typeof 1) === 'string'; // typeof 总是返回一个字符串
typeof String(1) === 'string'; // String 将任意值转换为字符串,比 toString 更安全
// 布尔值
typeof true === 'boolean';
typeof false === 'boolean';
typeof Boolean(1) === 'boolean'; // Boolean() 会基于参数是真值还是虚值进行转换
typeof !!(1) === 'boolean'; // 两次调用 !(逻辑非)运算符相当于 Boolean()
// Symbols
typeof Symbol() === 'symbol';
typeof Symbol('foo') === 'symbol';
typeof Symbol.iterator === 'symbol';
// Undefined
typeof undefined === 'undefined';
typeof declaredButUndefinedVariable === 'undefined';
typeof undeclaredVariable === 'undefined';
// 对象
typeof { a: 1 } === 'object';
// 使用 Array.isArray 或者 Object.prototype.toString.call
// 区分数组和普通对象
typeof [1, 2, 4] === 'object';
typeof new Date() === 'object';
typeof /regex/ === 'object';
// 下面的例子令人迷惑,非常危险,没有用处。避免使用它们。
typeof new Boolean(true) === 'object';
typeof new Number(1) === 'object';
typeof new String('abc') === 'object';
// 函数
typeof function() {} === 'function';
typeof class C {} === 'function'
typeof Math.sin === 'function';
//Null
// JavaScript 诞生以来便如此
typeof null === "object";
总结 注意
- typeof对基本类型可以做出准确判断,除了Null
- 所有使用new调用的构造函数都将返回非基本类型(
"object"或"function")
const num = new Number(100);
typeof str; // "object"
typeof num; // "object"
const func = new Function();
typeof func; // "function"
- 未声明的标识符,
typeof也会返回"undefined",而不是抛出错误。
typeof undeclaredVariable; // "undefined"
instanceof
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
示例
console.log({} instanceof Object);//true
console.log(function(){} instanceof Function);//true
// 如果后面是Object的话,那么都返回true,因为Object是它们的祖师爷
console.log([] instanceof Object);//true
console.log({} instanceof Object);//true
console.log(function(){} instanceof Object);//true
var a =1;
console.log(a instanceof Number);//false
console.log(a.constructor === Number);//true
// 这里有个坑!其实 a是Number的实例,Number的原型也在a的原型链上!为什么
// a instanceof Number结果是false? 其实是 object instranceof constructor ,
// 注意看!是object,如果不是object全部返false,
// 那么问题来了?为什么实例出来的对象变成了基本类型?
// 很懵!!! 其实当我们字面量创建变量时候,后台就会创建一个对应的基本包装类型的对象,
// 下面是它的创建过程!
// 1.创建String的实例
// 2.在实例上调用指定的方法
// 3.销毁这个实例
var a = "1234"
//等价于
var a = new Number("1234");
var copyA = a.substr(0);
a = null;
// 这个过程是非常短暂的,这个也是引用类型和包装类型的最大区别,也就是对象的生命周期,
// 在运行的一瞬间就被销毁了,这个也是为什么在运行的时候在包装对象上添加不了属性和方法;
var a = "1234"
a.add = "add";
console.log(a.add);//underfined
总结
- instanceof 可以判断引用类型,但是判断不了基本类型
- instanceof a instranceof b,意思判断 b的原型对象是否在a的原型链上"object instranceof constructor"
Object.prototype.toString
toString() 方法返回一个表示该对象的字符串。该方法旨在重写(自定义)派生类对象的[类型转换]的逻辑。
// 能够比较精准的判断各种类型,原理是借用Object原型上的toString()方法!
var test = Object.prototype.toString;
console.log(test.call(1));//[object Number]
console.log(test.call("1"));//[object String]
console.log(test.call(true));//[object Boolean]
console.log(test.call(function(){}));//[object Function]
console.log(test.call(undefined));//[object Undefined]
console.log(test.call(null));//[object Null]
console.log(test.call([]));//[object Array]
constructor
构造函数属于被实例化的特定类对象 。构造函数初始化这个对象,并提供可以访问其私有信息的方法。构造函数的概念可以应用于大多数面向对象的编程语言。本质上,JavaScript 中的构造函数通常在[类]的实例中声明。
示例
// 每个实例对象都从原型中继承了一个 constructor 属性,该属性指向了用于构造此实例对象的构造函数。
console.log((1).constructor ===Number);//true
console.log(true.constructor === Boolean);//true
console.log("1".constructor === String);//true
console.log([].constructor === Array);//true
console.log(function(){}.constructor === Function);//true
console.log({}.constructor === Object);//true
// 有一点需要注意,当直接给原型赋值后,那么constructor指向就会改变!
function A(){};
var p =new A();
console.log(p.constructor===A);//true
A.prototype = [];
var p1 =new A();
console.log(p1.constructor===A);//false
console.log(p1.constructor===Array);//true
总结
- 每个实例对象都从原型中继承了一个 constructor 属性,该属性指向了用于构造此实例对象的构造函数。
- 有一点需要注意,当直接给原型赋值后,那么constructor指向就会改变!
手写自己的方法
由上述可见这些方法大都有自己的弊端,那么我们为了更好的实现类型判断,借助相对完善的toString方式下面我们就来实现一个自己的方法
function myTypeof(obj){
if(obj===null) return "null"
if(typeof obj !== "object" ){
return typeof obj
} else{
return Object.prototype.toString.call(obj).slice(8, -1).toLocaleLowerCase()
}
}