js数据类型从入门到精通

数据类型

  • 六种原始类型

    • undefined
    • Boolean
    • Number
      • NaN (不是一个有效数字)
      • Infinity (无穷大)
    • String
    • BigInt
    • Symbol
  • Null

  • Object

    • 普通对象
    • 数组对象
    • 正则对象
    • JSON 对象
    • 日期对象
    • Set
    • Map
  • Function

    • 普通函数
    • 箭头函数
    • 构造函数
    • 生成器函数

    引用类型(Object Function)

基本类型和引用类型的区别

基本类型:值存储在栈内存中

引用类型:栈内存中存储的实际是对象在堆内存的引用地址,实际值都存储在堆内存中

Number

NaN 不和任何类型相等

console.log(NaN === NaN); //false

isNaN([value]); // 检测一个是否是无效的数字(不是有效数字是true)

Infinity 无限大 -Infinity 无限小

  • 显示转换

    Number() / parseInt() / parseFloat()

  • 隐式转换

    数学运算(10 - "10" 等于 0) (10 - "10px" 等于 NaN)

    基于 == 比较的时候

    isNaN([value]) // 会先转化成数字再做判断

String

+号除了数学运算还会有字符串拼接

var m = 10;
var n = "10";

console.log(10 + n); // 一边是字符串并且+号两边都有那么就是字符串拼接
console.log(+n); // 只有一边 那么会被转化成数字
console.log(++n); // 两个加号,会转化成数字 并且自身+1
// i++ 和 ++i 和 i+=1  大部分情况相同  i++ 和 ++i 永远都是数学运算 当 i是字符串的时候 i+=1是字符串拼接

// 如果+两边 有对象,那么也有可能是字符串拼接
// 10 + {}  10[Object Object]
// 10+[10] 10[Object Object]
// 特殊
// 10 + new Number(10) // 20
// {} + 10 或者 {name:'11'} + 10  // 都等于10  因为{}没有参数运算,浏览器认为这是一个代码块
// let x = {} + 10 // [Object Object]10 从词法分析上都参与计算了

// 底层机制:对象在做数学运算的时候规则
// + 检测对象的Symbol.toPrimitive这个属性,如果有则基于这个运算,没有继续向下找
// + 检测对象的valueOf() 这个值是基本类型,如果有则直接参与计算,没有继续向下找
// + 获取对象的toString() 把其变成字符串 遇到+ 则拼接

console.log(10 + new Number(10)); // 20 new Number(10).valueOf

let obj = {
  [Symbol.toPrimitive]: function (hint) {
    return 10;
  },
};
console.log(10 + obj); // 20

Symbol 的应用场景

因为每个 Symbol 实例是唯一的,永远不会重复,可以作为

  • 使用 Symbol 来作为对象属性名(key)
  • 使用 Symbol 来替代常量
  • 使用 Symbol 定义类的私有属性/方法

使用方法

let x = Symbol(); //唯一值
let obj = {
  [x]: 10,
};
console.log(obj[x]); // 10

实际使用较少

BigInt 大数

当我们超出最大安全数字范围的时候,我们的计算就会不准确,这个时候就需要使用

  • BigInt([number])
  • XXXn

最大安全数 Number.MAX_SAFE_INTEGER
最小安全数 Number.MIN_SAFE_INTEGER

使用场景:在大型项目中,服务器返回给我们的数字中可能出现大数(服务器数据库中基于 longint 存储数,这个值可能会超过最大安全数)

检测数据类型有几种方式

  • typeof
  • instanceof
  • constructor
  • Object.prototype.toString

typeof 的使用

typeof 检测类型是有限的,只有(number boolean string undefined function symbol )

var a = "1";
typeof a; // string

var b = null;
typeof b; // object

typeof NaN; // number NaN 代表非数字 但是是number类型

为什么 typeof 检测 null(空指针) 是对象呢,

首先这是一个设计上的失误,因为 typeof 检测类型是对存储值的二进制进行检测,每一种类型都有固定标识,
1:整型(int)
000:引用类型(object)
010:双精度浮点型(double)
100:字符串(string)
110:布尔型(boolean)

恰好 null 二进制 前三位也是 000 所以 typeof 检测 null 就会是一个对象(object)

instanceof 的使用以及原理

instanceof 可以检测出引用类型 弥补了 typeof 的不足 但是 instanceof 也有缺陷
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

var str = "1";
str instanceof String; // false

var str = new String("11");
str instanceof String; // true
typeof str; // object
let person = function () {};
let no = new person();
no instanceof person; //true

因为我们可以随意修改原型的指向,所以 instanceof 检测是不准的

instanceof 是不能检测出基本类型的,但是经过 new 的基本类型是可以检测出来的,实际上 new 的过程把基本类型包装成了一个对象

当我们理解了 instanceof 实际上就是检测实例是否存在某个实例对象的原型链上,那么我们是不是可以模拟 instanceof 的实现呢

看一段代码

function newInstanceof(left, right) {
  left = left._proto_;
  var rightValue = right.prototype;
  while (true) {
    if (left === rightValue) {
      return true;
    }
    if (left === null) {
      return null;
    }
    left = left._proto_;
  }
}
// 主要是通过原型链和原型的知识进行判断,如果原型和原型链的知识不 熟悉,可以向下面查看,然后再回过头查看这端模拟的函数

constructor

constructor 主要是利用原型上的 prototype.constructor 指向实例的构造函数来进行判断的
先定义一个构造函数 Animal, 并 new 一个实例 dog

const Animal = function (name) {
  this.name = name;
}; // 声明一个构造函数
let dog = new Animal("dog"); // 生成实例dog

声明 Animal 函数的同时 js 会在 Animal 上挂载一个 prototype 对象,同时在 prototype 对象上会自动生成一个 constructor 属性并指向构造函数 Animal,相当于:
Animal.prototype.constructor === Animal // true ,根据原型链的查找原则,
console(dog.prototype) // Animal
所以利用构造函数原型上的 constructor 属性可以判断当前实例是否为当前构造函数的实例,进而确定数据所属类型:

console.log("1".constructor === String); // true
console.log(new Number(1).constructor === Number); // true
console.log(true.constructor === Boolean); // true
console.log(alert.constructor === Function); // true
console.log([].constructor === Array); // true
console.log(new Date().constructor === Date); // true

null, undefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。

constructor 检测也是不准的,因为我们可以随意的更改

var n = 1;
n.constructor === Number; // true
Number.prototype.constructor = "aa";
n.constructor === Number; // false

Object.prototype.toString

最标准的一种检测
可以返回当前实例的所属信息

// 定义判断类型函数
let getType = (target) => Object.prototype.toString.call(target);

console.log(getType("")); // [object String]
console.log(getType(2)); // [object Number]
console.log(getType(true)); // [object Boolean]
console.log(getType(undefined)); // [object Undefined]
console.log(getType(null)); // [object Null]
console.log(getType(Symbol())); // [object Symbol]
console.log(getType({})); // [object Object]
console.log(getType([])); // [object Array]
console.log(getType(alert)); // [object Function]
console.log(getType(new RegExp())); // [object RegExp]
console.log(getType(new Date())); // [object Date]

封装成一个函数

function toType(obj) {
  var class2Type = {};
  var toString = class2Type.toString; // => Object.prototype.toString() 他们两个是相等的
  // 设定数据映射表
  var data = [
    "Boolean",
    "Number",
    "String",
    "Function",
    "Array",
    "Date",
    "RegExp",
    "Object",
    "Error",
    "Symbol",
  ];
  data.forEach((name) => {
    class2Type[`[object ${name}]`] = name.toLowerCase();
  });
  if (obj == null) {
    return obj + "";
  }
  return typeof obj === "object" || typeof obj === "function"
    ? class2Type[toString.call(obj)] || "object"
    : typeof obj;
}

数据类型转化规则

数字

把其他类型转化成 Number 的规则

1.特定需要转化成 Number

  • Number()

  • parseInt/parseFloat

    2.隐式转化(浏览器内部默认转成 Number()计算)

  • isNaN() 会先转成数字再计算

  • 数学运算符(+ 在出现字符串的时候是拼接不是数学运算)

  • 在==比较的时候有些需要转化成数字比较

console.log("10px"); // NaN 只要出现非有效字符串 那么就是NaN
console.log(undefined); // NaN
console.log(null); // 0
console.log(Symbol(10)); // 报错
// parseInt 机制:从左侧第一个字符开始查找,查找有效数字字符(遇到非有效数字将停止查找,把找到的有效数字字符转化成数字,没有就是NaN,parseFloat只多识别一个小数点)
//

字符串

把其他类型转化成 String 的规则

  1. 能使用的方法

    • toString()
    • String()
  2. 隐式转化(一般都是调用 toString()转化)

  • 加号运算 如果一边是字符串,那么就是字符串拼接
  • 把对象转成数字,需要先用 toString()转成字符串再转成数字
  • 基于 alert/confirm/prompt 这些方式输出内容,都是先把内容转化成字符串,再输出

其他类型转化成字符串都很简单,只有{}普通对象是调取 toString(),而这个 toString 是调取 Object.prototype.toString(),这个不是用来转化成字符串,而是检测数据类型,返回结果"[Object Object]"

布尔值

1.基于一下方式可以把其他数据类型转化成布尔值

  • !转化成布尔值后取反

  • !!

  • Boolean()

    2.隐式转化

  • 在循环或者条件判断中,条件处理的结果就式布尔值

规则:只有 0 null NaN undefined 空字符串 会变成布尔值 false 其余全部式 true

==

在==比较的过程中,数据转换规则

类型一致

  • {} == {} false :对象比较的式堆内存地址
  • [] == [] false
  • NaN == NaN false

类型不一致

null=undefined 但是 === 就不相同因为类型不一致

字符串 == 对象 要把对象转成字符串

其余的 == 如果两边数据类型不一致,那么都要抓化成数字再比较

面试题

console.log([] == false); // true
// 对象 == 布尔  都是转化成数字(隐式转化)
// 对象转数字:先toString()转化成字符串,(应该是先基于valueOf获取原始值,没有原始值再去toString)然后再转成数字
// 执行过程:[] 没有valueOf没有基本类型的值,所以直接toString()
// [].toString() 等于 ''
// ""转成数字  Number('') // 0
// false 转成数字 是0 所以两者相等

console.log(![] == false); // true
// ![] 把数组转化成布尔值然后取反  false
// false == false
let res = 10 + false + undefined + [] + "Tencent" + null + true + {};
// 10+false = 10 false 会转化成0
// 10+undefined  NaN
// NaN + []  字符串"NaN"
// 后面都是字符串拼接了
// "NaNTencentnulltrue[Object Object]"
let arr = [10.18, 0, 10, 25, 23];
arr = arr.map(parseInt);
console.log(arr);

/*
arr = arr.map((item,index)=>{
  循环每一项都会触发回调函数
  每一次还会传递当前项和当前项的索引
})
parseInt 也是一个函数,所以接受当前项和当前索引

最后所有的都需要转化成十进制
parseInt('10.18',0)  第二位代表当前 value的进制 0就代表十进制
parseInt 只取到字符串有效的数字 所以 第一个是10 因为是十进制 所以 还是10
parseInt('0',1) // 没有1进制 进制只有2-36  因为没有1进制 所以为NaN
parseInt('10',2) // 2进制只有有数字 01 都是有效数字

10转化成 十进制

1*2^1+0*2^0  2 

parseInt('25',3)
三进制有效数字只有 0 1 2
所以只有2有效

2*3^0  2

parseInt('23',4)
四进制有效数字是0123
23都是有效数字

2*4^1+3*4^0  11

所以最终答案为 [10,NaN,2,2,11]

以上所有的 进制如果第一查找数字不符合进制有效数字,那么转化都为NaN
*/
parseInt(070)
// JS中遇到“以0开始的数字”,浏览器解析阶段 就会把其当做8进制,最后转换为十进制
//    0*8^0 + 7*8^1 + 0*8^2 =>56
// parseInt(56) -> parseInt('56',10) 

参考文献

  • MDN
  • https://juejin.cn/post/6844903957903441927
  • https://juejin.cn/post/6844903854492876814

你可能感兴趣的:(js数据类型从入门到精通)