数据类型
- 原始值类型「值类型/基本数据类型」
- Number 数字
- Boolean 布尔
- String 字符串
- Null 空对象指针
- Undefined 未定义
- Symbol 唯一值 ???
- Bigint 大数 ???
- 对象类型「引用数据类型」
- 标准普通对象 object
- 标准特殊对象 Array、RegExp、Date、Math、Error……
- 非标准特殊对象 Number、String、Boolean……
- 可调用/执行对象「函数」function
常用的数据类型检测方法有哪些?
1. typeof 运算符
语法:
var str = typeof [value]
作用: 用来检测给定变量的数据类型
返回值: 一个字符串,字符串中包含了对应的数据类型 。
始终返回以下某个字符串:
- 'undefined' => 值未定义
- 'boolean' => 布尔值
- 'number' => 数值
- 'string' => 字符串
- "symbol" =>唯一值
- 'bignit' => 大数
- 'function' => 函数是一种对象,不是一种数据类型
- 'object' => 被检测的值是对象或者null
原理(底层机制): 所有数据类型的值在存储时,在计算机底层都是按照“二进制”来存储的「64位」
typeof 检测数据类型,就是按照存储的“二进制值”来进行检测的, 比如:
- 000 开头的 => 对象
- 1 开头的 => 整数
- 010 开头的 => 浮点数
- 100 开头的 => 字符串
- 110 开头的 => 布尔值
- 000000…. => null
如值的前三位是 000 的,都被认为是对象。在这个基础上,再检测对象内部是否实现了[[Call]]
方法
- 实现了,则认为是函数,返回“function”。
- 没有实现,则都返回 "object"。
优点:
- 因为 typeof 检测数据类型,就是按照存储的“二进制值”来进行检测的,因此使用 typeof 检测数据类型的时候,性能相对好一些。
- 在检测原始值类型的值( null 除外)时,结果都是准确的。
缺点(局限性):
- 使用 typeof 检测 null 时返回的是 'object'
- 使用 typeof 检测对象类型的值时,不能细分对象。除函数对象返回“function”外,其他的对象类型值返回的都是“object”。
- 使用 typeof 检测一个未被声明的变量,不会报错,而是返回“undefined”。
2. instanceof 操作符
语法:
var result = value instanceof Ctor
// Ctor => constructor 构造函数
作用:
检测某个实例是否属于这个类。可以弥补 typeof 的不足,把对象类型进行细分。
返回值:
- true => 实例属于这个类
- false => 实例 不属于这个类
原理(底层机制):
基于 instanceof
检测的时候,先看 Ctor 构造函数是否存在 Symbol.hasInstance
这个属性方法,
- 如果存在则基于这个方法进行检测,则调用
Ctor[Symbol.hasInstance](value)
- 如果没有这个属性方法,获取value的原型链(直到找到
Object.prototype
为止)- 如果
Ctor.prototype
出现在它的原型链上,则证明value是Ctor的实例, - 反之则不是...
- 如果
- 在新版本浏览器中,在
Function.prototype
上存在一个属性方法Symbol.hasInstance
,所以只要是函数「不论是普通函数,还是构造函数」,都具备Symbol.hasInstance
这个方法...
缺点(局限性):
- 用
instanceof
来检测数据类型,就是临时“凑个数”,所以答案仅供参考。 - 用
instanceof
检测任何引用值 和 Object 构造函数都会返回 true。因为所有引用值都是 Object 的实例。 - 用
instanceof
检测原始值,则始终会返回 false,因为原始值不是对象。
例1:
let n = [];
console.log(n instanceof Array); //true
console.log(n instanceof RegExp); // false
console.log(n instanceof Object); //true 所有引用值都是 Object 的实例。
例2:
自定义构造函数,如果借用其他构造函数的原型对象,会致使检测结果不准。
下面代码中的构造函数 Fn ,借用了 Array.prototype。
function Fn() {}
Fn.prototype = Array.prototype;
let f = new Fn;
console.log(f instanceof Array); // true 检测结果不准了。
例3:
内置类的 Symbol.hasInstance 属性,重写是无效的
Array[Symbol.hasInstance] = function () {
return 100;
};
let n = [];
console.log(n instanceof Array); //=> true
例4:
自定义构造函数,可以指定 Symbol.hasInstance 属性。可能会致使检测结果不准。
class Fn {
static[Symbol.hasInstance](val) {
return false;
}
}
let f = new Fn; // f 是构造函数 Fn 的实例
console.log(f instanceof Fn); //false
console.log(Fn[Symbol.hasInstance](f)); //false
3. constructor
语法:
var result = 实例对象.constructor ;
作用: 获取实例的构造函数。
返回值: 实例对象所属的构造函数(堆地址)
原理(底层机制):
大多数函数都有一个 prototype 属性(原型对象),其下面有个 constructor 属性。
默认情况下,constructor 属性指回与之关联的构造函数。
缺点(局限性):
- 用
constructor
来检测数据类型也是临时拉来凑数的,所以也不靠谱。 constructor
属性的指向可以被肆意更改。null
在访问constructor
属性时,会报错。
例1:
可以用 constructor
检测一个对象是否为标准普通对象「纯粹对象」, 是否为 Object 的直属实例。
n 是 Array 的直属实例, 而不是 RegExp 和 Object 的直属实例。
let n = [];
console.log(n.constructor === Array); // true
console.log(n.constructor === RegExp); // false
console.log(n.constructor === Object); // false
例2:
使用原始值访问constructor
属性时,会默认进行“装箱”操作。
所谓“装箱”是指原始值转换为其构造函数创造的对象类型实例,从而原始值就可以调用所属类原型上的方法了。
let n = 1;
console.log(n.constructor === Number);
// true
// 把原始值的1默认变为对象类型的实例 new Number(1)
例3:
修改了 constructor 属性的指向 ,检测结果不准了
let n = [];
Array.prototype.constructor = 'AAA'; //=>false
console.log(n.constructor === Array);
例4:
null
在访问 constructor
属性时,会报错
(null).constructor
// Uncaught TypeError: Cannot read property 'constructor' of null
4. Object.prototype.toString.call([value])
语法:
作用: 大部分类的原型上都有toString方法,都是用来转换为字符串的...
但是Object.prototype.toString不是用来转换为字符串的,而是检测数据类型的
返回值: “[object ?]”
原理(底层机制): 先检查[value][Symbol.toStringTag]属性,
- 如果有这个属性,属性值是啥 @X,最后检测的结果就是 “[object @X]”。
-
- 如果没这个属性,则按照自己所属的内置类进行处理
优点:
这个方法忒好用了...除了写起来麻烦一些,几乎没有漏洞
例1:
调用 Object.prototype.toString() 时,方法中的this是谁,就是检测谁的数据类型
// 1.
let obj = {
name: 'xiaoming'
}
console.log(obj.toString());
//=> "[object Object]" -> Object.prototype.toString
// 2. 通过 call() 改变方法执行时的 this 指向
Object.prototype.toString.call([10, 20])
// =>"[object Array]"
// 3. 实际开发中,为了简化代码,还会这样写
let obj = {},
toString = obj.toString; // => Object.prototype.toString
console.log(toString.call(1)); // => "[object Number]"
例2:
Number.prototype.toString 把数组实例 转换为字符串
- 数字.toString() 把数字转换为字符串
- 数字.toString(radix) 把数字转换为radix进制值的字符串
// 不指定 radix, 默认为十进制数
console.log((2).toString());
// "2"
// 指定 radix 为 2, 先把数值转为2进制数,再转成字符串
console.log((2).toString(2));
// "10"
例3:
console.log([10, 20, 30].toString());
//-> "10,20,30" -> Array.prototype.toString
例4:
为自定义构造函数的原型对象上添加属性 Symbol.toStringTag
class Fn {
constructor() {
this.name = 'zhufeng';
this[Symbol.toStringTag] = 'Fn';
}
}
let f = new Fn;
console.log(toString.call(f)); //=>“[object Fn]”
封装几个用于数据检测的函数
1. 检测是否是一个函数
var isFunction = function isFunction(obj) {
return typeof obj === 'function' && typeof obj.nodeType !== "number" && typeof obj.item !== "function"
}
第一个条件 typeof obj === 'function'
,已经判断出obj是函数了,为什么还要加 2 个判断条件呢?
那是为了处理两个兼容问题:
-
typeof document.createElement("object")==="function"
-
typeof document.getElementsByTagName("div")==="function"
在 HTML 中有个` <object>` 元素。使用<object> 元素可在 HTML 加入 Flash 文件。有些浏览器会判定<object> 元素为函数,所以我们需要排除 <object> 元素。那么如何才能排除呢? 在 JavaScript中,所有 DOM 节点类型都继承 Node 类型。也就是说每个 DOM 节点都能访问到 Node.prototype 上的属性和方法。而 Node.prototype 上就有一个 nodeType 属性,表示该节点的类型。节点类型由定义在 Node 类型上的 12 个数值常量表示。所以不管是什么类型的节点,返回的值类型都是 'number',即 `typeof obj.nodeType !== "number"` 。 通过 `document.getElementsByTagName() ` 获取到的是一个 `HTMLCollection` 元素的集合。 `HTMLCollection` 提供了用来从该集合中选择元素的方法和属性。 其中有个静态方法 item(),其作用是根据给定的索引(从0开始),返回具体的节点。
2. 检测是否 window 对象
const isWindow = function isWindow(obj) {
return obj !=null && obj === obj.window
}
3. 检测数据类型的方法(十八项全能)
let toString = {}.toString;
// {}.toString => Object.prototype.toString
const toType = function toType(obj) {
let reg = /^\[object ([0-9a-zA-Z]+)\]$/;
// null & undefined
if (obj == null) return obj + '';
// 其他类型的
return typeof obj === 'object' || typeof obj === 'function' ? reg.exec(toString.call(obj))[1].toLowerCase() : typeof obj ;
}
console.log(toType(0)); // 'number'
console.log(toType(/^$/)); // 'regexp'
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!