干货满满:数据类型判断(4种方法)、call的底层逻辑和Object.prototype调用toString方法时的步骤

前言

在JavaScript中,数据的类型可以分为原始类型和引用类型两大类。

其中,在es6之前的原始类型有数字类型(Number)、字符串类型(String)、布尔类型(Boolean)、空值类型(null)、未定义类型(undefined)五种类型;在es6后新增了两种原始类型,分别为BigInt和Symbol。

引用类型有对象类型(Object)、日期类型(Date)、正则表达式类型(RegExp)、数组(Array)、函数(Function)等。

JavaScript 是一种动态类型语言,这意味着变量的类型可以在运行时改变。需要有判断数据类型的方法。类型判断的方法有typeof( )、instanceofObject.prototype.toString.call( )Array.isArray( )

正文

新增的原始类型

BigInt

  1. BigInt介绍:

    BigInt提供了一种方法来表示大于 2^53 - 1 的整数。这原本是 Javascript 中可以用 Number表示的最大数字。BigInt 可以表示任意大的整数,解决了 JavaScript中整数溢出的问题。

  2. 定义BigInt的方法:

    可以用在一个整数字面量后面加 n 的方式定义一个 BigInt,或者调用函数BigInt()定义一个BigInt。

    javascript 复制代码
    const bigint = 99999999999999999999999999999n
    const big = BigInt(9000002281892828883838)
  3. BigInt与Number的不同点:

    BigInt 不能与 Number 混合运算,需要进行类型转换(BigInt变量转换成Number变量时可能会丢失精度);BigInt 不支持 Math 对象中的方法,如Math.sin()Math.cos()等。

Symbol

  1. Symbol介绍:

    每个从 Symbol() 返回的 symbol 值都是唯一的。一个symbol 值能作为对象属性的标识符;这是该数据类型仅有的目的。

  2. 定义一个Symbol的方法:

    调用Symbol()是函数创建Symbol。

    javascript 复制代码
    let symbol1 = Symbol(42)
    let symbol2 =  Symbol(42)
    
    console.log(symbol1 === symbol2)//false

    我们创建的symbol1symbol2变量在创建过程中是单独开辟的独立空间,二者地址不同所以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()进行类型判断:

  1. 可以精准得判断出原始类型,除了null;对null判断会返回object
  2. 对引用类型的区分有限,只能明确判断出一个值是否为函数,返回function;对于其他对象统一返回object
  3. 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.prototypearr.__proto__.__proto__==Object.prototype所以二者打印的都为true。

instanceof的特点:

  1. instanceof只能判断引用类型,不能判断原始类型。
  2. 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调用的时候才会走这些步骤:

  1. 如果this的值为null,就返回[object Null]
  2. 如果this的值为undefined,就返回[object Undefined]
  3. 如果this既不是null也不是undefined,则let O = ToObject(this),让O作为调用ToObject的结果。(ToObject 操作是一种将非对象值转换为对象的过程,如数字会转换为 Number 对象,字符串会转换为 String 对象等)
  4. 定义一个class作为O的内部属性[[class]]的值。([[class]] 是一个内部属性,用于表示对象的基本类型分类。例如,数组的 [[Class]] 可能是 "Array")
  5. 返回由"[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方法的主要步骤:

  1. 判断调用call方法的是不是一个函数;
  2. 通过在引入的形参对象上添加了一个独一无二的函数;
  3. 将this(调用call的函数)赋值为刚刚添加的函数;
  4. 然后将调用该函数的结果赋值给res(通过隐式绑定让this指向形参对象);
  5. 删除添加的函数;(为了使形参对象保持原状)
  6. 返回res。

该效果相当于object借用了func函数。

在了解完toStringcall后,该模拟一遍Object.prototype.toString.call( )是运行的。

eg:

javascript 复制代码
Object.prototype.toString.call('1')
//相当于
Object.prototype.toString.call(new String('1'))
//call的参数是对象
  1. 通过call方法将this指向new String('1')对象;

  2. Object.prototype调用toString方法,执行toString方法被调用的步骤:

    1. 判断是不是nullundefined;

    2. 创建一个变量O,将ToObject(this)的值(也就是String对象)赋值给变量O。

    3. 创建名为class的变量,将O的内部属性[[class]](也就是"String")赋值给class。

    4. 返回"[object"和"class"和"]"组成的字符串(也就是"[object"+"String"和"]")。

Array.isArray( )

Array.isArray() 是 JavaScript中用于判断一个值是否为数组的方法。它返回一个布尔值,如果传入的参数是一个数组则返回 true,否则返回 false

这个方法可以准确地区分数组和其他类似数组的数据结构或普通对象。

相关推荐
diygwcom7 分钟前
electron-updater实现electron全量版本更新
前端·javascript·electron
volodyan10 分钟前
electron react离线使用monaco-editor
javascript·react.js·electron
^^为欢几何^^19 分钟前
lodash中_.difference如何过滤数组
javascript·数据结构·算法
Hello-Mr.Wang23 分钟前
vue3中开发引导页的方法
开发语言·前端·javascript
程序员凡尘1 小时前
完美解决 Array 方法 (map/filter/reduce) 不按预期工作 的正确解决方法,亲测有效!!!
前端·javascript·vue.js
编程零零七4 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql
北岛寒沫5 小时前
JavaScript(JS)学习笔记 1(简单介绍 注释和输入输出语句 变量 数据类型 运算符 流程控制 数组)
javascript·笔记·学习
everyStudy5 小时前
JavaScript如何判断输入的是空格
开发语言·javascript·ecmascript
(⊙o⊙)~哦6 小时前
JavaScript substring() 方法
前端
无心使然云中漫步7 小时前
GIS OGC之WMTS地图服务,通过Capabilities XML描述文档,获取matrixIds,origin,计算resolutions
前端·javascript