JS 数据类型你了解多少?

JS 数据类型你了解多少?

数据类型概念

JavaScript 的数据类型有下图所示的 8 种:

数据类型大致可以分成两类来进行存储:

  1. 基础类型存储在栈内存,被引用或拷贝时,会创建一个完全相等的变量;
  2. 引用类型存储在堆内存,存储的是地址,多个引用指向同一个地址,这里会涉及一个“共享”的概念。

题目一:初出茅庐

let a = {
  name: 'lee',
  age: 18
}

let b = a;

console.log(a.name); // 第一个console 'lee'
b.name = 'son';
console.log(a.name); // 第二个console 'son'
console.log(b.name); // 第三个console 'son'

在执行了 b.name='son' 之后,会发现 a 和 b 的属性 name 都是 'son',第二个和第三个打印结果是一样的,这里就体现了引用类型的“共享”的特性,即这两个值都存在同一块内存中共享,一个发生了改变,另外一个也随之跟着变化。


题目二:渐入佳境

let a = {
  name: 'Julia',
  age: 20
}

function change(o) {
  o.age = 24;
  o = {
  name: 'Kath',
  age: 30
  }
  return o;
}

let b = change(a); // 注意这里没有new
console.log(b.age); // 第一个console
console.log(a.age); // 第二个console

通过上述代码可以看到第一个 console 的结果是 30,b 最后打印结果是 {name: "Kath", age: 30};第二个 console 的返回结果是 24,而 a 最后的打印结果是 {name: "Julia", age: 24}

原因在于:函数传参进来的 o,传递的是对象在堆中的内存地址值,通过调用 o.age = 24确实改变了 a 对象的 age 属性;12 行把参数 o 的地址重新返回了,将 {name: "Kath", age: 30} 存入其中,最后返回 b 的值就变成了 {name: "Kath", age: 30}。而如果把 return o 去掉,那么 b 就会返回 undefined


数据类型检测

第一种判断方法:typeof

typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof null // 'object'
typeof [] // 'object'
typeof {} // 'object'
typeof console // 'object'
typeof console.log // 'function'

虽然 typeof null 会输出 object,但这只是 JS 存在的一个悠久 Bug,不代表 null 就是引用数据类型,并且 null 本身也不是对象。如果需要在 if 语句中判断是否为 null,直接通过 ‘===null’ 来判断就好。


第二种判断方法:instanceof

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

语法

object instanceof constructor

参数

object 某个实例对象,constructor 某个构造函数

// 定义构造函数
function C(){}
function D(){}

var o = new C();

o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototype

o instanceof D; // false,因为 D.prototype 不在 o 的原型链上

o instanceof Object; // true,因为 Object.prototype.isPrototypeOf(o) 返回 true
C.prototype instanceof Object // true,同上

C.prototype = {};
var o2 = new C();

o2 instanceof C; // true

o instanceof C; // false,C.prototype 指向了一个空对象,这个空对象不在 o 的原型链上.

D.prototype = new C(); // 继承
var o3 = new D();
o3 instanceof D; // true
o3 instanceof C; // true 因为 C.prototype 现在在 o3 的原型链上

几个例子:

[] instanceof Array; //true
{} instanceof Object;//true
new Date() instanceof Date;//true
function Person(){};
new Person() instanceof Person;
[] instanceof Object; //true
new Date() instanceof Object;//tru
new Person instanceof Object;//true

从上面的例子中,我们发现虽然 instanceof 能够正确判断 [] 是 Array 的实例对象,但不能辨别 [] 不是 Object 的实例对象,为什么呢,这还需要从 js 的原型链说起,我们首先来分析一下 []ArrayObject 三者之间的关系,从instanceof判断能够得出:[].__proto__ ->Array.prototype, 而 Array.prototype.__proto__ 指向了Object.prototypeObject.prototype.__proto__ 指向了 null , 标志着原型链的结束。



手写 instanceof

function myInstanceof(left, right) {
  // 先用typeof来判断基础数据类型,如果是,直接返回false
  if (typeof left !== 'object' || left === null) {
  return false;
  }
  // getProtypeOf是Object对象自带的API,能够拿到参数的原型对象
  let proto = Object.getPrototypeOf(left);
  // 循环往下寻找,直到找到相同的原型对象
  while (true) {
    if (proto === null) {
      return false;
    }
    if (proto === right.prototype) {
    // 找到相同原型对象,返回true
      return true;
    }
    proto = Object.getPrototypeOf(proto)
  }
}

console.log(myInstanceof(new Number(123), Number)) // true
console.log(myInstanceof(123, Number)) // false

console.log([] instanceof Object) // true
console.log(myInstanceof([], Object)) // true

现在我们知道了两种判断数据类型的方法,那么它们之间有什么差异呢?我总结了下面两点:

  1. instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型;

  2. 而 typeof 也存在弊端,它虽然可以判断基础数据类型(null 除外),但是引用数据类型中,除了 function 类型以外,其他的也无法判断。

第三种判断方法:Object.prototype.toString

toString() 是 Object 的原型方法,调用该方法,可以统一返回格式为 “[object Xxx]” 的字符串,其中 Xxx 就是对象的类型。对于 Object 对象,直接调用 toString() 就能返回 [object Object];而对于其他对象,则需要通过 call 来调用,才能返回正确的类型信息。

Object.prototype.toString({})       // "[object Object]"
Object.prototype.toString.call({})  // 同上结果,加上call也ok
Object.prototype.toString.call(1)    // "[object Number]"
Object.prototype.toString.call('1')  // "[object String]"
Object.prototype.toString.call(true)  // "[object Boolean]"
Object.prototype.toString.call(function(){})  // "[object Function]"
Object.prototype.toString.call(null)   //"[object Null]"
Object.prototype.toString.call(undefined) //"[object Undefined]"
Object.prototype.toString.call(/123/g)    //"[object RegExp]"
Object.prototype.toString.call(new Date()) //"[object Date]"
Object.prototype.toString.call([])       //"[object Array]"
Object.prototype.toString.call(document)  //"[object HTMLDocument]"
Object.prototype.toString.call(window)   //"[object Window]"

从上面这段代码可以看出,Object.prototype.toString.call() 可以很好地判断引用类型,甚至可以把 document 和 window 都区分开来。

function getType(obj){
  let type  = typeof obj;
  if (type !== "object") {    // 先进行typeof判断,如果是基础数据类型,直接返回
    return type;
  }
  // 对于typeof返回结果是object的,再进行如下的判断,正则返回结果
  return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1');  // 注意正则中间有个空格
}

/* 代码验证,需要注意大小写,哪些是typeof判断,哪些是toString判断?思考下 */
getType([])     // "Array" typeof []是object,因此toString返回
getType('123')  // "string" typeof 直接返回
getType(window) // "Window" toString返回
getType(null)   // "Null"首字母大写,typeof null是object,需toString来判断
getType(undefined)   // "undefined" typeof 直接返回
getType()            // "undefined" typeof 直接返回
getType(function(){}) // "function" typeof能判断,因此首字母小写
getType(/123/g)      //"RegExp" toString返回
posted @   小小紫苏  阅读(91)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示