前言
在JavaScript中,数据的类型可以分为原始类型和引用类型两大类。
其中,在es6之前的原始类型有数字类型(Number)、字符串类型(String)、布尔类型(Boolean)、空值类型(null)、未定义类型(undefined)五种类型;在es6后新增了两种原始类型,分别为BigInt和Symbol。
引用类型有对象类型(Object)、日期类型(Date)、正则表达式类型(RegExp)、数组(Array)、函数(Function)等。
JavaScript 是一种动态类型语言,这意味着变量的类型可以在运行时改变。需要有判断数据类型的方法。类型判断的方法有typeof
( )、instanceof
、Object.prototype.toString.call( )
和Array.isArray( )
。
正文
新增的原始类型
BigInt
-
BigInt介绍:
BigInt提供了一种方法来表示大于
2^53 - 1
的整数。这原本是 Javascript 中可以用 Number表示的最大数字。BigInt 可以表示任意大的整数,解决了 JavaScript中整数溢出的问题。 -
定义BigInt的方法:
可以用在一个整数字面量后面加
n
的方式定义一个 BigInt,或者调用函数BigInt()
定义一个BigInt。javascriptconst bigint = 99999999999999999999999999999n const big = BigInt(9000002281892828883838)
-
BigInt与Number的不同点:
BigInt 不能与 Number 混合运算,需要进行类型转换(BigInt变量转换成Number变量时可能会丢失精度);BigInt 不支持 Math 对象中的方法,如
Math.sin()
、Math.cos()
等。
Symbol
-
Symbol介绍:
每个从
Symbol()
返回的 symbol 值都是唯一的。一个symbol 值能作为对象属性的标识符;这是该数据类型仅有的目的。 -
定义一个Symbol的方法:
调用
Symbol()
是函数创建Symbol。javascriptlet symbol1 = Symbol(42) let symbol2 = Symbol(42) console.log(symbol1 === symbol2)//false
我们创建的
symbol1
和symbol2
变量在创建过程中是单独开辟的独立空间,二者地址不同所以console.log(symbol1 === symbol2)
打印false。
类型判断(4种方法)
typeof( )
javascript
console.log(typeof (null));//object
console.log(typeof (undefined));//undefined
console.log(typeof (true));//boolean
console.log(typeof (1));//number
console.log(typeof ("1"));//string
console.log(typeof (Symbol()));//symbol
console.log(typeof (BigInt(1)));//bigint
console.log(typeof ([]));//object
console.log(typeof ({}));//object
console.log(typeof (function () { }));//function
通过typeof()
进行类型判断:
- 可以精准得判断出原始类型,除了
null
;对null
判断会返回object
。 - 对引用类型的区分有限,只能明确判断出一个值是否为函数,返回
function
;对于其他对象统一返回object
。 - typeof是通过将值转换为二进制后判断其二进制前三位是否为0,是则为object,否则为原始类型。
typeof (null)
返回的为什么是object
?
在 JavaScript 的早期实现中,类型检查是通过查看值的内部表示来进行的。所有对象在内存中的表示都以一定的二进制格式存储,而 null
值在内部表示为全零的二进制序列。在进行类型判断时,JavaScript 引擎会检查值的头部几个比特位来确定其类型。由于 null
的二进制表示全为0,恰好与对象类型的标记位(对象类型的标记可能是前三位为0)相吻合,因此在执行 typeof
操作时,它被错误地分类为了 "object"
。
typeof (function)
返回的为什么是function
?
typeof (function)
返回 "function"
的原因是,function
关键字在 JavaScript 中用于定义函数,而函数是一种可调用的对象,具有执行代码的能力。typeof
操作符专门对函数类型进行了特殊处理,当它用于检查一个函数表达式或函数声明时,会明确返回字符串 "function"
,以此来区分函数与其他对象类型。
instanceof
javascript
var arr = [1, 2, 3];
var obj = { a: 1, b: 2, c: 3 };
var str = '123';
var num = 123;
var bool = true;
var und = undefined;
var nul = null;
var sym = Symbol();
var func = function () { };
var date = new Date();
var big = 123n
//引用类型
console.log(arr instanceof Array);//true
console.log(obj instanceof Object);//true
console.log(func instanceof Function);//true
console.log(date instanceof Date);//true
//元素类型
console.log(sym instanceof Symbol);//false
console.log(big instanceof BigInt);//false
console.log(str instanceof String);//false
console.log(num instanceof Number);//false
console.log(bool instanceof Boolean);//false
console.log(und instanceof Undefined);//报错,Undefined 并不是一个构造函数
console.log(nul instanceof Null);//报错,Null 并不是一个构造函数
var arr1 = []
console.log(arr instanceof Array);//true
console.log(arr instanceof Object);//true
你会发现所有的原始类型返回的都是false,而引用类型返回的都是true。因为instanceof
是通过原型链判断类型的,而原始类型没有原型,所以无法判断其类型。
console.log(arr instanceof Array)
和console.log(arr instanceof Object)
打印的都是ture,是因为instanceof
是通过原型链判断类型的,并且arr.__proto__==Array.prototype
,arr.__proto__.__proto__==Object.prototype
所以二者打印的都为true。
instanceof
的特点:
instanceof
只能判断引用类型,不能判断原始类型。instanceof
是通过判断引用类型原型链中是否有该构造函数的原型,如果有则为true,否则为false。
手搓instanceof
的判断逻辑:
javascript
function myinstanceof(object, constructor) {
while (object !== null) {
if (object.__proto__ === constructor.prototype) {
return true
} else {
object = object.__proto__
}
}
return false
}
console.log(myinstanceof({}, Object));//true
console.log(myinstanceof([], Array));//true
console.log(myinstanceof(new Date(), Date));//true
console.log(myinstanceof(new Date(), Object));//true
console.log(myinstanceof([], Object));//true
console.log(myinstanceof({}, Array));//false
Object.prototype.toString.call( )
Object.prototype.toString.call()
是一种用于更精确地确定数据类型的方法。
javascript
//引用类型
console.log(Object.prototype.toString.call(new Array()));//[object Array]
console.log(Object.prototype.toString.call(new Date()));//[object Date]
console.log(Object.prototype.toString.call(new Function()));//[object Function]
console.log(Object.prototype.toString.call(new Object()));//[object Object]
//原始类型
console.log(Object.prototype.toString.call(null));//[object Null]
console.log(Object.prototype.toString.call(undefined));//[object Undefined]
console.log(Object.prototype.toString.call(BigInt(123)));//[object BigInt]
console.log(Object.prototype.toString.call(new Boolean()));//[object Boolean]
console.log(Object.prototype.toString.call(new String()));//[object String]
console.log(Object.prototype.toString.call(new Number()));//[object Number]
console.log(Object.prototype.toString.call(Symbol()));//[object Symbol]
可以发现Object.prototype.toString.call()
可以准确判断类型,并且返回值为[object Type]
,其中 Type
是根据对象的具体类型决定的。
在了解Object.prototype.toString.call()
之前,要先了解Object.prototype调用toString
方法时的步骤和call
对this的显示绑定。
当Object.prototype调用toString方法时的步骤
只有Object.prototype调用的时候才会走这些步骤:
- 如果this的值为
null
,就返回[object Null]
。 - 如果this的值为
undefined
,就返回[object Undefined]
。 - 如果this既不是
null
也不是undefined
,则let O = ToObject(this)
,让O
作为调用ToObject
的结果。(ToObject
操作是一种将非对象值转换为对象的过程,如数字会转换为Number
对象,字符串会转换为String
对象等) - 定义一个
class
作为O
的内部属性[[class]]
的值。([[class]]
是一个内部属性,用于表示对象的基本类型分类。例如,数组的[[Class]]
可能是 "Array") - 返回由"[object"和"class"和"]"组成的字符串。
但是只使用toString
方法还是无法准确判断数据类型。还要结合call
方法对this指向进行显式绑定。
call方法修改this指向(底层逻辑):
this指向的显式绑定问题传送门------玩转 JS 中 this 指向
eg:call方法改变this的指向例子。
javascript
var object = {
a: 1
}
function func() {
console.log(this.a)
}
//要通过func函数输出1,可以通过call方法将func里的this指向object
func.call(object)
我们可以用一个函数的方法模拟call方法,探索call方法的底层原理。
手搓call方法:
javascript
//要让函数可以调用,要将mycall函数挂在Function构造函数的原型上
Function.prototype.mycall = function (context) {
// 检查 this 是否为函数
if (typeof (this)!== 'function') {
// 如果不是函数,抛出 TypeError
return new TypeError(this + 'is not a function')
}
// 创建一个新的 Symbol(防止context对象里有func键名出现问题)
const func = Symbol('func')
// 将 this 赋值给 context 的 func 属性
context[func] = this
// 通过 context 的 func 属性调用 this 函数
const res = context[func]()//用隐式绑定将this指向了context
// 删除 context 的 func 属性
delete context[func]
// 返回调用的结果
return res
}
call方法的主要步骤:
- 判断调用call方法的是不是一个函数;
- 通过在引入的形参对象上添加了一个独一无二的函数;
- 将this(调用call的函数)赋值为刚刚添加的函数;
- 然后将调用该函数的结果赋值给res(通过隐式绑定让this指向形参对象);
- 删除添加的函数;(为了使形参对象保持原状)
- 返回res。
该效果相当于object借用了func函数。
在了解完toString
和call
后,该模拟一遍Object.prototype.toString.call( )
是运行的。
eg:
javascript
Object.prototype.toString.call('1')
//相当于
Object.prototype.toString.call(new String('1'))
//call的参数是对象
-
通过
call
方法将this指向new String('1')
对象; -
Object.prototype
调用toString
方法,执行toString
方法被调用的步骤:-
判断是不是
null
和undefined
; -
创建一个变量O,将
ToObject(this)
的值(也就是String对象)赋值给变量O。 -
创建名为class的变量,将O的内部属性[[class]](也就是"String")赋值给class。
-
返回"[object"和"class"和"]"组成的字符串(也就是"[object"+"String"和"]")。
-
Array.isArray( )
Array.isArray()
是 JavaScript中用于判断一个值是否为数组的方法。它返回一个布尔值,如果传入的参数是一个数组则返回 true
,否则返回 false
。
这个方法可以准确地区分数组和其他类似数组的数据结构或普通对象。