JavaScript的“基于原型”是运行时核心机制,非语法糖;动态性体现为对象结构、方法绑定、继承关系均可实时变更;原型链是属性查找的动态路径,隐式原型[[Prototype]]指向其原型,属性访问沿链向上查找直至null,且链路可随时修改。

JavaScript 的“基于原型”不是语法糖,而是语言运行时的核心机制;所谓“动态”,指对象结构、方法绑定、继承关系均可在运行中实时变更。理解它,关键在于跳出类式思维,直面对象、原型、构造函数三者间流动的关系。
原型链是属性查找的动态路径
每个对象内部都有一个隐式原型([[Prototype]]),它指向另一个对象——即其原型。当访问对象属性时,引擎不会止步于自身,而是沿原型链逐级向上查找,直到找到该属性,或抵达 null 终止。
这个过程完全在运行时发生,且链路可被修改:
- Object.setPrototypeOf(obj, newProto) 可随时重设对象的原型,改变后续所有属性查找路径
- 给 obj.__proto__ 赋值(非标准但广泛支持)等效于上述操作
- 修改原型对象上的属性或方法,所有继承它的实例会立即反映变化(因为查的是“活引用”,不是快照)
构造函数只是初始化函数,不是类型定义
function Person(name) { this.name = name; } 这行代码不声明“类”,只定义一个普通函数。它的特殊性仅来自两处:
- 被 new 调用时,自动创建新对象,并将该对象的 [[Prototype]] 指向 Person.prototype
- Person.prototype 是一个普通对象,默认带有一个 constructor 属性指向 Person —— 仅此而已,无编译期约束
你可以删除、覆盖、甚至让多个构造函数共享同一个 prototype,或让一个对象同时成为多个构造函数的实例(通过手动设置原型链)。类型检查、实例归属,全靠运行时原型链是否可达,而非声明时的“类名”。
动态扩展与“鸭子类型”天然共生
由于对象没有固定结构,任意对象都可在任意时刻添加/删除属性和方法:
- obj.newMethod = function() { ... } 直接挂载,仅影响该对象
- Obj.prototype.sharedMethod = function() { ... } 修改原型,影响所有继承链上的对象
- 甚至可把方法赋给基本类型包装对象(如 String.prototype),实现“伪原生扩展”
这种能力支撑了典型的 JavaScript 风格:不预设接口契约,而依赖“有某方法就能用”的鸭子类型。比如 Array.prototype.forEach 只检查 this 是否有 length 和数字索引,不关心它是不是真正的数组——于是类数组对象(如 arguments、NodeList)也能被借用。
ES6 class 是语法糖,但不可掩盖原型本质
class Person { constructor(n) { this.name = n; } say() { return this.name; } } 看似面向对象,实则完全编译为原型操作:
- Person 仍是函数,Person.prototype.say 存储方法
- extends 本质是调用 Object.setPrototypeOf(Sub.prototype, Super.prototype)
- super 在方法中展开为对原型上同名方法的显式调用
试图用 class 构建“强封装”或“不可变类型”会碰壁:prototype 可写、实例属性可删、私有字段(#field)仅靠语法限制,底层仍可反射绕过。真正可控的动态性,来自主动管理原型链与属性描述符(Object.defineProperty),而非依赖语法表象。
原型驱动不是历史包袱,而是赋予 JavaScript 灵活性与表达力的底层引擎。掌握它,意味着能看清框架如何代理、为何 polyfill 有效、怎样安全地做运行时增强——而不是把 new、this、prototype 当作魔法符号来背诵。Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











