基本数据类型
包括基础(原始)数据类型 和引用(复杂)数据类型
原始数据类型(基本数据类型):
- 数值类型(Number): 整数和浮点数
- 字符串类型(String): 用引号括起来的
- 布尔类型(Boolean):true和false
- undefined类型:确定一个已经声明但还没有赋值的变量
- null类型:表明某个变量的值为空 (空对象指针)
- Symbol 类似于一种标识唯一性的ID Symbol 是独一无二的所以可以保证对象属性的唯一
- Bigint 可以表示任意大的整数 BigInt(value); value: 创建对象的数值。可以是字符串或者整数
引用数据类型(对象数据类型):
- 对象 :Object
- 数值 :Array
- 函数 :function
所有的引用数据类型数据的创建都要开辟一个【堆内存】
检测数据类型
- typeof 用来检测数据类型的 【运算符】**,**【返回的结果是一个字符串】
- instanceof 用来检测当前实例是否属于某个类,【变相用来检测数据类型,看作是对typeof的补充】
- constructor 基于构造函数检测数据类型,(也是基于类的方式)
- Object.prototype.toString.call([value]) 【检测数据类型的最好方法】
typeof
特点1:返回的结果是字符串,字符串中包含了对应的数据类型
typeof typeof typeof [1,2,3] //面试题
- 因为typeof检测的结果都是字符串,所以只要两个及以上同时检测,最后结果必然是”string”
特点2:按照计算机底层存储的二进制进行检测(实现原理)优点:性能好
- GetValue(val)[c++内部提供的方法], 按照值存储的二进制进行检测
- 对象 000 -> 实现call,则返回’function’ , 没实现call返回’object’
- null 000000 -> 肯定没实现call ,返回’object’
- undefined -2**30**
- 数字
- 整数 1
- 浮点数 010
- 字符串 100
- 布尔 110
- 由于是按照二进制来检测,速度很快,除了null 都很准确
特点3:typeof null -> “object” null的结果是object 因为 null 是0000000
特点4:typeof Object && typeof function(){} -> “function”
- 验证是否是对象的判断
- 缺点:不能细分出具体为什么类型的对象 ,因为只要是对象数据类型,返回的结果都是”object”
- typeof 数组/正则/日期/对象 【只要是对象数据类型】=>’object’
特点5:typeof 未被声明的变量 -> “undefined”
//【对于基本数据类型】
let a = NaN;
typeof 1;//=>"number"
typeof a;//=>"number"
typeof NaN;;//=>"number"
typeof 'string';//=>"string"
typeof true;//=>"boolean"
typeof undefined;//=>"undefined"
typeof null;//"object"
typeof typeof undefined;//=>"string"
//【对于引用数据类型】
typeof {};//=>"object"
typeof [];//=>"object"
typeof /^/;//=>"object"
typeof function(){};//=>"function"
typeof typeof typeof function(){};//=>"string"
typeof
能检测的:
**function**
、**string**
,**number**
,**boolean**
,**undefined**
,**symbol
typeof
不能检测的:**null**
和**对象**
都会被检测为**object**
!!- 除了可调用(实现call对象, 函数会返回’function’)
- 不论是箭头函数、还是构造函数、还是生成器函数、以及普通函数等,都返回function
- 其余的对象数据值(不实现call)返回都是’object’
- 检测一个未被声明的变量不会报错 报 undefined
instanceof
instance运算符用于通过 查找** 原型链 来检测某个变量 是否 为某个类型数据的**实例。使用instanceof 运算符可以判断一个变量是数组还是对象。
A instanceof B 判断 A 是否为 B 的实例
是什么:instanceof 是用来判断实例对象 A 是否为构造函数 B 的实例 (返回 true / false)
基本原理:根据原型链检测,只要在原型链上就为 true,一直找到 object.prototype
【缺陷】:**
- 不能用来处理基本数据类型(基本数据类型基于构造函数方式创建的实例是可以的)
- 只要出现在实例的原型链上的类,该实例的检测结果都是true**(可以手动更改原型链的指向,这样导致检测结果不一定准确)
let arr = [],
reg = /^$/;
const b = {name: 'xx'};
console.log(arr instanceof Array);//=>true
console.log(arr instanceof Object);//=>true
console.log(b instanceof Array); // false
console.log(b instanceof Object); // true
console.log(reg instanceof Array);//=>false
console.log(1 instanceof Number);//=>false
console.log(new Number(1) instanceof Number);//=>true
console.log(Symbol() instanceof Symbol);//=>false
function fn(){}
fn.prototype = Array.prototype;
let f = new fn;
console,log(f instanceof Array);//=>true
constructor: 基于构造函数检测数据类型
constructor
主要是利用原型上的prototype.constructor
指向实例的构造函数来进行判断的
null
,undefined
是无效的对象,因此是不会有constructor
存在的,这两种类型的数据需要通过其他方式来判断。 函数的 constructor 是不稳定的,这个主要体现在自定义对象上,当开发者重写 prototype 后,原有的 constructor 引用会丢失,constructor 会默认为 Object
- 用
constructor
判断类型的限制过多且不准确,容易出错,少用,或者不用!
const a = [0, 1, 2];
console.log(a.constructor === Array); // true
console.log(a.constructor === Object); // false
const b = {name: 'xx'};
console.log(b.constructor === Array); // false
console.log(b.constructor === Object); // true
console.log('1'.constructor === String); // true
console.log(new Number(1).constructor === Number); // true
console.log(true.constructor === Boolean); // true
console.log(alert.constructor === Function); // true
console.log([].constructor === Array); // true
console.log(new Date().constructor === Date); // true
Object.prototype.toString.call():【检测数据类型的最好方法】准确的数据类型
最强大的检测数据类型的方法,(基本上没有弊端)
- 对于 number 类型,结果是 [object Number]
- 对于 boolean 类型,结果是 [object Boolean]
- 对于 null:[object Null]
- 对于 undefined:[object Undefined]
- 对于数组:[object Array]
- 对于对象:[object Object]
- ……等(可自定义)\ ```javascript object.prototype.toString.call(null) //“[object Null]” object.prototype.toString.call(1)// “[object Number]” object.prototype.toString.call(new Number(1)) //“[object Number]” object.prototype.toString.call(Symbol()) //“[object Symbol]” object.prototype.toString.call(function(){}) //“[object Function]” object.prototype.toString.call(Object) //“[object Function]” object.prototype.toString.call({}) //“[object Object]” object.prototype.toString.call([]) //“[object Array]” object.prototype.toString.call(/^$/) //“[object RegExp]” object.prototype.toString.call(new Date()) //“[object Date]”
function fn(){} let f = new fn();
object.prototype.toString.call(f);//=>’[object object]’
**原理:**<br />![](https://cdn.nlark.com/yuque/0/2021/png/1489212/1612144642671-75a8cfb5-32a8-4296-aca3-bb5b5edc5e7f.png#align=left&display=inline&height=192&margin=%5Bobject%20Object%5D&originHeight=384&originWidth=1027&status=done&style=none&width=513)<br />toString:
1. 对于Object:**检测数据类型**
1. 对于Object的实例:**转换为字符串**
**为什么不直接用 obj.toString () 呢?**<br />非普通对象.toString :调取的都是自己所属类原型上的toString,并不是Object.prototype.toString,都是转换为字符串<br />普通对象.toString :调取Object.prototype.toString这个方法,所以是检测数据类型
```javascript
console.log("jerry".toString()); // jerry
console.log({name: "leo"}.toString()); // [object, object]
console.log((1).toString()); // 1
console.log([1,2].toString()); // 1,2
console.log(new Date().toString()); // Wed Dec 21 2016 20:35:48 GMT+0800 (中国标准时间)
console.log(function(){}.toString()); // function (){}
console.log(null.toString()); // error
console.log(undefined.toString()); // error
同样是检测对象 obj 调用 toString 方法,obj.toString () 的结果Object.prototype.toString.call (obj) 的结果不一样,这是为什么?
console.log(Object.prototype.toString.call("jerry")); //[object String]
console.log(Object.prototype.toString.call(12)); //[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: "leo"})); //[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]
- 这是因为 toString 为 Object 的原型方法,而 Array 、Function 等类型作为 Object 的实例,都重写了 toString 方法
- 不同的对象类型调用 toString 方法时,根据原型链的知识,调用的是对应的重写之后的 toString 方法(Function 类型返回内容为函数体的字符串,Array 类型返回元素组成的字符串…..),而不会去调用 Object 上原型 toString 方法(返回对象的具体类型),所以采用 obj.toString () 不能得到其对象类型,只能将 obj 转换为字符串类型;
- 因此,在想要得到对象的具体类型时,应该调用 Object 上原型 toString 方法
- 为了实现这个目的,就使用call来借用该方法
我们可以验证一下,将数组的 toString 方法删除,看看会是什么结果
var arr=[1,2,3];
console.log(Array.prototype.hasOwnProperty("toString"));//true
console.log(arr.toString());//1,2,3
delete Array.prototype.toString;//delete操作符可以删除实例属性
console.log(Array.prototype.hasOwnProperty("toString"));//false
console.log(arr.toString());//"[object Array]"
删除了 Array 的 toString 方法后,同样再采用 arr.toString () 方法调用时,不再有屏蔽 Object 原型方法的实例方法,因此沿着原型链,arr 最后调用了 Object 的 toString 方法,返回了和 Object.prototype.toString.call (arr) 相同的结果。
- 修改Symbol.toStringTag
Symbol.toStringTag
的例子。可以看出,属性值期望是一个字符串,否则会被忽略。
var o1 = { [Symbol.toStringTag]: "A" };
var o2 = { [Symbol.toStringTag]: null };
Object.prototype.toString.call(o1); // => "[object A]"
Object.prototype.toString.call(o2); // => "[object Object]"
最终方案:
这里我们来给它封装成一个方法,用于准确判断变量的类型。
function judge(type) {
var string = Object.prototype.toString.call(type);
return string.substr(8, string.length - 9); 截取一下
}
案例:
let a = 4;
let c = [];
function judge(type) {
var string = Object.prototype.toString.call(type);
return string.substr(8, string.length - 9)
}
console.log(judge(c)) // Array
console.log(judge(a)) // Number
**