殷实基础-JavaScript 数据类型

吐槽君 分类:javascript

一、数据类型概念

image.png

值得注意的一点:null 从原始类型中被拆出来了,我在1月26 写认识TypeScript的时候看还没拆出来,*我也比较好奇他为什么会拆出来,这里,求指教

  1. 基础类型存储在栈内存,被引用或拷贝时,会创建一个完全相等的变量;

  2. 引用类型存储在堆内存,存储的是地址,多个引用指向同一个地址,这里会涉及一个“共享”的概念。

为什引用类型要存储在堆中?

因为一般引用数据类型占据的空间都很大,栈的空间比较小,引用数据类型在堆中都是以字符串的形式存储的,只有当调用的时候才会被解析成代码来执行,并开辟一个私有的栈空间,执行结束后销毁这个栈空间,然后就又变成了一堆字符串

二、数据类型的检测

  • typeof
  • instanceof
  • Object.prototype.toString()

1. typeof

typeof 1; // "number"
typeof '1'; // "string"
typeof true; // "boolean"
typeof 1n; // "bigint"
typeof Symbol(); // "symbol"
typeof undefined; // "undefined"
typeof null; // "object"
 
typeof []; // "object"
typeof {}; // "object"
typeof function fn() {}; // "function"
 

这虽然 typeof null 会输出 object,虽然现在标准把 null 当成引用类型,但说回来这是 JS 存在的一个悠久 Bug,所以初学者还是当成基础数据类型记得好,并且 null 本身也不是对象。

总结:

typeof 可以判断 null 除外的所有基础数据类型,引用类型除了 function 其他都无法判断

2. instancof

通过 instanceof 我们能判断这个对象是否是之前那个构造函数的实例对象,这样就基本可以判断出这个新对象的数据类型。

image.png

instanceof 判断主要是通过原型链,判断当前实例对象的原型链是否有和构造函数相同的原型对象。

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

手写 instanceof

function Instanceof(left, right) {
  // 判断 left 是否为基础数据类型
  if (typeof left !== "object" || left === null) {
    return false;
  }
  // 拿到实例对象的原型对象
  let proto = left.__proto__;
  // 循环向上查找
  while (true) {
    if (proto === null) { // 判断是否到达原型链的顶端
      return false;
    } else if (proto === right.prototype) { // 原型对象是否相同
      return true;
    }
    // 指向上一个的原型对象
    proto = proto.__proto__;
  }
}
 
console.log(Instanceof(new Number(123), Number)); // true
console.log(Instanceof(123, Number)); // false
console.log(Instanceof(new String(123), Number)); // false
 

3. Object.prototype.toString

toString() 是 Object 的原型方法,调用该方法,可以统一返回格式为 “[object Xxx]” 的字符串,其中 Xxx 就是对象的类型。Object 对象可以直接调用,其他对象可以通过 call 改变 this 指向来调用;

Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call(new Date()); // "[object Date]"
Object.prototype.toString.call(/123/); // "[object RegExp]"
Object.prototype.toString.call(document); // "[object HTMLDocument]"
Object.prototype.toString.call(window); // "[object Window]"
 

Object.prototype.toString.call() 可以准确判断数据类型,不过要注意,它返回的描述类型的字符串是以大写字母开头

全局通用的数据类型判断方法

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

三、数据类型的转换

关于数据类型的转换,我看过的几本书和专栏都有提到,也写的很细,但我还是记不住,所以这里只写一些常用的,和有点刁钻的。

凡是通过逻辑运算符 (&&、 ||、 !)、运算符 (+、-、*、/)、关系操作符 (>、 <、 <= 、>=)、相等运算符 (==) 或者 if/while 条件的操作,如果遇到两个数据类型不一样的情况,都会出现隐式类型转换。

1. '+' 的隐式类型转换规则

  • 如果其中有一个是字符串,另外一个是 undefined、null 或布尔型,则调用 toString() 方法进行字符串拼接;如果是纯对象、数组、正则等,则默认调用对象的转换方法会存在优先级,然后再进行拼接。

  • 如果其中有一个是数字,另外一个是 undefined、null、布尔型或数字,则会将其转换成数字进行加法运算,对象的的话调用 object 的 valueOf/toString 方法进行转换。

  • 如果其中一个是字符串、一个是数字,则按照字符串规则进行拼接。

  • 如果是 Infinity + Infinity,则结果是 Infinity

  • 如果是 -Infinity + -Infinity,则结果是 -Infinity

  • 如果是 Infinity + -Infinity,则结果是 NaN

其它运算符的规则与此相似,Number、parseInt,parseFloat, Boolean, String 等强制转化可以自己试试。

2. Object 的转换规则

对象转换的规则,会先调用内置的 [ToPrimitive] 函数,其规则逻辑如下:

  • 如果部署了 Symbol.toPrimitive 方法,优先调用再返回;

  • 调用 valueOf(),如果转换为基础类型,则返回;

  • 调用 toString(),如果转换为基础类型,则返回;

var obj = {
  value: 1,
  valueOf() {
    return 2;
  },
  toString() {
    return '3'
  },
  [Symbol.toPrimitive]() {
    return 4
  }
}
console.log(obj + 1); // 输出5
// 因为有Symbol.toPrimitive,就优先执行这个;
// 如果Symbol.toPrimitive这段代码删掉,则执行valueOf打印结果为3;
// 如果valueOf也去掉,则调用toString返回 "31" (字符串拼接)
10 + {}
// "10[object Object]",注意:{}会默认调用valueOf是{},不是基础类型继续转换,调用toString,返回结果"[object Object]",于是和 10 进行'+'运算,按照字符串拼接规则来
[1,2,undefined,4,5] + 10
// "1,2,,4,510",注意[1,2,undefined,4,5]会默认先调用 valueOf 结果还是这个数组,不是基础数据类型继续转换,也还是调用toString,返回"1,2,,4,5",然后再和10进行运算,
 
var a = {
  value: 0,
  valueOf: function() {
    this.value++;
    return this.value;
  }
};
// 注意这里a又可以等于1、2、3
console.log(a == 1 && a == 2 && a ==3);  //true 规则 Object隐式转换
 
console.log([] == false); // true 这里数组回调用 toString,转换成字符串

if ([]) { // 而这里空数组会被当成 true,执行下面的代码, 又是为什么那?
    console.log('1');
}
 

这里要记住所有对象强制转换成布尔值都为 true, 即使是 new Boolean(false),null 除外

image.png

回复

我来回复
  • 暂无回复内容