一、基础认知
原型(prototype)是 JS 实现继承的核心机制,JS 里几乎所有对象都有一个隐式的 __proto__ 属性,指向它的构造函数的 prototype(显式原型);函数才有 prototype 属性,普通对象没有。
简单一句话:对象的 proto 找构造函数的 prototype
关系图
实例对象.__proto__ === 构造函数.prototype
构造函数.prototype.constructor === 构造函数本身
二、基础示例
// 构造函数
function Person(name) {
this.name = name
}
// 给原型上挂载公共方法,所有实例共享
Person.prototype.sayHi = function () {
console.log(`你好,我是${this.name}`)
}
const p1 = new Person('张三')
const p2 = new Person('李四')
p1.sayHi()
p2.sayHi()
console.log(p1.__proto__ === Person.prototype) // true
console.log(Person.prototype.constructor === Person) // true
✅ 优势:方法写在原型上,不会每次 new 都创建一份函数,节省内存。
三、原型链查找规则
当访问对象的属性 / 方法时:
-
先在自身查找,找到直接返回;
-
找不到,顺着
__proto__去原型(prototype)找; -
一直向上查找,直到
Object.prototype; -
Object.prototype.__proto__是null,到顶停止,返回undefined。console.log(p1.toString())
// toString 不在 p1、不在 Person.prototype,在 Object.prototype
四、new 关键字做了哪几件事
- 创建一个空的简单 JavaScript 对象
{}; - 将新对象的
__proto__指向构造函数的prototype; - 绑定 this,执行构造函数代码,给新对象添加属性;
- 如果构造函数没有手动 return 对象,默认返回这个新对象。
手写简易 new:
function myNew(Fn, ...args) {
const obj = {}
obj.__proto__ = Fn.prototype
const res = Fn.apply(obj, args)
return res instanceof Object ? res : obj
}
五、class 语法糖和原型
ES6 class 本质还是基于原型,只是写法更贴近面向对象:
class Person {
constructor(name) {
this.name = name
}
sayHi() {
console.log(`你好,我是${this.name}`)
}
}
const p = new Person('小明')
// sayHi 依然存在 Person.prototype 上
六、继承实现(原型继承)
function Animal(type) {
this.type = type
}
Animal.prototype.eat = function () {
console.log('吃东西')
}
function Dog(name) {
Animal.call(this, '犬类') // 继承实例属性
this.name = name
}
// 继承原型方法
Dog.prototype = Object.create(Animal.prototype)
Dog.prototype.constructor = Dog
Dog.prototype.bark = function () {
console.log('汪汪')
}
ES6 class extends 写法:
class Animal {
constructor(type) {
this.type = type
}
eat() {
console.log('吃东西')
}
}
class Dog extends Animal {
constructor(name) {
super('犬类')
this.name = name
}
bark() {
console.log('汪汪')
}
}
七、instanceof 原理
A instanceof B:沿着 A 的原型链向上查找,看是否能找到 B.prototype。
function myInstanceof(a, b) {
let proto = a.__proto__
while (proto) {
if (proto === b.prototype) return true
proto = proto.__proto__
}
return false
}
八、常见坑点
-
不要直接修改
__proto__,性能差,推荐Object.create(); -
原型上的引用类型属性会被所有实例共享,容易互相污染;
Person.prototype.list = []
p1.list.push(1)
p2.list // [1] 跟着变了 -
constructor被覆盖之后记得手动还原; -
箭头函数没有
prototype,不能用作构造函数,不能 new。
九、小结
原型链是 JS 继承的底层,class 只是语法糖;属性查找顺着 __proto__ 向上走,终点是 null。理解原型,才能看懂继承、instanceof、this 以及框架底层封装。