Javascript数据类型

JavaScript 数据类型及实用技巧

原始类型/基本类型

在 JavaScript 中,有 7 种原始类型,分别是:

  • boolean
  • null
  • undefined
  • number
  • string
  • symbol
  • bigint

引用类型/复杂类型

引用类型包括:

  • 对象(Object):由大括号 {} 包围的一组属性的集合。
  • 数组(Array):由方括号 [] 包围的一组有序的值的集合。
  • 函数(Function):可以执行特定操作的可重复使用的代码块。
  • 日期(Date):表示日期和时间的对象。
  • 正则表达式(RegExp):用于匹配和操作字符串的对象。
  • Map:一种可迭代的键值对集合。
  • Set:一种不重复值的集合。

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

基本类型的值存储在栈中。当我们把 a 赋值给 b 的时候(b = a),会开辟一块新的空间存储 b 的值。今后即使修改 a 的值,b 也不会改变。

引用类型存储的是对象的地址。假设把 obj1 赋值给 obj2,只要我们修改其中一个变量所引用的对象时,其他引用该对象的变量也会受到影响。

如何判断一个变量类型,在不用 typeof 情况下

1
2
3
4
console.log(Object.prototype.toString.call([1,2,3]));
// 输出为 [object Array]
console.log(Object.prototype.toString.call({abc:1}));
// 输出为 [object Object]

0.1+0.2等于多少

0.1+0.2 = 0.30000000000000000004
这是因为计算机以二进制存储,0.1这些数二进制是无限循环数,而为了舍弃后面的数,计算机会根据舍去第一位是1 or 0来判断非舍弃最后一位是否进位。

判断数组的四种方法

1
2
3
4
console.log(Object.prototype.toString.call(b))//[object Array]
console.log(b.__proto__ === Array.prototype)//true
console.log(Array.isArray(b))//true   es6
console.log(b instanceof Array)//true

console.log(['1','2','3'].map(parseInt)) 输出是什么?

1
2
3
4
5
6
7
// 输出为 1 NaN NaN
parseInt('1',0)//参数:‘1’,进制为默认十进制(0)
// 返回为1
parse('2',1)
// 返回结果:NaN(无效的一进制数字)
parseInt('3',2)
// 返回结果:NaN(无效的二进制数字)

undefinednull 的区别

  • undefined 表示变量已声明但未初始化。
  • null 表示变量的值为“无”,即没有指向任何对象。

isNaNNumber.isNaN 区别

  • isNaN 会先将参数转换为数字,然后再检查是否为 NaN
  • Number.isNaN 则直接检查参数是否为 NaN,不会进行类型转换。

对象的浅拷贝有哪些?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const obj1 = {a:1,b:2}

const obj2 = Object.assign({},obj1);

console.log(obj2)//{a:1,b:2} 第一种方式

const obj3 = {...obj1}

console.log(obj3)//{a:1,b:2} 第二种方式


const obj4={}

for(let key in obj1){
    if(obj1.hasOwnProperty(key)){
        obj4[key] = obj1[key]

    }

console.log(obj4)//{a:1,b:2} 第三种方式

const obj5 = {}

Object.keys(obj1).forEach(key=>{

   obj5[key] = obj1[key]

})

console.log(obj5)//{a:1,b:2} 第四种方式

数组的浅拷贝

1
2
3
4
5
6
7
8
9
const arr1 = [1,2,3]

const arr2 = arr1.slice()

console.log(arr2)//[1,2,3] 第一种方式

const arr3 =[].concat(arr1)

console.log(arr3)//[1,2,3] 第二种方式

深拷贝

  • 使用 JSON.parse(JSON.stringify())(简单但有局限性):

    1
    let b = JSON.parse(JSON.stringify(a));

    注意:这种方法不能处理函数、undefinedDateRegExp 等特殊类型,也不能处理循环引用,也不能复制对象中函数,不能复制原型链上的属性,会忽略 symbolundefined 属性

  • 使用递归递实现(更通用):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    function deepClone(source, clonedMap = new Map()) {
    if(typeof source !== 'object' || source === null) {
    return source;
    }

    //如果这个对象已经被拷贝过,直接从Map中返回
    if(clonedMap.has(source)) {
    return clonedMap.get(source);
    }

    let target;

    if(Array.isArray(source)) {
    target = [];
    }
    else if (source instanceof Date) {
    target = new Date(source);
    }
    else if (source instanceof RegExp) {
    target = new RegExp
    }
    else{
    target = {}
    }

    //在Map中记录这个对象
    clonedMap.set(source, target);
    for(const key in source) {
    if(Object.hasOwnProperty.call(source, key)) {
    if(typeof source[key] === 'object'&& source[key] !== null) {
    target[key] = deepClone(source[key], clonedMap);
    }
    else {
    target[key] = source[key];
    }
    }
    }

    //克隆Symbol属性
    const symbolKeys = Object.getOwnPropertySymbols(source);
    for(const symbolKey of symbolKeys) {
    target[symbolKey] = deepClone(source[symbolKey], clonedMap);
    }

    return target;
    }

    let b = deepClone(a);

for...infor...of 的主要区别

特性 for...in for...of
用途 遍历对象的可枚举属性(键名) 遍历可迭代对象的值
适用对象 普通对象 数组、字符串、MapSet 等可迭代对象
遍历顺序 不保证顺序 保证顺序
是否访问原型链 会访问继承的属性(需结合 hasOwnProperty 不访问继承的属性
是否适用于数组 不推荐(会遍历非数字键和继承属性) 推荐(不会遍历非数字键和继承属性)