原始值与引用值
- 无论什么值,必须要将其转换成二进制然后保存在内存中
- 不同类型的值占据的内存大小是不一样
- 变量是栈内存别名,用于存放引用/地址以及部分原始值
- v8引擎优化中,如果原始值能直接放入内存槽中(如小整型12),则将值直接放入,而不需要去堆上开辟内存,然后将值放入变量中
js中的变量是固定栈内存槽别名,基本上存储的就是值的引用,值在js中有七种类型,但是这七种类型在拷贝赋值时不一样,分为原始值和引用值:
- 定义
- 原始值:值会优先直接存入变量中,但如果存不下则放入堆中,变量也是存放引用,但是在性质和行为上与引用值不同,具体有以下类型Number、Boolean、String、Symbol、Undefined和Null的字面量
- 引用值:变量中存放的是指向堆内存的引用,如Object类型.
| 特性 | 原始值 | 引用值 |
|---|---|---|
| 数据类型 | string、number、boolean 等 | Object、Array、Function 等 |
| 可变性 | 不可变 | 可变 |
| 存储位置 | 堆/栈内存(内联优化) | 堆内存 |
| 变量存储内容 | 值本身(或堆地址,若需间接存储) | 堆内存地址 |
| 赋值行为 | 复制值,新旧独立 | 复制引用,共享对象 |
| 比较方式 | 比较值是否相同 | 比较引用是否指向同一对象 |
引用-
定义:变量别名
-
本质:也是指针,只是其行为和普通变量一致
-
引用并不占据内存,声明后必须立即赋值,之后不可更改
注意js中
变量存储的值是引用,cpp中变量是引用//cpp中引用完全就是别名,无论如何操作,两者都是同步的 #include <iostream> class MyClass { public: int value; MyClass(int val = 0) : value(val) {} // 构造函数,初始化value void display() const { // 成员函数,用于输出value std::cout << "Value: " << value << std::endl; } }; int main() { // 给obj赋值一个对象 MyClass obj(10); // 给obj一个引用 MyClass& ref = obj;//变量ref是引用,声明后立即赋值,且后续不可更改 ref.display(); // 输出 "Value: 10" // 给obj重新赋值一个对象 obj = MyClass(20); ref.display(); // 输出 "Value: 20",因为ref是obj的引用,所以会反映obj的最新状态 return 0; }//js中引用是变量的值,虽然引用不可变,但是变量的值是可变的 let a = {a:1};//a中存储的是对象{a:1}的引用 let ref = a;//将引用赋值给ref a = {a:2};//a中的值变为对象{a:2}的引用 console.log(ref.a);//但是ref中并没有改变
-
- 对引用值的操作
- 对引用值的操作,都是相当于对对象进行操作,如属性添加
- 引用值之间的传递(包括函数的传参)
let obj1 = {a:1}; let obj2 = obj1;//传递的是对象Object的引用
执行上下文与作用域
- 变量、函数都有上下文,通过
上下文才能知道他们能访问哪些变量和函数; - 每个上下文都有一个关联的
变量对象,这个上下文中定义的所有变量、函数都会存在于这个对象上,如全局的window对象,但是注意函数内部的变量对象是无法直接访问的; - 由于var变量提升,所以这个变量对象在开始运行时就已经将用var声明的函数、变量存入;
- 上下文中代码执行的时候,会创建变量对象的
作用域链:正在执行的上下文的变量对象位于作用域链最前端,然后是包含上下文,以此类推
var color = "blue";
function changeColor(){
let anotherColor = "red";
function swapColors(){
let tempColor = anotherColor;
anotherColor = color;
color = tempColor;
}
swapColors();
}
changeColor();
5. 作用域链增强:某些语句(try-catch中的catch/with)导致作用域链前端临时添加一个上下文,代码块执行完后会删除添加的上下文。如catch中,对作用域链增强以包含错误对象的声明与初始化。
try {
throw new Error("Something went wrong!");
} catch (e) {
console.log(e instanceof Error); // 输出:true,因为e是Error对象的一个实例
console.log(e.message); // 输出:"Something went wrong!",访问异常对象的message属性
console.log(e.name); // 输出:"Error",访问异常对象的name属性
console.log(e.stack); // 输出异常的调用栈
}
- 变量声明
- 如果在函数中直接使用没有声明的变量,则该变量会被添加到全局上下文中
- var:存在变量提升、且全局上下文中执行声明会被添加到window对象中
- let/const:存在变量提升,但由于临时性死区,可认为没有提升,且不允许声明多次;全局上下文中声明执行后变量并不会被添加到window对象中
// 全局作用域 var globalVar = 'I am a global variable using var'; let globalLet = 'I am a global variable using let'; const globalConst = 'I am a global variable using const'; console.log(window.globalVar); // 输出:'I am a global variable using var' console.log(window.globalLet); // 输出:undefined console.log(window.globalConst); // 输出:undefined
垃圾回收
- 基本思路:确定哪个变量不再使用,然后释放它占用的内存;
- 方法:
- 标记清理
- 标记内存中存储的所有变量
- 将所有在上下文中的变量,以及被在上下文中的变量引用的变量的标记去掉
- 清除有标记的变量
- 引用计数
- 每个
值都有引用值,声明时赋值为1 - 值被另一个变量值引用时+1,反之-1
- 为0时清理
- 每个
- 标记清理
- 内存泄漏
- 意外声明全局变量
- 定时器
- 闭包