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__也是如此。

相关推荐
无小道1 小时前
Qt——QWidget
开发语言·qt
时艰.1 小时前
Java 并发编程之 CAS 与 Atomic 原子操作类
java·开发语言
梵刹古音2 小时前
【C语言】 函数基础与定义
c语言·开发语言·算法
梵刹古音2 小时前
【C语言】 结构化编程与选择结构
c语言·开发语言·嵌入式
Yvonne爱编码2 小时前
JAVA数据结构 DAY3-List接口
java·开发语言·windows·python
一方_self2 小时前
了解和使用python的click命令行cli工具
开发语言·python
南宫码农2 小时前
我的电视 - Android原生电视直播软件 完整使用教程
android·开发语言·windows·电视盒子
摘星编程3 小时前
React Native + OpenHarmony:Spinner旋转加载器
javascript·react native·react.js
CoderCodingNo3 小时前
【GESP】C++四级/五级练习题 luogu-P1223 排队接水
开发语言·c++·算法
sycmancia3 小时前
C++进阶01——示例
开发语言·c++