JS值类型和引用类型

1、常见的值类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const u                     // undefined
const s = "string"          // string
const b = true              // boolean
const n = 100               // number
const y = Symbol("symbol")  // symbol
const i = BigInt(1)         // bigint

console.log(typeof u === "undefined")  // true
console.log(typeof s === "string")     // true
console.log(typeof b === "boolean")    // true
console.log(typeof n === "number")     // true
console.log(typeof y === "symbol")     // true
console.log(typeof i === "bigint")     // true

2、引用类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
const n = null(
  // 特殊的引用类型,指向空地址
  // 复合条件检测null类型
  !n && typeof n === "object"
); // true

const obg = { x: 100 }; // object
const arr = ["a", "b", "c"]; // object

// 特殊的引用类型 但不用于存储数据,所以没有“拷贝、复制拷贝函数”这一说
function fn() {}
// 怎么理解function也是引用类型
// 因为function fn(){}的实现是由以下几个步骤

let fn; // undefined
fn = function () {};
console.log(fn); // function

console.log(typeof n === "object"); // true
console.log(typeof fn === "function"); // true
console.log(typeof arr === "object"); // true

undeclared - 未申明

undeclaredundefined是两码事,没有定义的变量都是 undeclared(未申明),打印出的结果如下

1
console.log(p); // ReferenceError: p is not defined

如果使用typeof返回的是undefined,这是 javascript 一个特殊的安全防范,但是开发人员会把两者认为是一个内容,这是错误的理解

1
2
3
4
5
6
console.log(typeof p); // undefined

// 规避出现undeclared问题
if (typeof p !== "undefined") {
  // ...
}

3、引用类型和值类型区别

先看看例子就能理解引用类型和值类型区别 值类型:单独复制一份数据。原数据和新数据互不关联互不影响,修改原数据原数据会变,但是复制的新数据不会变更 引用类型:数据都是同一份。通俗一点理解为一张银行卡,你可以在支付宝、微信支付、云闪付绑定你的银行卡,但是不管在哪家消费,都是用的是同一张银行卡上的钱,支付宝、微信支付、云闪付理解为变量名,银行卡就是你要赋值给变量的数据。 机器上的理解引用类型,引用类型就是数据存入内存中,获取一个地址,其他变量都是指向这个地址 4、引用类型例子 下面的例子可以用于理解,这是一份简化后的权限目录 _arr 是后端拿到的数据,我们要筛选出一个树状结构目录,把没有关联起来的数据做归类和整理

 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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
const _arr = [
  {
    id: 1,
    data: 1,
    pid: "",
  },
  {
    id: 2,
    data: 2,
    pid: "",
  },
  {
    id: 3,
    data: 3,
    pid: "1",
  },
  {
    id: 4,
    data: 4,
    pid: "2",
  },
  {
    id: 5,
    data: 5,
    pid: "2",
  },
];

// 做个_arr字典
let b = 0;
const map = {};
while (b < _arr.length) {
  map[_arr[b].id] = _arr[b];
  b++;
}

console.log(map);
// map打印结果
/*
{ '1': { id: 1, data: 1, pid: '' },
  '2': { id: 2, data: 2, pid: '' },
  '3': { id: 3, data: 3, pid: '1' },
  '4': { id: 4, data: 4, pid: '2' },
  '5': { id: 5, data: 5, pid: '2' } }
*/

const val = [];
for (let i = 0; i < _arr.length; i++) {
  const parent = map[Number(_arr[i].pid)]; // 使用字典获取pid获取对应的父级目录数据
  if (parent) {
    (parent.children || (parent.children = [])).push(_arr[i]);
    // 简化内容
    /*
    if(parent.children){
    	parent.children.push(_arr[i])
    }else{
    	parent.children = []
      parent.children.push(_arr[i])
    }
    */
  } else {
    val.push(_arr[i]);
  }
}
val[0].data = "sdsd"; // 修改整理后数据

console.log(map);
console.log(_arr);
console.log(val);

/*
map
{ '1': { id: 1, data: 'sdsd', pid: '', children: [ [Object] ] },
  '2':
   { id: 2, data: 2, pid: '', children: [ [Object], [Object] ] },
  '3': { id: 3, data: 3, pid: '1' },
  '4': { id: 4, data: 4, pid: '2' },
  '5': { id: 5, data: 5, pid: '2' } }
_arr
[ { id: 1, data: 'sdsd', pid: '', children: [ [Object] ] },
  { id: 2, data: 2, pid: '', children: [ [Object], [Object] ] },
  { id: 3, data: 3, pid: '1' },
  { id: 4, data: 4, pid: '2' },
  { id: 5, data: 5, pid: '2' } ]
val
[ { id: 1, data: 'sdsd', pid: '', children: [ [Object] ] },
  { id: 2, data: 2, pid: '', children: [ [Object], [Object] ] } ]

*/
/*
val[0].data = "sdsd"
可以看到map、_arr、val都受到影响,说明他们是同一份数据
map['1'] 因为'1': { id: 1, data: 1, pid: '' }
是一个对象,对象是引用类型,所以val[0].data修改的是同一份数据
*/

上面的例子理解会有点难度,实际点的例子还有,比如 axios

1
2
3
4
5
6
7
8
9
axios
  .get("xxxx", {
    params: { key: "value" },
  })
  .then((res) => {
    // 假设res是通过后端返回json,并且是对象
    const data = res.data;
    // 基于data的增删改查都会影响res
  });

React 中的 redux,别人项目中会看到以下代码

1
JSON.parse(JSON.stringify(state)); // 深度拷贝

这是一个简单的拷贝,但是它的作用是规避引用类型,复制一份新的数据,避免使用原数据,这样做到新数据和原数据数据隔离,即复制了数据,又避免了数据的相互污染和修改

毕竟谁也不想自己一个操作,导致某个数据变动,然后全局都在用这个数据,最后产生无法回溯的 bug