JavaScript 数据类型

378 阅读5分钟

JavaScript 数据类型分为 基础数据类型和引用数据类型

其中基础数据类型有:undefined、Null、Boolean、Number、 String、Symbol、BigInt

引用数据类型(Object)有:RegExp、Math、Date、Function、Array

JavaScript 的数据类型最后都会在初始化之后放在不同的内存中,因此基础数据类型和引用数据类型可以分为两类来进行存储:

  • 基础数据类型存储在 **栈内存 ,**在被引用或者拷贝的事就,会创建一个完全相等的变量;
  • 引用类型存储在**堆内存,**存储的是地址,多个应用指向同一个地址时会相互引用。

什么是栈内存?栈内存中的变量一般都是已知大小或者有范围上限的,算作一种简单的存储。包括 undefined、Null、Boolean、Number、 String、Symbol、BigInt。

什么是堆内存?堆内存存储的对象类型的数据,对于大小这方面,一般都是未知的。这也是为什么 null 作为 Object 类型的变量却存在栈内存中的原因。

如下图所示:

数据类型的检测

对于数据类型的检测一般有 typeof 、instanceof 等

1. typeOf 会返回一个字符串,表示未经计算的操作数类型。

typeof 1
typeof '1'
typeof null
typeof undefined
typeof truetypeof {}
typeof [];
typeof Symbol()
typeof function f() {}

输出结果如下图:

如上图所示:typeof 可以判断出 undefined、Boolean、Number、 String、Symbol 等数据类型,但是 null 的 typeof 是 Object ,这是 js 中 的一个 bug ,并不表示 null  就是引用类型,并且 null 本身也不是对象。因此 typeof 判断 null  返回的结果是有问题的,一定要注意,不能作为判断 null  的方法。

引用类型的数据,用  typeof 来判断的话,除了 function 是 OK 的,其他的都是 Object,是无法判断出来的。

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

 当我们 new 一个对象的时候,那么这个新的对象就是在它原型链上面的对象了,通过 instanceof 我们能够去判断这个对象是否是之前那个构造函数生成的对象,这样基本上就可以判断出这个新对象的数据类型。

const p = new Person();
console.log(p instanceof Person); // true
const str = '1111';
console.log(str instanceof String); // false
const str1 = new String('456');
console.log(str1 instanceof String); // true
console.log([] instanceof Array); // true
console.log([] instanceof Object); // true

上面就是 instanceof 方法去判断引用类型数据类型,下面我们自己实现一个 instanceof 。

function _instanceof(left, right) {
    // 1. 首先判断 left 是不是基础数据类型,如果是基础数据类型,直接返回 false
    if (typeof left !== 'object' || typeof left === null) {
        return false;
     } 
    // 2. 获取 left 的 proto  
    // const leftProto = left.__proto__; 
    const leftProto = Object.getPrototypeOf(left); 
    while (true) {
        // 3. 如果 leftProto 为 null,说明 left 为 Object.prototype , leftProto 就找到了原型链的末端,还未找到,直接发返回false
        if (leftProto === null) {
            return false;
        } 
        if (leftProto === right.prototype) {
            return true;
         }
        // 4. 如果没有找到,继续往原型链上层搜寻
        leftProto = Object.getPrototypeOf(leftProto);  
    }
}

插叙:

new 关键字会做出如下操作:

  1. 创建一个空对象 {};

  2. 链接该对象(设置该对象的 constructor)到另一个对象,其实也就是将新的空对象的 __proto__ 指向构造函数的 prototype;

  3. 将步骤 1 所创建的新的对象作为 this 的上下文,改变 this 的指向

  4. 如果该函数没有返回新的对象,则返回 this。

模拟 new  的实现,如下:

function _new(ctr, ...args) {
    // ctr 校验  
    if (typeof ctr != 'function') {
        throw 'ctr is must be a function';
    }  
    // 1. 创建一个新的对象
    const obj = {};  
    // 2. 链接该对象(设置该对象的 constructor)到另一个对象,其实也就是将新的空对象的 __proto__ 指向构造函数的 prototype;
    obj.__proto__ = Object.create(ctr.prototype);
    // 3. 改变 this 的指向
    const res = ctr.apply(obj, [...args]); 
    // 如果该函数没有返回对象,则返回this
    const isObject = typeof res === 'object' && typeof res != null;  \
    const isFunction = typeof res === 'function'; 
    return isObject || isFunction ? res : obj;
}

下面总结一下 typeof 和 instanceof 的区别

  1. instanceof 可以 准确的判断复杂引用数据类型,但是不能准确的判断基础数据类型
  2. typeof 虽然可以判断基础数据类型(null 除外),但是引用类型数据 中除了 function 外,其他的不能判断。

3. Object.prototype.toSring,该方法返回一个表示该对象的字符串。

每个对象都有一个 toString() 方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下,toString() 方法被每Object 对象继承。如果此方法在自定义对象中未被覆盖,toString()返回 "[object type]",其中 type 是对象的类型。

Object.prototype.toString({});
Object.prototype.toString.call({});
Object.prototype.toString.call(1);
Object.prototype.toString.call('1111');
Object.prototype.toString.call(null);
Object.prototype.toString.call(undefined);
Object.prototype.toString.call(true);
Object.prototype.toString.call(/258/g);
Object.prototype.toString.call(new Date());
Object.prototype.toString.call([]);
Object.prototype.toString.call(Symbol());
Object.prototype.toString.call(window);
Object.prototype.toString.call(document);
Object.prototype.toString.call(function () { });

从上面的代码可以看出 Object.prototype.toString.call() 可以很好地判断引用类型,甚至可以把 document 和 window 都区分开来,但是在写判断条件的时候一定要注意,使用这个方法最后返回统一字符串格式为 "[object Xxx]" ,而这里字符串里面的 "Xxx" ,第一个首字母要大写。

下面我们实现一个全局通用的可以判断数据类型的方法。

function getObjectType(obj) {
    // 判断是否是基础类型 
    if (typeof obj !== 'object') {
        return typeof obj;
    } 
    // 对于typeof返回结果是object的,再进行如下的判断,正则返回结果  
    return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1');
}