proto 原始
英 美[ˈproʊtə]
prototype 和 __proto__ 的出现,正是 JavaScript 原型链的核心设计。
让我详细解释这个看似”混乱”但实际非常精妙的设计:
1. 根本原因:JavaScript 语言设计
JavaScript 中只有 函数对象才拥有 prototype 属性,而所有对象都拥有 __proto__ 属性。这是JavaScript语言的规定,不是 Vue 特有的。
2. 三者的本质区别
| 对象类型 | 是否有 prototype |
是否有 __proto__ |
原因 |
|---|---|---|---|
| Vue 构造函数 | ✅ 有 | ✅ 有 | 函数对象天生有 prototype,用于定义实例共享方法 |
| Vue 实例对象 | ❌ 无 | ✅ 有 | 实例是普通对象,只需通过 __proto__ 继承方法,无需自己定义原型 |
| Vue 原型对象 | ❌ 无 | ✅ 有 | Vue.prototype 本身也是对象,通过 __proto__ 继承 Object.prototype |
3. 内存结构可视化
4. 具体代码验证
// 1. Vue 构造函数(函数对象)
console.log(typeof Vue); // "function"
console.log(Vue.hasOwnProperty('prototype')); // true(函数特有)
console.log(Vue.__proto__ === Function.prototype); // true(继承Function原型)
// 2. Vue 原型对象(普通对象)
console.log(typeof Vue.prototype); // "object"
console.log(Vue.prototype.hasOwnProperty('prototype')); // false(非函数,无prototype)
console.log(Vue.prototype.__proto__ === Object.prototype); // true(继承Object原型)
// 3. Vue 实例对象(普通对象)
const vm = new Vue();
console.log(typeof vm); // "object"
console.log(vm.hasOwnProperty('prototype')); // false(实例无prototype)
console.log(vm.__proto__ === Vue.prototype); // true(继承Vue原型)
5. 设计哲学解析
为什么函数有 prototype?
function Vue() {} // 构造函数
// prototype 的用途:定义实例共享的蓝图
Vue.prototype.$mount = function() { /* 所有实例共享 */ };
Vue.prototype.$watch = function() { /* 所有实例共享 */ };
const vm = new Vue();
vm.$mount(); // 通过原型链找到共享方法
设计理由:函数作为”构造函数”时,需要 prototype 来存储所有实例共享的方法模板。
为什么实例没有 prototype?
const vm = new Vue();
// 实例是"产品",不需要生产其他对象
console.log(vm.prototype); // undefined
// 实例只需继承方法,无需定义新原型
console.log(vm.__proto__ === Vue.prototype); // true
设计理由:实例是终端的”产品对象”,不再用于创建其他对象,故无需 prototype。
6. 完整的原型链验证
const vm = new Vue();
// 完整的原型链
console.log(vm.__proto__ === Vue.prototype); // true
console.log(Vue.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
// 构造函数本身的继承链
console.log(Vue.__proto__ === Function.prototype); // true
console.log(Function.prototype.__proto__ === Object.prototype); // true
7. 实际内存布局
内存地址 0x100: [Vue 构造函数]
- prototype: 0x200(指向原型对象)
- __proto__: 0x300(指向Function.prototype)
内存地址 0x200: [Vue.prototype 原型对象]
- $mount: function() {}
- $watch: function() {}
- __proto__: 0x400(指向Object.prototype)
内存地址 0x500: [vm 实例对象]
- _data: {...}(实例自有数据)
- __proto__: 0x200(指向Vue.prototype)8. 与 ES6 class 的对比
ES6 class 是语法糖,底层仍是原型继承:
class Vue {
constructor() { this._data = {}; }
$mount() { /* 方法 */ }
}
// 等价于
function Vue() {
this._data = {};
}
Vue.prototype.$mount = function() {};
const vm = new Vue();
console.log(vm.__proto__ === Vue.prototype); // true(原理相同)
9. 总结回答
Vue 构造函数有
prototype- 因为它是函数对象,需要定义实例的共享蓝图
Vue 实例只有
__proto__- 因为实例是普通对象,只需继承方法,无需生产新对象
Vue 原型对象有
__proto__- 因为
Vue.prototype本身也是对象,需要继承Object的原型方法
- 因为
核心记忆点:
- 函数:
prototype(定义蓝图) +__proto__(继承功能) - 对象:只有
__proto__(纯消费者) - 设计目的:实现高效的方法共享和内存优化
这种设计确保了 JavaScript 的对象系统既灵活又高效,是语言的核心特性之一。
