每天一道JS手写题💪「Day1数据类型判断+手写instanceof」

数据类型判断

typeof

typeof 可以区分基础数据类型,而对所有的引用数据类型都会返回 object, 对所有的类都会返回 function(在 ECMA-262 中实现 [[Call]];classes也是函数)。

js 复制代码
typeof "Ken"                  // 返回 "string"
typeof 3.14                   // 返回 "number"
typeof false                  // 返回 "boolean"
typeof function () {}         // 返回 "function"
typeof undefined              // 返回 "undefined"
typeof [1,2,3,4]              // 返回 "object"
typeof {name:'Ken', age:18}   // 返回 "object"
typeof new Date()             // 返回 "object"
typeof null                   // 返回 "object"
typeof Object                 // 返回 "function"
typeof Number                 // 返回 "function"

instanceof

instanceof 运算符 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。基础数据类型、undefined、null没有构造函数,都无法使用这一方法。

js 复制代码
123 instanceof Number                // 返回 false
new Number(123) instanceof Number    // 返回 true

'123' instanceof String              // 输出 false
new String('123') instanceof String  // 返回 true

([]) instanceof Array                // 返回 true
({}) instanceof Object               // 返回 true
(function(){}) instanceof Function   // 返回 true

constructor

Object 实例的 constructor 数据属性返回一个引用,指向创建该实例对象的构造函数。注意,此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。

constructor能判断基本数据类型string、number、boolean和对象类型(array、function等等),但是它不能判断 undefined 和 null,试图从 undefined 和 null 上读属性是会报错的。

js 复制代码
const o1 = {};
o1.constructor === Object; // true

const o2 = new Object();
o2.constructor === Object; // true

const a1 = [];
a1.constructor === Array; // true

const a2 = new Array();
a2.constructor === Array; // true

const n = 3;
n.constructor === Number; // true

Object.prototype.toString.call()

js 复制代码
function typeOf(obj) {
    // Object.prototype.toString() 的输出是 [Object object] 这种格式,这里通过 slice 做了裁剪
    return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}

typeOf([])        //返回 'array'
typeOf({})        //返回 'object'
typeOf(new Date)  //返回 'date'

关于这种方法 mdn 文档中有提及 Object.prototype.toString()

以这种方式使用 toString() 是不可靠的;对象可以通过定义 Symbol.toStringTag 属性来更改 Object.prototype.toString() 的行为,从而导致意想不到的结果。

但实际开发中我们大多不会去修改 Symbol.toStringTag 属性的值,故而这种方法已经够用了。

为什么Object.prototype.toString.call()可以准确判断数据类型呢?

简单的说就是官方的定义,Object.prototype上的 toString 方法,它就是干这个的。

但要注意的是每个数据类,他们都重写了 toString() 方法,例如我们熟悉的打印数组和对象。所以判断类型必须使用 Object.prototype 上的 toString 方法,而不是当前数据本身的 toString

为什么最后要加 call() 呢?

因为 Object.prototype.toString() 返回的是调用者的类型。不论你 toString() 本身的入参写的是什么,在Object.prototype.toString() 中,他的调用者永远都是 Object.prototype;所以,在不加 call() 情况下,我们的出来的结果永远都是 [object Object]

这里的 call() 方法, 是为了改变 Object.prototype.toString 这个函数中的 this 指向。让 Object.prototype.toString 这个方法的 this 指向我们所传入的数据。

对比

typeof instanceof constructor Object.prototype.toString
可用性 可以判断基本数据类型 instanceof 可以用于判断具有构造函数的引用数据类型 constructor能判断基本数据类型string、number、boolean和对象类型 Object.prototype.toString.call() 方法是判断类型的最准确的方法
局限性 对所有的引用类型都会返回object 无法判断不具有构造函数的基本数据类型、undefined 和 null 不能判断undefined和null 对象可以通过定义Symbol.toStringTag 属性来更改 Object.prototype.toString() 的行为

写到这里我还有一个小疑惑,为什么 instanceof 无法判断基本数据类型,但是 constructor 可以呢?原来JavaScript 在读取基本数据类型的 constructor 属性时,会隐式地将原始值封装成相应的包装对象,这种行为是 JavaScript 语言规范中定义的, 最后其实你访问到的是这个临时的包装对象上的属性。

实现一个 instanceof

在动手实现 instanceof 之前,我们要先知道它的功能是如何实现的:

  1. instancof 有两个参数,一个待判断的对象obj和一个构造函数constructor
  2. 如果 instancof 右侧的参数不是一个函数,它会报错 Right-hand side of 'instanceof' is not an object .
  3. 它检查obj是否为一个对象或函数,如果不是,则返回false,因为基础数据类型不具有构造函数。
  4. 获取obj的原型,并沿着原型链向上查找,直到找到与constructor.prototype相同的原型或者到达原型链的顶端。如果找到了相同的原型,则返回true,否则返回false

代码:

js 复制代码
function myInstanceOf(obj, constructor) {
    if (typeof constructor !== 'function') {
        throw new TypeError('Right-hand side of \'instanceof\' is not an object');
    }
    if (obj === null || typeof obj !== 'object' && typeof obj !== 'function') {
        return false;
    }
    let proto = Object.getPrototypeOf(obj);
    while (proto) {
        if (proto === constructor.prototype) {
            return true;
        }
        proto = Object.getPrototypeOf(proto);
    }
    return false;
}
相关推荐
rayufo几秒前
【工具】列出指定文件夹下所有的目录和文件
开发语言·前端·python
RANCE_atttackkk5 分钟前
[Java]实现使用邮箱找回密码的功能
java·开发语言·前端·spring boot·intellij-idea·idea
摘星编程44 分钟前
React Native + OpenHarmony:Timeline垂直时间轴
javascript·react native·react.js
2501_944525542 小时前
Flutter for OpenHarmony 个人理财管理App实战 - 支出分析页面
android·开发语言·前端·javascript·flutter
jin1233222 小时前
React Native鸿蒙跨平台完成剧本杀组队详情页面,可以复用桌游、团建、赛事等各类组队详情页开发
javascript·react native·react.js·ecmascript·harmonyos
李白你好2 小时前
Burp Suite插件用于自动检测Web应用程序中的未授权访问漏洞
前端
经年未远3 小时前
vue3中实现耳机和扬声器切换方案
javascript·学习·vue
刘一说3 小时前
Vue 组件不必要的重新渲染问题解析:为什么子组件总在“无故”刷新?
前端·javascript·vue.js
可触的未来,发芽的智生3 小时前
狂想:为AGI代称造字ta,《第三类智慧存在,神的赐名》
javascript·人工智能·python·神经网络·程序人生
徐同保4 小时前
React useRef 完全指南:在异步回调中访问最新的 props/state引言
前端·javascript·react.js