先看示例:
const person = {
arms: 2,
legs: 2
}
// 通过 Object.create 将 person 设为 father 的原型
const father = Object.create(person)console.log(father) //{}
console.log(father.arms) //2
console.log(father. __proto__ === person) //true// Object.create 第二个参数可以设定属性
const son = Object.create(father, {
name: {
value: "xixi",
enumerable: true
}
})
console.log(son.__proto__ === father) //true
console.log(son.__proto__.__proto__ === person) //true对象自己没有的属性,就去原型上实时查找。
优势是简单,劣势是 Object.create 需要手动设置属性和初始化逻辑,不如new + 构造函数方便
为什么 new 更方便?new + 构造函数都做了什么?
function Computer (name, price){
// 1. 创建一个空对象 obj = {}
// 2. 把 obj.__proto__ 指向 Computer.prototype
// 3. 执行 Computer.call(obj, name, price)
// 4. 如果构造函数没返回对象,返回 obj
this.name = name
this.price = price
}
// 方法通常放 prototype 上
// 一万个实例共享一个方法,省内存。
// 更重要的是放 prototype 上的方法可以被继承链共享,放 this 上的不行。
Computer.prototype.showPrice = function() {
console.log(this.price)
}原型对象/构造函数/实例对象三者形成如图所示的三角关系。

每个对象可以通过 __proto__ 访问原型对象。
构造函数的 prototype 指向一个对象,该对象是实例对象的原型对象。
原型对象的 constructor 属性指向其构造函数。
打个比方:函数就像一家工厂:
- 工厂盖好的那天,旁边就自动配了一个展示柜(prototype),里面摆着样品(constructor 指向工厂自己)
- 工厂的流水线(new)每生产一个产品,就把这个产品连到展示柜上(__proto__ 指向 prototype)
- 产品放自己独有的东西(this.name),展示柜放共享的东西(方法)
上一个三角关系,我们明确了任何对象都有构造函数,且该对象的 __proto__ 指向构造函数的 prototype。
在此基础上我们扩展 4 个规则。
规则 1:任何函数的 __proto__ 都是 Function.prototype
因为函数也是对象,所以才有 __proto__,指向构造函数 Function 的 prototype,逻辑和上一个三角关系相同。
规则 2:任何普通对象的原型链终点都是 Object.prototype,再往上只有 null。
原型链本就如此,哪怕 const obj = {},obj 的 __proto__ 也是 Object.prototype
Object.prototype.__proto__ === null // true,规则 3:每个 prototype 对象上都必有 constructor 指回自己的函数
Foo.prototype.constructor === Foo // true