1.typeof [value]:检测数据类型的运算符
1.1返回结果是一个字符串,其次字符串中包含对应的数据类型,例如:“number”、“object”、“function”..
console.log(typeof 2); // number
console.log(typeof true); // boolean
console.log(typeof 'str'); // string
console.log(typeof []); // object []数组的数据类型在 typeof 中被解释为 object
console.log(typeof function(){}); // function
console.log(typeof {}); // object
console.log(typeof undefined); // undefined
console.log(typeof null); // object null 的数据类型被 typeof 解释为 object
1.2 弊端
-
typeof null 的结果为 “object”
-
typeof 对象 -> 除函数对象被识别为“function”,其余的对象都是返回“object”
-
typeof 未被声明的变量 -> 不会抱错,而是“undefined”
除了这些以外,用typeof检测原始值类型「或者函数类型」还是非常的方便、准确的
1.3 检测原理
所有的数据类型值在计算机底层都是按照二进制来进行存储的,而typeof检测类型,也是按照二进制值来进行检测的「特点:性能好」;所有以 000 开始的,都是对象类型,再排查一下有没有实现call,实现的是函数对象,没实现的就是其余的对象...而null这个值存储的二进制都是零,所以也被识别为“object”!!
2.instanceof:检测当前实例是否属于这个类「临时拉来做数据类型检测」
2.1 用法
实例 instanceof 构造函数,是返回true,否则返回false
console.log(2 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log('str' instanceof String); // false
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object); // true
// console.log(undefined instanceof Undefined);
// console.log(null instanceof Null);
2.2 作用
instanceof可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型[实现了对typeof 的补充];
2.3 弊端
- 无法基于instanceof检测是否为标准普通对象「纯粹对象:直接是Object类的实例」;因为所有对象都是Object类的实例,基于“xxx instanceof Object” 检测的时候,返回结果都是true;
- 无法检测原始值类型的值;因为基于instanceof检测的时候,默认不会进行“装箱”操作!!
- 检测的结果不一定严谨;因为,原型链以及所属构造函数是谁,是可以用户自己去改变的!!
2.4 instaceof 调用的原理
- 首先调用 [Ctor]Symbol.hasInstance 这个函数,如果存在这个函数,则直接基于这个函数处理即可
- 当代浏览器基本都有,因为Symbol.hasInstance在Function.prototype中,每一个构造函数都是Function的实例,都可以调用Function.prototype[Symbol.hasInstance]这个方法
- 如果不存在这个函数,浏览器会按照当前[value]原型链一层层向上查找,直到找到Object.prototype为止;查看[Ctor].prototype是否出现在它的原型链中,如果出现了,则结果是true,说明[value]是[Ctor]的实例,反之则为false...
2.5 分析instanceof的优缺点和底层实现机制,并且重写instanceof
const instance_of = function instance_of(value, Ctor) {
// 保证Ctor的格式也是正确的
if (typeof Ctor !== "function") throw new TypeError('Right-hand side of instanceof is not callable');
if (!Ctor.prototype) throw new TypeError('Function has non-object prototype in instanceof check');
// 不支持原始值类型值的检测
if (value === null) return false;
if (!/^(object|function)$/.test(typeof value)) return false;
// 支持Symbol.hasInstance方法的直接按照这个处理
if (typeof Ctor[Symbol.hasInstance] === "function") return Ctor[Symbol.hasInstance](value);
// 不支持的则按照原型链一层层的查找即可 Object.getPrototypeOf(value):获取value所属构造函数的原型对象
let proto = Object.getPrototypeOf(value);
while (proto) {
// Ctor.prototype出现在了value的原型链上「value是Ctor的实例对象」:直接返回true & 结束查找
if (proto === Ctor.prototype) return true;
proto = Object.getPrototypeOf(proto);
}
return false;
};
3 constructor :获取当前实例所属的构造函数「临时可做数据检测」
3.1 用法
@1 [value].constructor 获取其构造函数,验证是否为我们想检测的类
例如:[value].constructor===Array
3.2 相比较于instanceof来讲
- 可以检测原始值类型的值「排除null/undefined」
- 检测是否为标准普通对象 [value].constructor===Object
- 和instanceof一样,检测的结果仅供参考「constructor这个值是可以被肆意修改的」
console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log((function() {}).constructor === Function); // true
console.log(({}).constructor === Object); // true
这里有一个坑,如果我创建一个对象,更改它的原型,
constructor就会变得不可靠了
function Fn(){};
Fn.prototype=new Array();
var f=new Fn();
console.log(f.constructor===Fn); // false
console.log(f.constructor===Array); // true
4 .**Object.prototype.toString.call()**专门检测数据类型的办法
Object.prototype.toString.call([value]):专门检测数据类型的办法
@1 调用Object.prototype.toString方法,让方法中的this指向检测的值,就是检测当前值的数据类型;
返回结果是一个字符串 “[object ?]”
例如:Object.prototype.toString.call(10) -> "[object Number]"
Object.prototype.toString.call(new Number(10)) -> "[object Number]"
它是所有检测数据类型的办法中,最强大、最稳定...的方式{除了写起来麻烦一些}
@2 返回结果是 “[object ?]”,“?” 会是啥呢?
+ 首先获取[value][Symbol.toStringTag]属性值,如果存在这个属性,则这个属性值是啥,“?”就是啥!
+ 如果没有这个属性,一般“?”是当前实例所属的构造函数!! Symbol.prototype & BigInt.prototype & Math & GeneratorFunction.prototype & Promise.prototype & Set.prototype & Map.prototype ... 这些类的原型上,都有Symbol.toStringTag这个属性!!
Object.prototype.toString这个方法是用来检测数据类型的,而且方法内部规定:方法中的this是谁,我们就检测谁的类型,所以我们基于call方法去改变this指向!!!
toString() 是 Object 的原型方法,调用该方法,可以统一返回格式为 “[object Xxx]” 的字符串,其中 Xxx 就是对象的类型。对于 Object 对象,直接调用 toString() 就能返回 [object Object];而对于其他对象,则需要通过 call 来调用,才能返回正确的类型信息。我们来看一下代码。
Object.prototype.toString({}) // "[object Object]"
Object.prototype.toString.call({}) // 同上结果,加上call也ok
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call('1') // "[object String]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(function(){}) // "[object Function]"
Object.prototype.toString.call(null) //"[object Null]"
Object.prototype.toString.call(undefined) //"[object Undefined]"
Object.prototype.toString.call(/123/g) //"[object RegExp]"
Object.prototype.toString.call(new Date()) //"[object Date]"
Object.prototype.toString.call([]) //"[object Array]"
Object.prototype.toString.call(document) //"[object HTMLDocument]"
Object.prototype.toString.call(window) //"[object Window]"
实现一个全局通用的数据类型判断方法,来加深你的理解,代码如下
function getType(obj){
let type = typeof obj;
if (type !== "object") { // 先进行typeof判断,如果是基础数据类型,直接返回
return type;
}
// 对于typeof返回结果是object的,再进行如下的判断,正则返回结果
return Object.prototype.toString.call(obj).replace(/^[object (\S+)]$/, '$1'); // 注意正则中间有个空格
}
/* 代码验证,需要注意大小写,哪些是typeof判断,哪些是toString判断?思考下 */
getType([]) // "Array" typeof []是object,因此toString返回
getType('123') // "string" typeof 直接返回
getType(window) // "Window" toString返回
getType(null) // "Null"首字母大写,typeof null是object,需toString来判断
getType(undefined) // "undefined" typeof 直接返回
getType() // "undefined" typeof 直接返回
getType(function(){}) // "function" typeof能判断,因此首字母小写
getType(/123/g) //"RegExp" toString返回
小结
-
typeof- 直接在计算机底层基于数据类型的值(二进制)进行检测
typeof null为object原因是对象存在在计算机中,都是以000开始的二进制存储,所以检测出来的结果是对象typeof普通对象/数组对象/正则对象/日期对象 都是objecttypeof NaN === 'number'
-
instanceof- 检测当前实例是否属于这个类的
- 底层机制:只要当前类出现在实例的原型上,结果都是true
- 不能检测基本数据类型
-
constructor- 支持基本类型
- constructor可以随便改,也不准
-
Object.prototype.toString.call([val])- 返回当前实例所属类信息
判断
Target的类型,单单用typeof并无法完全满足,这其实并不是bug,本质原因是JS的万物皆对象的理论。因此要真正完美判断时,我们需要区分对待:
- 基本类型(
null): 使用String(null) - 基本类型(
string / number / boolean / undefined) +function: - 直接使用typeof即可 - 其余引用类型(
Array / Date / RegExp Error): 调用toString后根据[object XXX]进行判断
jQ中封装的数据类型检测,检测一个对象是不是个纯对象
// 检测数据类型的方法封装
(function () {
var getProto = Object.getPrototypeOf; // 获取实列的原型对象。
var class2type = {};
var toString = class2type.toString;
var hasOwn = class2type.hasOwnProperty;
var fnToString = hasOwn.toString;
var ObjectFunctionString = fnToString.call(Object);
[
"Boolean",
"Number",
"String",
"Symbol",
"Function",
"Array",
"Date",
"RegExp",
"Object",
"Error"
].forEach(function (name) {
class2type["[object " + name + "]"] = name.toLowerCase();
});
function toType(obj) {
if (obj == null) {
return obj + "";
}
return typeof obj === "object" || typeof obj === "function" ?
class2type[toString.call(obj)] || "object" :
typeof obj;
}
// 判断是不是存对象。
function isPlainObject(obj) {
var 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;
}
window.toType = toType;
window.isPlainObject = isPlainObject;
})();