JS原型链总结

JS中的原型链

在理解原型链前,需先掌握 JS 中与原型相关的三个核心属性 / 方法

定义

原型链是 JS 实现继承 的核心机制:当访问一个对象的属性 / 方法时,JS 引擎先在对象自身查找;若找不到,则通过 [[Prototype]] 向上查找其原型对象;若原型对象仍找不到,继续向上查找原型的原型,直到 Object.prototype;若最终仍未找到,返回 undefined(方法则报错 xxx is not a function)。这条由 [[Prototype]] 串联的链式结构,就是原型链

核心逻辑

  • 原型链的核心是 "委托" 而非 "复制":对象不拥有原型的属性 / 方法,仅在需要时委托原型查找。
  • 所有对象最终都指向 Object.prototypeObject.prototype.__proto__ === null(原型链终点)。
  • 函数既是函数也是对象 :函数的 [[Prototype]] 指向 Function.prototype,而 Function.prototype[[Prototype]] 指向 Object.prototype

核心概念

prototype

描述 :**prototype**是仅函数(Function,箭头函数、绑定函数除外)拥有的原型对象,函数作为构造函数创建实例时,实例的隐式原型[[Prototype]](浏览器环境中通过实例的__proto__属性获取)也会指向这个原型对象。

默认特征

  • 原型对象默认包含constructor属性,指向所属构造函数;
  • 原型对象继承自Object.prototype
js 复制代码
function Person(name) {
  this.name = name;
}
// Person函数的prototype是原型对象
console.log(Person.prototype.constructor === Person);  // true
js 复制代码
function User(name, age){
    this.name = name;
    this.age = age;
}
const user = new User('张三', 18);
console.log(user.__proto__===User.prototype);  // true 实例的隐式原型为其构造函数的原型对象

[[Prototype]]

描述 :所有对象(包括函数、普通对象、数组等)都拥有隐式原型。获取对象的__proto__属性是浏览器支持的获取隐式原型的方式,ES标准推荐使用``Object.getPrototypeOf()/Object.setPrototypeOf()` 获取/操作隐式原型。

  1. 以构造函数构造的对象,对象的原型指向构造函数的原型对象。
js 复制代码
function User(name, age){
    this.name = name;
    this.age = age;
}
const user = new User('张三', 18);
console.log(Object.getPrototypeOf(user)===User.prototype);  //true
/* user的原型链
user.__proto__ => User.prototype
User.prototype.__proto__ => Object.Prototype
*/
  1. function函数本身也是对象,它的隐式原型指向Function.prototype
js 复制代码
function User(name, age){
    this.name = name;
    this.age = age;
}
console.log(Object.getPrototypeOf(User) === Function.prototype);  //true
console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype);  //true
/* User的原型链
User.__proto__ => Function.prototype
Function.prototype.__proto__ => Object.Prototype
*/
  1. 直接键值对定义的对象,对象的原型指向Object.prototype
js 复制代码
const obj ={
    name:'张三',
    age:18,
}
console.log(Object.getPrototypeOf(obj)===Object.prototype);  //true
/*obj的原型链
obj.__proto__ => Object.Prototype
*/
  1. 数组的原型指向Array.prototype
js 复制代码
const users = ["张三", "李四"];
console.log(Object.getPrototypeOf(users) === Array.prototype);  //true
console.log(Object.getPrototypeOf(Array.prototype) === Object.prototype);  //true
/*users的原型链
users.__proto__ => Array.prototype
Array.prototype.__proto__ => Object.prototype
*/

Object.prototype

描述Object.prototypeJS 中所有对象的 "根原型",其 [[Prototype]]null(原型链的终点);Object.prototype包含 JS 内置的通用方法(如 toString()valueOf()hasOwnProperty()),所有对象均可通过原型链访问这些方法

原型继承

java 复制代码
// 父类
function Animal(name) {
  this.name = name;
}

// 子类
function Dog(name, breed) {
  Animal.call(this, name); // 继承实例属性
  this.breed = breed;
}
// 继承原型方法(连接原型链)
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // 恢复constructor

const dog = new Dog("旺财", "中华田园犬");
console.log(dog.__proto__ === Dog.prototype); // true
console.log(Dog.prototype.__proto__ === Animal.prototype); // true

函数的prototype属性是可修改的,通过修改函数的原型可实现原型链的连接进而实现继承。

ES6及以上提供了class更语义化地定义类,提供extends更语义化地实现类继承(底层原理还是原型链的连接)。

js 复制代码
class Person{
    constructor(name, age){
        this.name = name;
        this.age = age;
    }
}
class Student extends Person{
    constructor(name, age, score){
        super(name, age);
        this.score = score;
    }
}
const student = new Student('张三', 18, 100);

console.log(student.__proto__ === Student.prototype);  //true
console.log(Student.prototype.__proto__ === Person.prototype);  //true
console.log(Person.prototype.__proto__ === Object.prototype);  //true
/*student的原型链
student.__proto__ => Student.prototype
Student.prototype.__protot__ => Person.prototype
Person.prototype.__proto__ => Object.proto
*/

常见误区

  1. 混淆 prototype__proto__prototype 是函数的属性,__proto__ 是所有对象的属性,前者是函数对象的原型对象,后者是对象指向原型的链路。
  2. 认为原型链是 "复制" :原型链是 "委托查找",而非将原型属性复制到实例,修改原型会影响所有实例
  3. 直接赋值 __proto____proto__ 性能差且易导致原型链混乱,优先使用 Object.getPrototypeOf()/Object.setPrototypeOf()
  4. 忽略 Object.prototype.__proto__ === null :所有原型链最终指向 null,而非 undefined

总结

User构造的user对象为例,分析原型链:

看似有点复杂,但可以总结以下特点:

  1. 所有普通对象 都有__proto__属性,所有函数对象都有一个prototype属性,prototype属性是也一个普通对象

  2. 所有函数对象的__proto__属性指向FUnction.prototype,包括Function自身的__proto__也是如此。

相关推荐
炫饭第一名2 小时前
速通Canvas指北🦮——基础入门篇
前端·javascript·程序员
进击的尘埃4 小时前
Vue3 响应式原理:从 Proxy 到依赖收集,手撸一个迷你 reactivity
javascript
willow4 小时前
JavaScript数据类型整理1
javascript
LeeYaMaster4 小时前
20个例子掌握RxJS——第十一章实现 WebSocket 消息节流
javascript·angular.js
UIUV5 小时前
RAG技术学习笔记(含实操解析)
javascript·langchain·llm
颜酱7 小时前
理解二叉树最近公共祖先(LCA):从基础到变种解析
javascript·后端·算法
FansUnion7 小时前
我如何用 Next.js + Supabase + Cloudflare R2 搭建壁纸销售平台——月成本接近 $0
javascript
左夕8 小时前
分不清apply,bind,call?看这篇文章就够了
前端·javascript
滕青山9 小时前
文本行过滤/筛选 在线工具核心JS实现
前端·javascript·vue.js
时光不负努力9 小时前
编程常用模式集合
前端·javascript·typescript