小宋爱睡觉 小宋爱睡觉
首页
  • HTML
  • CSS
  • JavaScript
  • Vue
  • React
  • 计算机网络
  • 浏览器原理
  • 性能优化
  • 设计模式
手写系列
  • 字符串
  • 数组
  • 链表
  • 树
  • 动态规划
  • 排序算法
  • GitHub (opens new window)
  • JueJin (opens new window)
首页
  • HTML
  • CSS
  • JavaScript
  • Vue
  • React
  • 计算机网络
  • 浏览器原理
  • 性能优化
  • 设计模式
手写系列
  • 字符串
  • 数组
  • 链表
  • 树
  • 动态规划
  • 排序算法
  • GitHub (opens new window)
  • JueJin (opens new window)
  • 数据类型
    • 介绍一下js的数据类型和区别
    • 数据检测方法有哪些
    • 为什么Object.toSting和Object.prototype.toString.call()返回的结果不一样呢
    • 判断数组的方法
    • null和undefined的区别
    • 实现一个instanceof
    • 为什么0.1 + 0.2 !== 0.3 如何让他相等
    • ["1", "2", "3"].map(parseInt)?
    • 如何定义 a,使得 a == 1 && a == 2 && a == 3 为真
    • typeof NaN是什么
    • isNaN和Number.isNaN函数的区别
    • ==的强制类型转换规则
    • 其他值是怎么转字符串的
    • 其他值转number的规则
    • 其他值转Boolean的规则
    • Object.is()和 == 和 ===的区别
    • 什么是js的包装类型
    • 介绍一下JS是如何进行隐式转换的
      • +操作符
      • - * ``操作符
    • ==操作符
    • 为什么有BigInt提案
    • Object.assign和扩展运算符是深拷贝还是浅拷贝 两者区别
  • ES6
  • JS基础
  • 执行上下文/ 闭包/ 作用域链
  • 原型和原型链
  • 情景题
  • JavaScript
Crucials
2021-11-27

数据类型

# 介绍一下js的数据类型和区别特别重要

七种基本数据类型:undefined null string number bigInt boolean symbol

一种引用数据类型:object (包含普通对象-Object,数组对象-Array,正则对象-RegExp,日期对象-Date,数学函数-Math,函数对象-Function)

其中es6新增的是symbol和bigint

  • symbol代表创建后独一无二不可变的数据类型 为了解决可能出现的全局变量冲突

  • bigint是一种数字类型的数据 可以表示任何精度格式的整数,可以安全存储操作大整数,可以超过number能够表示的安全整数范围

他们分别存放在

  • 栈:原始数据类型(undefined null boolean number string)

    • 占据空间少 大小固定 属于频繁被使用数据 所以放在栈中
    • 栈区内存由编译器自动分配释放 存放函数的参考值 局部变量的值
  • 堆:引用数据类型(object array function)

    • 占据空间大 大小不固定 引用数据在栈中存储了指针指向堆中实体的起始地址
    • 由开发者分配释放 若开发者不释放程序结束时由垃圾回收机制回收
  • 堆:JavaScript的闭包

    • 在编译过程中,如果 JavaScript 引擎判断到一个闭包,会在堆空间创建换一个“closure(fn)”的对象(这是一个内部对象,JavaScript 是无法访问的),用来保存闭包中的变量

# 数据检测方法有哪些特别重要

typeof

其他解析都正确 除了数组 对象 null Regexp Date会被判断成object

值得注意的是

typeof function() {} === 'function'

为什么null会被解析成object呢

原因

在最初的js是32位操作系统,所有值都存储在32位的单元里,每个单元包含一个小的类型标签(0-3bit)以及当前要存储值的真实数据,类型标签存储在每个单元的低位

000: object   - 当前存储的数据指向一个对象。
  1: int      - 当前存储的数据是一个 31 位的有符号整数。
010: double   - 当前存储的数据指向一个双精度的浮点数。
100: string   - 当前存储的数据指向一个字符串。
110: boolean  - 当前存储的数据是布尔值。
1
2
3
4
5

对象的标签值为000,而null则是空指针全是0,所以typeof null 是object

对象被赋值了null 以后,对象对应的堆内存中的值就是游离状态了,GC 会择机回收该值并释放内存。 因此,需要释放某个对象,就将变量设置为 null,即表示该对象已经被清空,目前无效状态

instanceof 可以判断对象的类型,内部机制是在其原型链上是否找到该类型的原型

只能正确判断引用类型 不能判断基本数据类型

引申:

可以判断一个对象在其原型链上是否存在一个构造函数的prototype属性

值得注意的是

将数字转成包装类型

let a = new Number(12)

a instanceof Number // true

此外

undefined instanceof undefined和 null instanceof null会报错

Uncaught TypeError: Right-hand side of 'instanceof' is not an object

constructor

都能判断出来,其作用一是判断数据类型

二是对象实例通过constructor对象访问他的构造函数,如果创建一个对象来改变他的原型 constructor就不能判断他的数据类型了

Object.prototype.toString.call()

# 为什么Object.toSting和Object.prototype.toString.call()返回的结果不一样呢

因为toString是object的原型方法,而array function 作为object的实例,重写了toString方法(function返回内容为函数体的字符串 array返回的是元素促成的字符串),若想得到对象的具体类型 应该调用object原型上的toString

# 判断数组的方法特别重要

  1. 通过Object.prototype.toString.call()
Object.prototype.toString.call(obj).slice(8,-1) === 'Array';
1
  1. 通过原型链判断
obj.__proto__ === Array.prototype
1
  1. es6的isArray
Array.isArray(obj)
1
  1. 通过instanceof
obj.instanceof Array === true
1
  1. 通过Array.prototype.isPrototypeOf
Array.prototype.isPrototypeOf(obj) === true
1

# null和undefined的区别特别重要

  • 都是基本数据类型

  • undefined代表未定义 null代表空对象,一般变量声明还没赋值或一个函数没有返回值是undefined,null主要是赋值给一些可能返回对象的变量作为初始化

  • undefined在js中不是一个保留字 可以作为变量名 哈哈哈但是会影响对undefined值的判断 可以通过void 0来获得安全的undefined值

  • undefined == undefined undefined === undefined null === null null == null

  • 在验证null时,一定要使用 === ,因为 ==无法分别null 和 undefined

# 实现一个instanceof

原理是判断构造函数的prototype是否出现在对象的原型链上

function myInstance(left, right) {
  // 获取对象的原型
	let proto = Object.getPrototypeOf(left)
  
  // 判断构造函数的prototype对象是否在对象的原型链上
  while(true) {
  	if(!proto) return false
    if(proto === right.prototype) return true
    // 没找到就继续在其原型链向上找
    proto = Object.getPrototypeOf(proto)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 为什么0.1 + 0.2 !== 0.3 如何让他相等特别重要

先解决如何让其相等

  1. (0.1 + 0.2).tofixed(2)
  2. (0.1 * 10 + 0.2 * 10) / 10

因为计算机是通过二进制的形式存储数据 0.1的二进制是0.000 1100 1100...(1100循环) 0.2的二进制是0.00 1100 1100...(1100循环),js遵循IEEE 754标准,使用64位固定长度表示(标准的双精度浮点数),最多保留52位,加上前面的个位 就是保留53位有效数字,剩余的舍去 那么0.1 + 0.2相加再转换成十进制就是 0.30000000000000004

img

  • 第一部分(蓝色):用来存储符号位(sign),用来区分正负数,0表示正数,占用1位
  • 第二部分(绿色):用来存储指数(exponent),占用11位
  • 第三部分(红色):用来存储小数(fraction),占用52位

# ["1", "2", "3"].map(parseInt)?重要

[1, NaN, NaN]因为 parseInt 需要两个参数 (val, radix),其中radix 表示解析时用的基数

下面我们来分析一下['1', '2', '3'].map(parseInt);

1. parseInt('1', 0); // radix为0时,使用默认的10进制。
2. parseInt('2', 1); // radix值在2-36,无法解析,返回NaN
3. parseInt('3', 2); // 基数为2,2进制数表示的数中,最大值小于3,无法解析,返回NaN
parseInt('111', 2) // 7
1
2
3
4

# 如何定义 a,使得 a == 1 && a == 2 && a == 3 为真

let a = {
  i: 1,
  toString() {
    return this.i++;
  }
};

let i = 1;
Object.defineProperty(globalThis, 'a', {
  get() {
    return i++;
  }
});

let a = new Proxy({ i: 1 }, {
  get(target, key) {
    return target.i++;
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# typeof NaN是什么

NaN不是一个数字,NaN是一个警戒值(sentinel value 有特殊用途的常规值)用于指出数字类型的错误情况

typeof NaN是number

NaN是唯一一个非自反的值 NaN !== NaN为true

# isNaN和Number.isNaN函数的区别

  • 函数isNaN会尝试将参数转换为数值 任何不能转化为数值的都返回true
  • Number.isNaN先会判断传入参数是否为数字,如果是数字会判断是不是NaN不会进行数据类型转换

# ==的强制类型转换规则

如果类型不一样 会进行类型转换

  1. 首先判断类型是否相同 相同则直接比较

  2. 不相同就进行类型转换

    • 会对比是不是null和undefined 是的话返回true

    • 判断两者是不是string和number是的话字符串转换为number

  3. 判断两者是不是boolean和number是的话将boolean转换为number

    • 判断两者一方是不是Object另一方为string number symbol的一种 是的话将object转为原始类型在判断
'1' == {name: 'js'}
             ↓
'1' == '[object Object]'
1
2
3

# 其他值是怎么转字符串的

  • null和undefined boolean直接转

    • null -> 'null' undefined -> 'undefined' false -> 'false' true -> 'true'
  • number类型的直接转换 不过那些极小极大的会使用指数

  • symbol直接转换,但只允许显式强制类型转换 使用隐式会出错

  • 对普通对象来说 除非自行定义的tostring 否则会调用Object.prototype.toString()

# 其他值转number的规则

  • undefined 转为 NaN

  • null转为0

  • boolean中true转为1 false转为0

  • string的话先隐式转换(相当于调Number()) 如果包含非数字值的转换为NaN 空字符串为0

  • symbol不能转换为数字

  • 对象(包括数组)会首先被转换为相应基本类型的值 如果返回非数字的基本类型值,再遵循上面的规则强制转换为数字

  • 转换为相应基本类型的值的意思是 通过内部操作DefaultValue检查是否有valueOf()方法,如果有就返回基本类型值进行转换为number 没有就使用toString的返回值来...

  • 如果都没有 就报TypeError

# 其他值转Boolean的规则

undefined null false +0 -0 NaN都是false

其他都为true

# Object.is()和 ==和 ===的区别

  • 使用双等会进行数据类型的转换

  • 使用三等不会进行数据类型的转换 更为严格

  • 使用Object.is()大致和三等差不多 主要还是解决一些特殊情况例如Object.is(+0, -0) 是false; Object.is(NaN, NaN)是true的问题

# 什么是js的包装类型

基本类型

是没有方法和属性 为了便于操作基本类型的值 在调用基本类型的方法和属性时js会隐式转化为对象

const a = "abc";
a.length; // 3   js将abc转换为String('abc')
a.toUpperCase(); // "ABC"
1
2
3

可以使用valueof将包装类型倒转成基本类型

var a = 'abc'
var b = Object(a)
var c = b.valueOf() // 'abc'
1
2
3

看看下面一题

var a = new Boolean( false );
if (!a) {
	console.log("Oops"); // never runs
}

// 什么都不会打印 因为虽然传入的是一个false 但是false被包裹成包装类型就成为了对象 他的值并非false
1
2
3
4
5
6

# 介绍一下JS是如何进行隐式转换的

首先讲讲ToPrimitive方法 这是每个值隐含的方法 用来将值转换为基本类型的值

// 如果值为对象

/**
* @obj 需要转换的对象
* @type 期望的结果类型
*/
ToPrimitive(obj,type)
1
2
3
4
5
6
7
  1. 当type为number时

    • 调用object的valueof方法 如果为原始值则返回 否则进行下一步

    • 调用object的toString方法 后续同上

    • 抛出TypeError

  2. 当type为string时

    • 调用toString方法 后续同上

    • 调用valueof方法

    • 抛出TypeError

两者最大的不同就是tostring方法和valueof的方法的先后不同

  • 如果对象为Date对象 则type默认为string

  • 其他情况默认为number属性

// 概括为
var objToNumber = value => Number(value.valueOf().toString())
objToNumber([]) === 0
objToNumber({}) === NaN
1
2
3
4

# +操作符

如果两边至少有一个为字符型的 那么两边都会被隐式转成字符串,其他情况两边都被转成数字

1 + '23' // '123'
 1 + false // 1 
 1 + Symbol() // Uncaught TypeError: Cannot convert a Symbol value to a number
 '1' + false // '1false'
 false + true // 1
1
2
3
4
5

# - * \操作符

优先将值转换为数字NaN也是数字

1 * '23' // 23
 1 * false // 0
 1 / 'aa' // NaN
1
2
3

# ==操作符

两边的值都尽量转成number

3 == true // false, 3 转为number为3,true转为number为1
'0' == false //true, '0'转为number为0,false转为number为0
'0' == 0 // '0'转为number为0
1
2
3

看一道题

var a = {}
a > 2 // false
// 对比结果如下
// a.valueOf() // {}, 上面提到过,ToPrimitive默认type为number,所以先valueOf,结果还是个对象,下一步
// a.toString() // "[object Object]",现在是一个字符串了
// Number(a.toString()) // NaN,根据上面 < 和 > 操作符的规则,要转换成数字
// NaN > 2 //false,得出比较结果

// 又比如
var a = {name:'Jack'}
var b = {age: 18}
a + b // "[object Object][object Object]"
1
2
3
4
5
6
7
8
9
10
11
12

总结一下

除了加法操作符以外的,两边都会优先转换成number类型

# 为什么有BigInt提案

JavaScript中Number.MAX_SAFE_INTEGER表示最⼤安全数字,即在这个范围内不会出现精度丢失的问题(小数除外),官方提出BigInt来解决此问题

# Object.assign和扩展运算符是深拷贝还是浅拷贝 两者区别

扩展运算符

let outObj = {
  inObj: {a: 1, b: 2}
}
let newObj = {...outObj}
newObj.inObj.a = 2
console.log(outObj) // {inObj: {a: 2, b: 2}}
1
2
3
4
5
6

Object.assign

let outObj = {
  inObj: {a: 1, b: 2}
}
let newObj = Object.assign({}, outObj)
newObj.inObj.a = 2
console.log(outObj) // {inObj: {a: 2, b: 2}}
1
2
3
4
5
6

可以看出两者都是浅拷贝

  • Object.assign接收的第一个参数作为目标对象,后面的参数作为源对象 然后将所有源对象合并到目标对象中 他修改了一个对象 触发了ES6的setter
  • 扩展操作符 数组或对象中每一个值都会被拷贝到新的数组或对象中 不复制继承的属性或类的属性 但是会复制ES6的symbols属性
上次更新: 2025/06/08, 23:39:58
ES6

ES6→

Copyright © 2021-2025 粤ICP备2021165371号
  • 跟随系统
  • 浅色模式
  • 深色模式