JS类型判断—手写call和instanceof
前言
在JavaScript这片充满活力与变数的编程疆域里,数据类型犹如变幻莫测的魔法元素,赋予了代码生命与灵魂。它们或是温顺的字符串,低吟着字符的诗篇;或是数字的洪流,精确计算着宇宙的奥秘;亦或是神秘的对象,编织着复杂而有序的信息网。然而,在这魔法的施展过程中,辨识与操控这些数据类型的真身,便成为了每位JavaScript巫师必修的基本法术。
本文,便是一段专为探索与驾驭JavaScript类型判断艺术量身定制的奇幻旅程。我们将携手踏入这片既熟悉又时常令人困惑的领域,揭开每种类型背后的秘密面纱,掌握那些在编码征途中不可或缺的“辨形术”。从朴素的typeof
咒语,到进阶的instanceof
与构造函数比对,再到ES6新晋贵族Array.isArray
与Object.prototype.toString.call
的精妙运用,每一式每一招都蕴含着转变代码世界的无限可能。
准备好了吗?让我们一同启程,不仅仅是为了学会判断,更是为了理解每一种类型的核心本质,让我们的代码如诗般优雅,如剑般锋利。在这趟旅途中,你将发现,精准的类型判断不仅是解决问题的钥匙,更是通往高效编程与逻辑思维清晰化的神秘通道。现在,让我们一同步入JavaScript类型宇宙的深邃与广阔,开始这场既实用又充满乐趣的探索吧!
正文
数据类型
原始类型
- 数值Number
- 字符串String
- 布尔值Boolean
- Null
- Undefined
- Symbol
- BigInt
引用类型
- 对象Object
- 函数Function
- 数组Array
- Date
定义上面提到的所以数据类型
// 原始数据类型
let s = '123';
let n = 123;
let f = true;
let u = undefined;
let nul = null;
let sy = Symbol(123);
let b = BigInt(123);
// 引用类型
let obj = {};
let arr = [];
let fn = function () {};
let date = new Date();// date类型对象(特殊对象)
typeof
console.log(typeof(s));
console.log(typeof(n));
console.log(typeof(f));
console.log(typeof(u));
console.log(typeof(sy));
console.log(typeof(nul));// object --> 二进制前三位为0
console.log(typeof(obj));// object
console.log(typeof(arr));// object
console.log(typeof(fn));
console.log(typeof(date));// object
typeof结论
- 可以判断除 null 之外的所有原始类型
- 除了function其他所有的引用类型都会判断为object
- typeof是通过将值转换为二进制后判断其二进制前三位是否为0,是则为object
instanceof
console.log(obj instanceof Object);
console.log(arr instanceof Array);
console.log(obj instanceof Object);
console.log(date instanceof Date);
console.log(s instanceof String);// false
面试官叫你手写instanceof
- 首先我们要知道
arr.__proto__ = Array.prototype;Array.prototype.__proto__ = Object.prototype;
- 一直往上找原型,直到满足
=L.__proto__ === R.prototype
,同时将L.__proto__
赋值给L
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));// fals
instanceof结论
- 只能判断引用类型
- 通过原型链查找来判断类型
Object.prototype.toString.call
Object.prototype.toString(this),该方法只能判断对象类型
var a = {}
var b = new Date();
var c = function(){};
var d = [];
var e = 123;
console.log(Object.prototype.toString(a));
console.log(Object.prototype.toString(b));
console.log(Object.prototype.toString(c));
console.log(Object.prototype.toString(d));
console.log(Object.prototype.toString(e));
步骤 1.如果 this 值未定义, 返回“[对象未定义]” 2. 如果 此值为 null, 返回“[object Null]” 3. 将 O 作为 ToObject(this) 的执行结果 4. 定义一个class作为内部属性 [[class]] 的值 5. 返回由 "[object" 和 class 和 "]" 组成的字符串
Object.prototype.toString.call(xxx)
用来获取对象类型的标准且可靠的方法,利用了Object.prototype.toString
方法,并通过.call()
方法改变其内部的this
绑定,使其能够应用于任何对象,从而判断该对象的具体类型
var a = {}
var b = new Date();
var c = function(){};
var d = [];
var e = 123;
console.log(Object.prototype.toString.call(a));
console.log(Object.prototype.toString.call(b));
console.log(Object.prototype.toString.call(c));
console.log(Object.prototype.toString.call(d));
console.log(Object.prototype.toString.call(e));
手写call方法(改变任意函数的调用上下文(即函数内部的this
值))
- 类型检查: 首先,通过
typeof this !== 'function'
检查调用mycall
方法的主体(即this
指向的对象)是否为函数类型。如果不是函数,则抛出一个类型错误(TypeError
),提示调用者这不是一个函数。 - 创建唯一标识符: 使用
Symbol
创建一个独一无二的属性名fn
。这样做可以防止与对象中已存在的属性名冲突,保证临时添加的函数不会覆盖原有的属性。 - 将函数绑定到目标对象: 将当前函数(
this
,即想要改变调用上下文的函数)赋值给context
对象上的fn
属性。这样,context
对象就暂时拥有了这个函数,可以通过context[fn]
来调用它。 - 调用函数并利用隐式绑定: 接下来,通过
context[fn]()
执行这个函数。由于是在context
对象上调用的,根据JavaScript的函数调用规则,这里的this
会被绑定到context
上,实现了上下文的改变。 - 清理: 最后,通过
delete context[fn]
删除之前添加到context
对象上的临时函数,以保持原对象的清洁,不留下任何副作用。
Function.prototype.mycall = function(context) {
// 调用的哥们是不是函数体
if (typeof this !== 'function' ) {
return new TypeError(this + 'is not a function');
}
// this里面的this => context
const fn = Symbol('key');
context[fn] = this;// 让对象拥有该函数 {Symbol('key'): foo}
context[fn]();// 触发了隐式绑定
delete context[fn];
}
应用
var obj = {
a:1
}
function foo() {
console.log(this.a);
}
foo.mycall(obj);
Array.isArray
判断某个变量是否为数组类型的一个内置函数
let a = [];
let b = {};
let c = 123;
console.log(Array.isArray(a));
console.log(Array.isArray(b));
console.log(Array.isArray(c));
结语
类型判断不仅是技术的实践,更是一种编程哲学的体现,它提醒我们时刻关注数据的本质,理解每一个变量的内涵,从而在错综复杂的代码世界中游刃有余。让我们带着这份对类型深刻理解的智慧,继续在编程的浩瀚星海中探索、创造,书写更多技术与艺术完美交融的篇章。
转载自:https://juejin.cn/post/7369435074759360551