原型链解决对象属性查找,作用域链解决变量查找;前者沿__proto__向上至object.prototype,后者沿词法作用域向上至全局作用域。

原型链和函数作用域解决的是两类完全不同的查找问题:一个管“对象找属性”,一个管“函数找变量”。它们不共享同一套规则,也不互相影响。
原型链:对象属性的向上委托查找
当你写 obj.method() 或 obj.prop,而 obj 自身没有这个属性时,JavaScript 就启动原型链查找:
- 先检查
obj自身是否有该属性(实例属性) - 没有就查
obj.__proto__(即构造函数的prototype) - 还没找到,继续查
obj.__proto__.__proto__,直到抵达Object.prototype - 最终到
Object.prototype.__proto__ === null停止,找不到就返回undefined
这个过程和函数在哪定义、在哪调用完全无关——只取决于对象的 __proto__ 链。比如 new Person() 的实例,无论在全局还是嵌套函数里调用它的方法,走的都是同一条原型链。
函数作用域:变量的词法嵌套查找
当你写 console.log(x),JS 要确定 x 是谁,就启动作用域链查找:
- 从当前函数的词法环境(Lexical Environment)开始找
- 没找到就跳到外层函数定义时所在的词法环境(不是调用时的位置)
- 逐层向外,直到全局作用域(
window或globalThis) - 全程基于代码书写结构,与对象、构造函数、
new无关
哪怕同一个函数被多次调用,只要它是在 outer() 里定义的,每次执行时都会固定沿 outer → 全局 这条路径找变量,不会因为调用位置变化而改变。
关键区别一目了然
查什么? 原型链查对象的 属性和方法;作用域链查执行上下文中的 变量和函数声明。
靠什么连? 原型链靠 __proto__ 和 prototype 链接;作用域链靠函数定义时形成的嵌套关系(词法环境引用)。
顶点是谁? 原型链终点是 Object.prototype;作用域链终点是全局对象。
是否可跨? 对象不能访问函数作用域里的变量(比如 obj.x 不会去 foo() 里找 x);函数也不能通过原型链拿到父级作用域变量(bar() 不能靠 __proto__ 访问 foo 的局部变量)。
一个典型混淆场景辨析
看这段代码:
function Foo() {
this.name = 'Foo';
}
Foo.prototype.say = function() {
console.log(this.name); // 这里的 this.name 查的是对象属性,走原型链起点,但 name 值来自实例本身
};
function bar() {
const msg = 'hello';
const f = new Foo();
f.say(); // 输出 'Foo' —— say 执行时,this 指向 f,f.name 存在,不触发原型链上溯找 name
}
注意:say 方法本身来自原型链,但方法内部访问的 this.name 是实例属性查找,不是作用域查找;而 msg 在 say 内部根本不可见——它不在作用域链上,say 也不知道 bar 的存在。











