看山是山,看山不是山,看山又是山。
代码
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
speak() {
console.log(`我叫${this.name},今年${this.age}岁`)
}
}
const p1 = new Person("张三", 22)
内存分布(抽象图)
【栈内存】 【堆内存】
───────────────────────────── ───────────────────────────────
变量名 → 引用地址 实际对象内容
Person ───────┐ Function 对象(类构造函数)
│ {
│ prototype: { constructor: f, speak: f }
▼ }
[0x001] ──────────────────────────────┐
│
p1 ──────────┐ │
│ ▼
▼ { name: "张三", age: 22 }
[0x002] --------------------→ __proto__ → Person.prototype
分析步骤
类 Person
- 本质是一个 构造函数对象(存在栈内存中,有引用指向堆内存的函数体)。
- 在内存中会自动生成一个 Person.prototype 对象,里面存放方法(如 speak)。
Person.prototype
- 存在于堆内存中。
- 存放实例共享的方法(speak)和一个默认的 constructor 属性(指回 Person)。
实例 p1
- 存在于堆内存中【实例的引用在栈,实例的实际数据在堆】,里面存放的是 独有的属性:{ name: “张三”, age: 22 }。
- 它的 proto(隐藏属性,标准叫 [[Prototype]])指向 Person.prototype。
方法查找
- 当执行 p1.speak() 时:
- 先在 p1 自己身上查找 speak → 找不到
- 顺着 proto 去 Person.prototype 找 → 找到并执行
栈和堆存什么?
- 栈:存放基本类型值、引用地址、执行上下文等。
- 堆:存放对象的实际内容(包括实例、原型对象、函数对象)。
代码打印验证
console.log(typeof Person) // "function" → 说明类本质是构造函数
console.log(Person.prototype) // { constructor: f, speak: f }
console.log(p1.__proto__ === Person.prototype) // true
console.log(p1.constructor === Person) // true
总结:
- 类本质是函数(存在栈内存里),它有一个自动生成的 prototype 对象(存在堆内存里)。
- 实例存在堆内存,存放独有属性;共享方法放在原型对象上。
- 实例通过 proto 链接到类的 prototype,形成 原型链。