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

JavaScript的类型和存储机制是开发者必须深入理解的重要概念,本文将详细探讨V8引擎的存储机制、以及类型判断的方法,并结合实际示例加以说明。

V8的存储机制

在聊类型判断之前,我们先来了解一下v8的存储机制。V8是Google开发的JS引擎,在Chrome浏览器和Node.js中广泛使用。理解V8的存储机制有助于我们更好地理解JS的内存管理。

原始类型的存储

原始类型包括number、string、boolean、undefined、symbol和bigint。这些类型的值直接存储在调用栈中。

let num = 200;
let str = "Hello, world!";

在上述代码中,num和str的值都直接存储在调用栈中。这种存储方式快速且高效,因为原始类型的数据量通常较小。

复杂类型的存储

复杂类型(如对象和数组)的值存储在堆结构中,并将堆中的引用地址存储在调用栈中。由于调用栈空间有限,而复杂类型的数据量可能非常大,如果将引用类型直接存储在调用栈中,很可能会导致栈溢出。

let obj = { name: "Jesper" };
let arr = [1, 2, 3];

在上述代码中,obj和arr的实际数据存储在堆中,而它们的引用地址存储在调用栈中。这种设计使得JavaScript可以处理大型和复杂的数据结构,而不会轻易导致栈溢出。而我们要怎么进行类型的判断呢

类型判断typeof运算符

typeof运算符用于判断数据类型,但它有一些限制和特殊情况。

判断原始类型

typeof可以判断除null之外的所有原始类型。

console.log(typeof 200); // 输出:number
console.log(typeof "Hello"); // 输出:string
console.log(typeof true); // 输出:boolean
console.log(typeof undefined); // 输出:undefined
console.log(typeof Symbol()); // 输出:symbol
console.log(typeof BigInt(123)); // 输出:bigint
console.log(typeof null); // 输出:object (这是一个JavaScript的设计缺陷)

typeof null返回object是JavaScript的一个历史遗留问题。尽管如此,开发者需要了解并记住这一点,以避免在实际编程中出现误判。

判断引用类型

typeof只能准确判断函数类型,对于其他引用类型,它会返回object。

console.log(typeof function() {}); // 输出:function
console.log(typeof {}); // 输出:object
console.log(typeof []); // 输出:object

typeof判断原理

typeof判断的原理是将值转换成二进制后检查其前三位是否为0。除了函数之外,所有引用类型的二进制前三位都是0,而null的二进制表示是全0,因此会被判断为object。

了解了typeof后,我们是不是可以手写一个函数判断一个变量类型是否为对象呢

手写typeof

function isObject(obj){
    if(typeof obj === 'object' && obj != null){
        return true
    }
    return false
}

要特别注意,因为null也会被typeof判断为对象,所以要加一个限制条件obj != null

instanceof运算符

js判断浏览器类型跳转__作为面试官最重要的是判断力

instanceof运算符用于判断一个对象是否是某个构造函数的实例,它通过检查对象的原型链来进行判断。

function Person() {}
let alice = new Person();
console.log(alice instanceof Person); // 输出:true
console.log(alice instanceof Object); // 输出:true

instanceof只能用于判断引用类型,对于原始类型,它会返回false。

console.log(42 instanceof Number); // 输出:false

同样,知道了instanceof是通过原型链来进行判断的,我们也可以自己手写一个instanceof函数

手写instanceof

function myInstanceof(L,R){
    while(L != null){
        if(L.__proto__ == R.prototype){
            return true
        }
        L = L.__proto__
    }
    return false
}

instanceof的局限性

instanceof在跨iframe或跨窗口时可能会失效,因为不同的环境具有不同的全局对象。此时,Object.prototype.toString通常是更好的选择。

Object.prototype.toString

Object.prototype.toString是一个强大的方法,可以准确判断各种数据类型。

调用方法

通过Object.prototype.toString.call()可以获取变量的准确类型。注意这里要加上call(),否则toString方法不能准确的作用在传入的参数上,call()的作用就是让Object原型上的toString方法的this指向传入的参数。还不了解this和call的掘友可以去看我之前有关this的文章。

console.log(Object.prototype.toString.call(200)); // 输出:[object Number]
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(null)); // 输出:[object Null]
console.log(Object.prototype.toString.call(Symbol())); // 输出:[object Symbol]
console.log(Object.prototype.toString.call(BigInt(123))); // 输出:[object BigInt]
console.log(Object.prototype.toString.call(function() {})); // 输出:[object Function]
console.log(Object.prototype.toString.call({})); // 输出:[object Object]
console.log(Object.prototype.toString.call([])); // 输出:[object Array]

Object原型上 toString的原理如果toString接收的值是undefined,则返回"[object Undefined]"。如果toString接收的值是null,则返回"[object Null]"。否则,调用ToObject(x)将x转为对象,此时对象内部一定拥有一个属性[[class]],该属性的值就是x的类型。设class是[[class]]的值。返回由"[object "和class以及"]"拼接得到的字符串。

虽然能准确的判断数据类型,但这个方法返回的字符串是不是有些“不干净”呢,我们可以来稍微再改进一下。

function type(x){
    return Object.prototype.toString.call(x).slice(8,-1)
}
console.log(type('hello'));//输出String
console.log(type(123));//输出Number
console.log(type([]));//输出Array

只需要用字符串自带的slice方法切割一下,看着是不是舒服多了。slice(start,end)方法可以传入两个参数,代表切割的起始下标和结束下标(左闭右开),如果第二个不填则是切到底。注意此方法不会改变原字符串,会返回一个新的字符串。

应用场景

Object.prototype.toString在需要准确判断变量类型,特别是判断引用类型(如Array、Date等)时非常有用。它也适用于跨iframe或跨窗口的情况。

Array.isArray

Array.isArray是判断一个变量是否为数组的标准方法,它只适用于数组类型。

console.log(Array.isArray([])); // 输出:true
console.log(Array.isArray({})); // 输出:false
console.log(Array.isArray("Hello")); // 输出:false

相比于typeof和instanceof,Array.isArray是判断数组类型的最佳方法,因为它不仅准确,而且不会受到跨窗口或框架的影响。

省流

V8引擎的存储机制告诉我们,原始类型的数据存储在调用栈中,而复杂类型的数据存储在堆中。typeof运算符提供了一种快速但不完美的类型检查方法,特别是对于原始类型。instanceof可以用于判断引用类型的实例关系,但在某些情况下不够准确。Object.prototype.toString是一种强大且可靠的类型检查方法,适用于各种数据类型。最后,Array.isArray是判断数组的标准方法,简单而有效。

希望本文能帮助读者更深入地理解这些概念,再次遇到需要类型判断的场景时可以准确的应用这些方法。