原型链解决对象属性访问问题,作用域链解决变量名解析问题;前者通过__proto__向上查找属性,后者通过词法嵌套关系查找标识符,二者机制、触发时机与目标均不同。

原型链和函数作用域解决的是两类完全不相干的问题:一个管“对象有没有这个属性”,一个管“代码里写的那个变量名到底指谁”。它们名字都带“链”,又都“往上找”,但背后机制、触发时机、查找目标全都不一样。
原型链查的是对象身上的属性或方法
当你写 obj.name 或 arr.push() 这类点操作时,JS 就启动原型链查找:
- 先看 obj 自身有没有 name 属性;没有就去 obj.__proto__ 指向的对象上找
- 还没找到,就继续顺着 __proto__ 往上,查 Date.prototype → Object.prototype
- 最终到 Object.prototype.__proto__ === null 停止,找不到就返回 undefined
- 这个过程和函数在哪定义、在哪调用完全无关,只取决于对象的内部原型链接
函数作用域查的是变量名(标识符)
当你写 console.log(x) 或直接调用 myFunc() 时,JS 启动的是作用域链查找:
- 从当前函数执行时创建的作用域开始找 x;没找到就查它外层函数定义时所在的作用域
- 一层层往外,直到全局作用域(globalThis 或 window)
- 这个链条在函数写好时就固定了,跟运行时调用位置无关——这就是词法作用域
- 闭包能记住外部变量,靠的就是保留了这条作用域链的引用
关键区别一句话分清
写 obj.xxx 走原型链,写 xxx(单独出现)走作用域链。
- 原型链终点是 Object.prototype,再往上是 null
- 作用域链终点是全局对象(如 globalThis),变量最终会挂在这个对象上(比如 globalThis.x)
- 虽然全局变量 表现上 是全局对象的属性,但这只是运行时映射,不是设计逻辑等价
常见混淆点提醒
别因为 两者都“向上找”就以为它们是一回事:
- new Date().toISOString() 能用,是因为原型链找到了 Date.prototype 上的方法
- function foo() { console.log(a); } 中的 a 能访问外层变量,靠的是作用域链,和 Date.prototype 一毛钱关系没有
- 哪怕你把变量名起成 obj,它还是走作用域链;哪怕你把对象属性名起成 console,它也只走原型链











