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. 内存结构可视化

拥有prototype属性自身是对象 拥有__proto__顶层原型通过__proto__指向Vue构造函数Vue原型对象Object.prototypenullVue实例对象

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. 总结回答

  1. Vue 构造函数有 prototype

    • 因为它是函数对象,需要定义实例的共享蓝图
  2. Vue 实例只有 __proto__

    • 因为实例是普通对象,只需继承方法,无需生产新对象
  3. Vue 原型对象有 __proto__

    • 因为 Vue.prototype 本身也是对象,需要继承 Object 的原型方法

核心记忆点

  • 函数prototype(定义蓝图) + __proto__(继承功能)
  • 对象:只有 __proto__(纯消费者)
  • 设计目的:实现高效的方法共享内存优化

这种设计确保了 JavaScript 的对象系统既灵活又高效,是语言的核心特性之一。