JavaScript 原型与原型链

一、基础认知

原型(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 都创建一份函数,节省内存。

三、原型链查找规则

当访问对象的属性 / 方法时:

  1. 先在自身查找,找到直接返回;

  2. 找不到,顺着 __proto__ 去原型(prototype)找;

  3. 一直向上查找,直到 Object.prototype;

  4. Object.prototype.__proto__ 是 null,到顶停止,返回 undefined。

    console.log(p1.toString())
    // toString 不在 p1、不在 Person.prototype,在 Object.prototype

四、new 关键字做了哪几件事

  1. 创建一个空的简单 JavaScript 对象 {};
  2. 将新对象的 __proto__ 指向构造函数的 prototype;
  3. 绑定 this,执行构造函数代码,给新对象添加属性;
  4. 如果构造函数没有手动 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
}

八、常见坑点

  1. 不要直接修改 __proto__,性能差,推荐 Object.create();

  2. 原型上的引用类型属性会被所有实例共享,容易互相污染;

    Person.prototype.list = []
    p1.list.push(1)
    p2.list // [1] 跟着变了

  3. constructor 被覆盖之后记得手动还原;

  4. 箭头函数没有 prototype,不能用作构造函数,不能 new。

九、小结

原型链是 JS 继承的底层,class 只是语法糖;属性查找顺着 __proto__ 向上走,终点是 null。理解原型,才能看懂继承、instanceof、this 以及框架底层封装。

相关推荐
Amos_Web1 小时前
通过 Rspack 源码学 Rust(四):用 `Option<T>` 表达“查询可能没有结果”
前端
朋友圈自动点赞工具1 小时前
Ruby方法查找:对象不存方法
开发语言·前端·ruby·科技资讯
晓得迷路了1 小时前
栗子前端技术周刊第 152 期 - Rspack 2.3、TypeScript 7.1 Beta、Bun 1.4.4...
前端·javascript·css
D e re k ZH1 小时前
C++ 模板与泛型编程进阶(No 2)
开发语言·c++
一级新生1 小时前
LLM _ Agent _ Skill _ 专家 的关系与选型笔记
java·开发语言·人工智能·笔记·大模型·智能体·skill
starzy19901 小时前
Scala String操作:从插值器到正则的完整指南
开发语言·后端·scala
普通人61 小时前
CSS-box-sizing-使用指南
开发语言·前端·css·1024程序员节
她说彩礼65万1 小时前
C# TaskCompletionSource
java·开发语言·c#
风骏时光牛马2 小时前
AI开源工具一站式尝鲜体验
前端