今天在刷力扣 [146. LRU 缓存](https://leetcode.cn/problems/lru-cache/) 的时候,遇到了原型链的写法,想想这个写法我正式开发中从来都没有用过,到底是个什么玩意?遂将各个节点和变量都定义在外面,但是代码居然报错啦!
问了哈吉米大人后才知道代码逻辑没问题,但由于力扣每次测试的时候全局定义的变量是不会重置的,导致数据残留,因此看到了原型链写法,了解之后才知道它就是通常说的 class 写法,只不过 JavaScript 语言早期的缺陷没有考虑到,只能用原型链代替;
但还好,ES6 还是加上了 class。class 本质上只是"语法糖",它的底层运行机制依然是我们刚刚讲过的"原型链"。
在普通的面向对象语言(比如 Java/C++)里,有明确的 class(类)概念。但在早期的 JavaScript(ES6 之前)里,是没有真正的 class 的,大家只能用函数来"客串"类。
一、 new 操作符的"四步魔法"
在构造函数中,
this永远指向那个正在被new创造出来的新实例对象本身。
1、 凭空创建: 在内存中创建一个全新的、空的 JavaScript 对象(例如 let obj = {};)。
2、 基因绑定(链接原型): 将这个新对象的隐藏属性 __proto__ 指向构造函数的 prototype(共享包)。
3、 灵魂附体(绑定 this): 将构造函数内部的 this 强行指向这个刚刚创建的新对象,并执行函数体内的代码(为新对象添加各种属性)。
4、 自动交货: 函数执行完毕后,自动返回这个填满属性的新对象(除非你的函数手动返回了另一个对象)。
二、 原型 (prototype) 与 原型链
为什么需要原型?
为了节省内存。如果把所有方法(如 get、put)都写在构造函数内部,每次 new 一个新实例,内存中就会复制一份全新的函数代码。
核心概念拆解
prototype(显式原型 / 共享技能包): 每个函数出生时自带的属性。我们把所有实例共用的方法挂载在这里(如 LRUCache.prototype.get = ...)。
__proto__(隐式原型 / 寻址网线): 每个实例对象自带的隐藏属性。它指向创造它的那个构造函数的 prototype。
原型链的工作机制
当调用 cache.get() 时:
Step 1:JS 引擎先在 cache 这个对象自己的肚子里找 get 方法。
Step 2:找不到,就顺着 cache.__proto__ 爬到 LRUCache.prototype(共享包)里去找。
Step 3:如果共享包里还没有,继续顺着网线往上爬,直到找到顶级对象 Object.prototype。如果还是没有,就返回 undefined 或报错。这就是原型链。
原型链关系示意图
下面通过 Mermaid 流程图直观展示 cache 实例、LRUCache.prototype 和 Object.prototype 之间的原型链关系:
顶级原型 (Object.prototype)
构造函数原型 (LRUCache.prototype)
实例对象 (cache)
proto 指向
proto 指向
proto 指向
cache 实例
自身属性: capacity, cache, head, tail...
LRUCache.prototype
共享方法: get(), put(), ...
Object.prototype
基础方法: toString(), hasOwnProperty(), ...
null
图例说明:
- cache 实例: 通过
new LRUCache(capacity)创建的具体对象,包含实例特有的属性 - LRUCache.prototype: 构造函数的原型对象,存储所有实例共享的方法
- Object.prototype: JavaScript 中所有对象的最终原型,提供基础方法
__proto__链接: 实例通过__proto__属性指向其构造函数的原型,形成原型链
当调用 cache.get() 时,JavaScript 引擎会沿着这条链向上查找:
- 先在
cache自身查找get方法 - 找不到 → 通过
cache.__proto__找到LRUCache.prototype - 在
LRUCache.prototype中找到并执行get方法 - 如果还找不到,继续向上到
Object.prototype,最终到null
三、 this 的指向法则
黄金定律:谁调用了它,this 就指向谁!(看点 . 的左边)
- 默认调用(独立函数):
func()没有点调用,this默认指向全局对象(浏览器环境是window,严格模式下是undefined)。 - 隐式绑定(方法调用):
obj.func()点左边是obj,func内部的this瞬间指向obj。 - new 绑定(构造调用):
new Func()this指向全新被构造出来的实例对象。