快乐学习JavaScript中的类型判断

零 JavaScript教程评论121字数 5874阅读19分34秒阅读模式

快乐学习JavaScript中的类型判断

引言文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html

在JavaScript中,准确地判断变量的类型是编程中不可或缺的一部分。无论是进行数据验证、类型转换还是实现复杂的逻辑,掌握类型判断的方法都是非常重要的。文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html

本文将带你深入了解四种常用的类型判断方法:typeofinstanceofObject.prototype.toString.call 以及 Array.isArray。通过这些方法,你可以更精确地识别变量的类型,为你的JavaScript开发之路添砖加瓦。文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html


一、typeof

typeof 操作符用于判断变量的类型,尤其适用于原始类型。文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html

判断原始类型(除 null 之外)

首先,我们要知道原始类型目前一共有7种:文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html

js

代码解读
复制代码
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 大整型 超过2的53次方

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

image.png文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html

判断引用类型

我们再来看下面的对象判断出的类型是什么:文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html

js

代码解读
复制代码
let fn = function () { }
let obj = {}
let arr = []
let date = new Date()


console.log(typeof (fn));//function
console.log(typeof (obj));//object
console.log(typeof (arr));//object
console.log(typeof (date));//object

image.png文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html

可以看到对function和对象obj可以准确判断出,数组arr判断出的类型为object(但是数组也确实属于对象,说得过去),日期类型date也为object文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html

image.png文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17453.html

从上面我们可以总结出:

  • typeof 可以准确判断出的基本数据类型有:numberstringbooleanundefinedsymbolbigint 和 function。 对于 null和其他的引用类型,typeof null 返回 "object",这到底是怎么回事呢??

image.png

判断null返回 "object"???

这,其实是一个 JavaScript 的历史遗留下来的bug。

image.png

在 JavaScript 的早期版本中,值在底层是以二进制形式存储的,并且变量的类型是通过检查其二进制表示的前几位来确定的。如果前几位是 000,那么就判定为是对象类型。而null 的二进制值又全是0,那必然它的前几位也是 000,所以才被错误地判定为 object

虽然这个bug早已被认识到,但由于历史原因和兼容性考虑,这一行为在 JavaScript 中一直没有被修改。所以到现在typeof null 返回 "object" 被视为 JavaScript 语言的一个历史遗留问题。

二、instanceof

原始类型我们实现了精确判断(当然,除了null),那我们想要精确判断引用类型用什么呢?

没关系,它来了,它就是instanceof !这位选手可以准确检测出对象是否是某个构造函数的实例。

标准用法

js

代码解读
复制代码
let obj = {}; 
let arr = []; 
let date = new Date(); 
let fn = function() {};

console.log(obj instanceof Object); // true 
console.log(arr instanceof Array); // true 
console.log(date instanceof Date); // true 
console.log(fn instanceof Function); // true

image.png

“非标准用法”

如果用instanceof判断原始类型呢?试试!

试试就逝世

js

代码解读
复制代码
let s = '123' 
let n = 123 

console.log(s instanceof String);
console.log(n instanceof Number);

image.png

诶,都说了只能判断引用类型啦

动画30.gif

实现原理

在细说instanceof的实现原理之前,我们先来看看这段代码输出什么:

js

代码解读
复制代码
let arr = []

console.log(arr instanceof Object);

image.png

是的没错,数组属于对象,输出结果为true,正确的,客观的,中肯的。

这样我们大概就能推测出instanceof是通过原型链进行判断的

啥?不记得圆形脸啦?复习一下!

原型链的工作原理

在 JavaScript 中,每个对象都有一个内部属性 [[Prototype]](可以通过 __proto__ 访问)指向其构造函数的 prototype 对象。这个 prototype 对象本身也有一个 [[Prototype]],最终连接到 Object.prototype。这种链式结构形成了原型链。

嗦哒嘶内~

复习完了我们来看看这段:

image.png

  • p 属于 Person

检查对象 p 是否在 Person 的原型链上。由于 p 是通过 new Person() 创建的,p 是 Person 构造函数的实例,因此 p instanceof Person 返回 true

  • p 属于 Object

在 JavaScript 中,所有对象(包括通过构造函数创建的对象)都继承自 Object.prototype。由于 Person 构造函数本身也是一个对象,其原型链最终会连接到 Object 的原型链上。所以,p 也会在 Object 的原型链上。因此,p instanceof Object 返回 true

底层逻辑

知道了原理后,深入了解instanceof底层逻辑就不难了

js

代码解读
复制代码
function myInstanceof(L, R) {
  // 获取L的原型
  let proto = L.__proto__;
  // 获取R的prototype对象
  let prototype = R.prototype;
  // 判断对象是否是某个构造函数的实例
  while (proto !== null) {
    if (proto === prototype) {
      return true;
    }
    proto = proto.__proto__;
  }
  return false;
}

// 测试代码
console.log(myInstanceof([1], Array));//ture
console.log(myInstanceof({}, Array));//false  对象不是数组,就像四边形不是正方形
console.log(myInstanceof([], Object));//ture

image.png

小结

instanceof总结一下:

  1. 只能判断引用类型
    • instanceof 用于判断引用类型,不能用于判断原始类型。
  2. 通过原型链判断
    • instanceof 检查构造函数的 prototype 属性是否出现在对象的原型链上。所以instanceof才不能判断原始类型,原始类型人家压根没原型啊。

    ps.

    对象的原型叫对象原型,也叫隐式原型;

    函数的原型叫原型,也叫显示原型.

三、Object.prototype.toString.call()

Object.prototype.toString.call() 是一种更加精确的类型判断方法。

使用方法

js

代码解读
复制代码
Object.prototype.toString.call(123);          // "[object Number]"
Object.prototype.toString.call("hello");      // "[object String]"
Object.prototype.toString.call(true);         // "[object Boolean]"
Object.prototype.toString.call([]);           // "[object Array]"
Object.prototype.toString.call({});           // "[object Object]"
Object.prototype.toString.call(new Date());   // "[object Date]"

image.png

判断得非常精准

image.png

方法解释

这个方法在官方文档的位置为es5.github.io/#x15.2.4.2

好,全是英文,让我们打开翻译

image.png

还有点懵?没关系,我们看下面的步骤讲解

1.判断是否为 null 或 undefined

  • 对于 null 和 undefinedObject.prototype.toString 分别返回 "[object Null]" 和 "[object Undefined]"

例子:

js

代码解读
复制代码
Object.prototype.toString.call(null);       // "[object Null]"
Object.prototype.toString.call(undefined);  // "[object Undefined]"

2. 将其他值转换为对象并判断类型

  • 对于其他类型,Object.prototype.toString 通过内部属性 [[Class]] 返回一个格式为 "[object Type]" 的字符串,其中 Type 是对象的类型。

我们点击第3点里的ToObject跳转到页面:image.png

详细解释

1) 未定义 (Undefined) 如果传递的参数是 undefined,则引发 TypeError 异常。

2) 无效的 (Null)

如果传递的参数是 null,则引发 TypeError 异常。

3) 布尔值 (Boolean)

如果传递的参数是布尔值(如 true 或 false),则创建一个新的布尔对象(Boolean Object)。这个对象的 [[PrimitiveValue]] 内部属性被设置为布尔值本身。例如,ToObject(true) 会返回一个 Boolean 对象,其内部属性 [[PrimitiveValue]] 为 true

4) 数字 (Number)

如果传递的参数是数字(如 42),则创建一个新的 Number 对象。这个对象的 [[PrimitiveValue]] 内部属性被设置为数字值本身。例如,ToObject(42) 会返回一个 Number 对象,其内部属性 [[PrimitiveValue]] 为 42

5) 字符串 (String)

如果传递的参数是字符串(如 "hello"),则创建一个新的 String 对象。这个对象的 [[PrimitiveValue]] 内部属性被设置为字符串值本身。例如,ToObject("hello") 会返回一个 String 对象,其内部属性 [[PrimitiveValue]] 为 "hello"

6)对象 (Object)

如果传递的参数本身就是一个对象,则直接返回这个对象(无转换)。例如,ToObject({a: 1}) 会直接返回 {a: 1}

3. 显示绑定

  • 通常,Object.prototype.toString 直接调用时,其上下文对象 (this) 是 Object.prototype,返回的结果是 [object Object]。通过使用 call 方法,可以显式地指定 this 的值,从而准确判断传入变量的类型。

模拟(简单版)

js

代码解读
复制代码
Function.prototype.mycall = function(context, ...args) {
  // 确认调用的对象是否是函数
  if (typeof this !== 'function') {
    throw new TypeError(this + ' is not a function');
  }

  // 确保 context 不为 null 或 undefined,如果是,则设置为全局对象(在浏览器中为 window)
  context = context || globalThis;

  // 创建一个唯一的符号属性,以防止覆盖已有属性
  const fn = Symbol('key');
  // 将函数(this)作为属性添加到传入的对象(context)
  context[fn] = this;  
  // 调用该函数,并传递额外的参数
  const result = context[fn](...args);
  // 删除刚才添加的属性
  delete context[fn];
  // 返回函数的返回值
  return result;
}

// 测试代码
var obj = {
  a: 1
}
function foo(b, c) {
  console.log(this.a, b, c);
}
foo.mycall(obj, 2, 3);  // 输出:1 2 3
console.log(obj);  // 输出:{ a: 1 }

image.png

四、Array.isArray

Array.isArray() 是一个非常直接且简单的方法,它用于检查一个给定的值是否是 JavaScript 中的数组。如果该值是一个数组就返回 true ,否则返回 false

这种方法特别有用,因为它可以正确地区分数组和其他类型的对象,例如对象字面量或函数。

在早期版本的 JavaScript 中,人们通常使用 typeof 操作符或者 constructor 属性来尝试判断一个值是不是数组,但这两种方法都有其局限性。

局限性

typeof:返回的是 "object" 而不是 "array";

constructor:如果某个对象的原型链被修改过,那么 constructor 可能会被改变。

例子:

js

代码解读
复制代码
console.log(Array.isArray([])); // true 
console.log(Array.isArray({})); // false 
console.log(Array.isArray("hello")); // false 
console.log(Array.isArray(new Date())); // false
console.log(Array.isArray(function() {})); // false 
console.log(Array.isArray([1, 2, 3])); // true

image.png

零
  • 转载请务必保留本文链接:https://www.0s52.com/bcjc/javascriptjc/17453.html
    本社区资源仅供用于学习和交流,请勿用于商业用途
    未经允许不得进行转载/复制/分享

发表评论