Files
DefectingCat.github.io/source/_md/你不知道的JavaScript-中.md
DefectingCat 062e3304ac 填坑
1. 挖坑
2. TypeScript编程
3. webpack静态网站开发
4. Nodejs多进程
5. TypeScript零碎笔记
2021-06-07 21:42:46 +08:00

114 lines
4.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 值与引用
在许多编程语言中赋值和参数传递可以通过值赋值value-copy或者引用复制reference-copy来完成。
例如在 C 中,传递一个引用值可以通过声明类似于这样的`int* num`参数来按引用传递,如果传递一个变量 x那么`num`就是指向 x 的引用。引用就是指向变量的指针如果不声明为引用的话参数值总是通过值来传递的。即便是复杂的对象值也是如此C++)。
与 C/C++ 不同的是JavaScript 没有指针这一概念值的传递方式完全由值来决定。JavaScript 中变量不可能成为指向另一个变量的指针。
基本类型(简单类型)的值总是通过以值复制的方式来赋值/传递,这些类型包括:`null``undefined`、字符串、数字、布尔和`symbol`
而复合值,也就是对象(以及对象的子类型,数组、包装对象等)和函数,则总是以引用复制的方式来赋值/传递。
在了解了基本类型和引用类型的值之后,先来看下他们传递有什么不同:
基本类型:
由于基本类型是按值传递的,所以 a 与 b 是分别在内存中两处保存了自己的值。a 有在内存中有自己的空间b 也有自己单独的空间,他们互不影响。
```js
let a = 123;
let b = a; // 按值进行传递
a += 1; // 修改 a
console.log(a); // 124
console.log(b); // 123 b 不受影响
```
引用类型:
引用值的情况正好相反,所谓按引用传递,就是`arr1``arr2`指向的是内存中的同一块地址,**修改**任何一个变量的值,都会立即反应到另一个变量上。因为他们对应的是同一块内存。
```js
let arr1 = [1, 2, 3];
let arr2 = arr1;
arr1.push(99); // 修改 aar1
console.log(arr1); // [ 1, 2, 3, 99 ]
console.log(arr2); // [ 1, 2, 3, 99 ]
```
但是引用值还有个特性容易犯错,那就是修改:
这里咋一看是修改了`arr1`的值,但为什么没有反应到`arr2`身上呢?说好的一起变呢?
仔细回想一下引用值的定义,他们是因为指向同一块内存地址,所以修改这段地址中的值时,就会同时反应在两个变量上。但是这里的`arr1 = { name: 'xfy' }`并不是修改内存中的值,而是修改了`arr1`的指向,使其指向一块新的内存地址。而`arr2`还是指向以前的地址,所以`arr2`没有改变。
```js
let arr1 = [1, 2, 3];
let arr2 = arr1;
arr1 = { name: 'xfy' };
console.log(arr1); // { name: 'xfy' }
console.log(arr2); // [ 1, 2, 3, 99 ]
```
### 使用函数修改值
由于上述值的传递特性,这也会导致在传递给函数参数时发生个中问题。
修改引用值:
```js
function changeValue(value) {
// 按引用传递,可以直接修改
value.push(99);
// 重新赋值,并没有修改内存中的值
value = { name: 'xfy' };
}
let arr = [1, 2, 3];
changeValue(arr);
console.log(arr); // [ 1, 2, 3, 99 ]
```
修改基本值:
```js
function changeValue(value) {
// 按值传递value 获取到 num 的值
// 但是他们分别保存在两个内存中
value++;
}
let num = 123;
changeValue(num);
console.log(num); // 123
```
这也就是为什么在 Vue3 的 Composition API 中使用 ref 封装的响应式变量必须要有`.value`属性。
![按引用传递与按值传递](../images/%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript-%E4%B8%AD/pass-by-reference-vs-pass-by-value-animation.gif)
## 强制类型转换
将值从一种类型转换为另一种类型通常称为类型转换type casting这是显式的情况隐式的情况称为强制类型转换coercion
也可以这样来区分类型转换发生在静态类型语言的编译阶段而强制类型转换则发生在动态类型语言的运行时runtime
### 抽象值操作
在了解强制类型之前我们需要先掌握类型之间转换的基本规则。ES5 规范第 9 节定义了一些“抽象操作”和转换规则。
#### ToString
ToString 负责非字符串到字符串的强制类型转换操作。
基本值转换为字符串的规则为直接添加双引号null 转换为`"null"`true 转换为`"true"`等。数字也遵循这种规则,不过极大或极小的数字使用指数形式。
```js
(1.07 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000).toString()
// "1.07e+21"
```