javascript属性访问通过原型链自下而上动态查找,闭包则使函数捕获并持久化词法环境;二者均依赖运行时链接维护,共同支撑对象行为与数据封装。

JavaScript 中的属性访问不是直接“查表”,而是按规则逐层向上查找;闭包则让函数能记住并访问它诞生时的环境。两者看似无关,实则共同支撑着 JS 的对象行为与数据封装能力。
原型链上的属性查找是动态的、自下而上的
当你读取一个对象的属性(比如 obj.name),JS 引擎会:
- 先检查 obj 自身是否拥有该属性(自有属性)
- 如果没有,就去 obj.__proto__(即它的原型对象)中找
- 还没找到,就继续顺着 __proto__ 向上,依次查找 Object.prototype,直到 __proto__ === null
- 全程找不到,返回 undefined,不会报错
这个过程不依赖定义时的结构,只依赖运行时的对象链接关系。所以修改原型上的方法,所有实例立刻可见;但若在实例上赋值同名属性(如 obj.name = 'new'),就会遮蔽原型上的值——这是“属性屏蔽”,不是覆盖。
原型继承容易导致引用类型共享问题
用 Child.prototype = new Parent() 实现继承时,所有 Child 实例共用同一个 Parent 实例作为原型。如果 Parent 构造函数里初始化了数组或对象(引用类型),它们就会被共享:
- 示例:Parent 中有 this.items = [],那么 child1.items.push(1) 后,child2.items 也会包含 1
- 根本原因:原型对象是共享的,而数组是引用值,多个实例指向同一块内存
- 解决方向:避免在原型上存放可变的引用数据;改用构造函数继承(call/apply)复制实例属性
闭包让函数“捕获”外部作用域变量,形成独立的数据环境
闭包不是语法结构,而是函数与其词法环境绑定后的结果。典型表现是:内部函数被返回或传出后,仍能访问外层函数的变量。
- 只要内部函数还存在(比如被赋给全局变量、传入定时器、作为事件回调),外层函数的局部变量就不会被回收
- 这可以模拟私有状态:例如 function createCounter() { let count = 0; return () => ++count; },每次调用 createCounter() 都生成一个独立的 count 变量
- 注意:滥用闭包(尤其在循环中未正确绑定变量)可能引发意料外的共享或内存滞留
原型链和闭包都依赖“作用域链”与“链接关系”的运行时维护
虽然原型链操作的是对象之间的 __proto__ 连接,闭包依赖的是执行上下文中的词法环境引用,但它们共享一个底层机制:JS 引擎必须在运行时持续维护这些隐式链接,并保证访问路径有效。
- 原型链靠 __proto__ 和 prototype 属性维系;闭包靠执行上下文(EC)和词法环境(LE)的嵌套引用维系
- 二者都支持“延迟求值”:属性访问或变量读取,都在真正执行时才确定来源
- 调试时,可通过 console.dir(obj) 查看原型链,用 debugger 或 DevTools 的 Scope 面板观察闭包变量
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











