• 作者:老汪软件技巧
  • 发表时间:2024-09-23 21:03
  • 浏览量:

前言

在JavaScript中,类型判断是一个非常基础但也十分重要的知识点。不同的类型判断方法适用于不同的场景,掌握这些方法可以帮助我们更好地理解和使用JavaScript。本文将详细介绍typeofinstanceof、Object.prototype.toString以及Array.isArray这四种常用的类型判断方法,并通过实例代码帮助大家加深理解。

正文typeof

typeof操作符可以用来判断基本数据类型,如string、number、boolean、undefined、symbol、bigint等。它对于null和所有引用类型的判断会返回"object",而对于函数则会返回"function"。

特点:可以判断除null之外的所有原始类型。除了function,其他所有的引用类型都会被判断成object。typeof是通过将值转换为二进制后判断其二进制前三位是否为0,是则为object示例代码:

let s = '123'; // string
let n = 123; // number
let f = true; // boolean
let u = undefined; // undefined
let nu = null; // null
let sy = Symbol(123); // Symbol
let big = 1234n; // BigInt
console.log(typeof s); // "string"
console.log(typeof n); // "number"
console.log(typeof f); // "boolean"
console.log(typeof u); // "undefined"
console.log(typeof sy); // "symbol"
console.log(typeof big); // "bigint"
console.log(typeof nu); // "object" - 特殊情况
let obj = {};
let arr = [];
let fn = function() {};
let date = new Date();
console.log(typeof obj); // "object"
console.log(typeof arr); // "object"
console.log(typeof date); // "object"
console.log(typeof fn); // "function"
function isObject(o) {
  if (typeof o === 'object' && o !== null) {
    return true;
  }
  return false;
}
let res = isObject({a: 1});
console.log(res); // true

instanceof

instanceof用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。因此,它主要用于判断引用类型。

特点:只能判断引用类型。通过原型链查找来判断类型。示例代码:

let obj = {};
let arr = [];
let fn = function() {};
let date = new Date();
console.log(obj instanceof Object); // true
console.log(arr instanceof Array); // true
console.log(fn instanceof Function); // true
console.log(date instanceof Date); // true
console.log(arr instanceof Object); // true
console.log(arr instanceof String); // false
console.log(n instanceof Number); // false

因为原始类型没有原型而引用类型有原型,所有instanceof主要用于判断引用类型,那么根据这个我们是不是可以手写一个instanceof。

手写·instanceof实现:首先我们要知道v8创建对象自变量是这样的,拿let arr = []举例子:

function createArray() {
    // 创建一个新的对象
    let arr = new Array();
    // 设置原型
    arr.__proto__ = Array.prototype;
    // 返回创建的数组对象
    return arr;
  }

V8 引擎会调用Array构造函数来创建一个新的数组对象,Array构造函数的内部实现会创建一个新的空数组对象,并初始化其内部属性并且将新创建的数组对象的__proto__属性设置为Array.prototype,这意味着数组对象会继承Array.prototype上的所有方法和属性,最后,新创建的数组对象会被赋值给变量arr。

那么我们是不是可以通过实例对象的隐式原型等于其构造函数的显式原型来判断类型,代码如下:

function myInstanceOf(L,R){
    if(L.__proto__ === R.prototype){
        return true;
    }
    return false;
}

但是我们看到console.log([] instanceof Object); // true,所有还要改进一下:

我们要知道这么一件事情:

内置构造函数的原型链:

Object.prototype 的原型:

所以我们是不是可以这样:

function myinstanceof(L, R) {
  while (L !== null) {
    if (L.__proto__ === R.prototype) {
      return true;
    }
    L = L.__proto__;
  }
  return false;
}
console.log(myinstanceof([], Array));  // true
console.log(myinstanceof([], Object));  // true
console.log(myinstanceof({}, Array));  // false

所以就完美实现了。

Object.prototype.toString.call

Object.prototype.toString.call 是一个非常有用的工具,可以用来获取任何 JavaScript 值的类型信息。它结合了 Object.prototype.toString 和 Function.prototype.call 两个方法的功能。

特点:可以判断任何类型代码示例

console.log(Object.prototype.toString.call(null));       // [object Null]
console.log(Object.prototype.toString.call(123));        // [object Number]
console.log(Object.prototype.toString.call([]));         // [object Array]
console.log(Object.prototype.toString.call({}));         // [object Object]
console.log(Object.prototype.toString.call('hello'));    // [object String]
console.log(Object.prototype.toString.call(true));       // [object Boolean]
console.log(Object.prototype.toString.call(undefined));  // [object Undefined]
console.log(Object.prototype.toString.call(Symbol()));   // [object Symbol]
console.log(Object.prototype.toString.call(123n));       // [object BigInt]

Object.prototype.toString底层逻辑

根据官方文档,Object.prototype.toString 方法的执行步骤如下:

如果此值未定义,则返回"[object undefined]" 。如果此值为null,则返回"[object Null]" 。定义O是调用ToObject(该方法作用是把O转换为对象) 的结果,将this值作为参数传递。定义class是O的[[Class]]内部属性的值。返回"[object"和class和"]"组成的字符串的结果。关键点解释

console.log(Object.prototype.toString(123));//[object Object]
console.log(Object.prototype.toString('123'));//[object Object]
console.log(Object.prototype.toString({}));//[object Object]
console.log(Object.prototype.toString([]));//[object Object]

为什么需要call?

Object.prototype.toString 方法默认的 this 值是 Object.prototype 本身。如果我们直接调用 Object.prototype.toString(123),this 值仍然是 Object.prototype,而不是我们传入的值。因此,我们需要使用 call 方法来改变 this 值,使其指向我们传入的值。

手写call

obj = {
    a:1,
}
function foo(){
    console.log(this.a);
}
//我们需要将foo中的this指向obj里面
Function.prototype.myCall = function(context){
    if(!(this instanceof Function)){    在构造函数原型上,this指向的是实例对象,这里即foo
        return new TypeError(this+'is not function')
    }
 
    const fn = Symbol('key'); //使用symbol作为key是因为可能会同名
    context[fn] = this;//添加变量名为fn,值为上面的,context={Symbol('key'): foo}
    context[fn](); // 触发了隐式绑定
    delete context[fn]; //删除这个方法
}
foo.myCall(obj)    // 1
console.log(obj);  // {a:1}

我们知道call方法是将函数里面的this强行掰弯到我们传入的对象里面去,它的原理是这样的,首先判断你传入的参数是不是一个函数,因为只有函数身上才有call方法,函数调用然后通过隐式绑定规则,将this指向这个对象,那么不就强行更改了this的指向,[不知道this的可以看这篇文章](你不知道的JavaScript(核心知识点概念详细整理-掘金 ())

Array.isArray

Array.isArray是一个静态方法,用于检测给定的值是否为数组。

示例代码:

let arr = [];
let obj = {};
console.log(Array.isArray(arr)); // true
console.log(Array.isArray(obj)); // false

手写Array.isArray实现:

function myIsArray(value) {
  return Object.prototype.toString.call(value) === '[object Array]';
}
console.log(myIsArray(arr)); // true
console.log(myIsArray(obj)); // false

总结

希望这篇文章能够帮助你更好地理解和使用JavaScript中的类型判断方法,感谢你的阅读!