学习笔记-JavaScript的原型和原型链

基本概念

1. 原型(Prototype)

在JavaScript中,每个函数都有一个特殊的属性叫做prototype(原型属性),这个属性指向一个对象,该对象包含了可以由该函数创建的所有实例共享的属性和方法。

js 复制代码
function Person(name) {
    this.name = name;
}

// 为Person的原型添加方法
Person.prototype.sayHello = function() {
    console.log(`Hello, I'm ${this.name}`);
};

const person1 = new Person('Alice');
person1.sayHello(); // "Hello, I'm Alice"

2. 原型链(Prototype Chain)

当访问一个对象的属性时,JavaScript引擎会:

  1. 首先在该对象自身属性中查找
  2. 如果没有找到,则沿着原型链向上查找
  3. 直到找到该属性或到达原型链的末端(null)

核心机制

1. __proto__ 属性

每个对象都有一个__proto__属性,指向创建该对象的构造函数的prototype

js 复制代码
function Animal(type) {
    this.type = type;
}

const dog = new Animal('mammal');

console.log(dog.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null

2. 构造函数、原型、实例的关系

rust 复制代码
实例对象 --__proto__--> 构造函数的prototype --constructor--> 构造函数

深入理解

1. 原型继承

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

Animal.prototype.eat = function() {
    console.log(`${this.name} is eating`);
};

// 子类
function Dog(name, breed) {
    Animal.call(this, name); // 调用父类构造函数
    this.breed = breed;
}

// 设置原型链继承
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

// 添加子类特有方法
Dog.prototype.bark = function() {
    console.log(`${this.name} is barking`);
};

const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.eat();  // 继承自Animal
myDog.bark(); // Dog自己的方法

2. ES6 Class语法糖

js 复制代码
class Animal {
    constructor(name) {
        this.name = name;
    }
    
    eat() {
        console.log(`${this.name} is eating`);
    }
}

class Dog extends Animal {
    constructor(name, breed) {
        super(name);
        this.breed = breed;
    }
    
    bark() {
        console.log(`${this.name} is barking`);
    }
}

// 本质上还是基于原型的继承
console.log(typeof Animal); // "function"
console.log(Animal.prototype.hasOwnProperty('eat')); // true

重要方法和属性

1. 原型相关方法

js 复制代码
function Car(brand) {
    this.brand = brand;
}

Car.prototype.drive = function() {
    console.log(`${this.brand} is driving`);
};

const myCar = new Car('Toyota');

// 检查原型关系
console.log(myCar instanceof Car); // true
console.log(Car.prototype.isPrototypeOf(myCar)); // true

// 检查属性来源
console.log(myCar.hasOwnProperty('brand')); // true
console.log(myCar.hasOwnProperty('drive')); // false

// 获取原型
console.log(Object.getPrototypeOf(myCar) === Car.prototype); // true

2. 属性遍历

js 复制代码
function List(items) {
    this.items = items || [];
}

List.prototype.add = function(item) {
    this.items.push(item);
};

const myList = new List([1, 2, 3]);
myList.customProp = 'custom';

// for...in 会遍历原型链上的可枚举属性
for (let key in myList) {
    console.log(key); // items, customProp, add
}

// Object.keys 只返回自身可枚举属性
console.log(Object.keys(myList)); // ['items', 'customProp']

// 获取所有自身属性(包括不可枚举)
console.log(Object.getOwnPropertyNames(myList)); // ['items', 'customProp']

实际应用场景

1. 方法共享与内存优化

js 复制代码
// 不好的做法:每个实例都创建新函数
function BadCircle(radius) {
    this.radius = radius;
    this.getArea = function() {
        return Math.PI * this.radius * this.radius;
    };
}

// 好的做法:方法放在原型上共享
function GoodCircle(radius) {
    this.radius = radius;
}

GoodCircle.prototype.getArea = function() {
    return Math.PI * this.radius * this.radius;
};

const circles = [];
for (let i = 0; i < 1000; i++) {
    circles.push(new GoodCircle(i));
}
// 所有实例共享同一个getArea方法,节省内存

2. 扩展内置对象

js 复制代码
// 为数组添加自定义方法(谨慎使用)
Array.prototype.last = function() {
    return this[this.length - 1];
};

const arr = [1, 2, 3, 4, 5];
console.log(arr.last()); // 5

原型链示意图

javascript 复制代码
实例对象
    ↓ __proto__
构造函数的prototype
    ↓ __proto__
Object.prototype
    ↓ __proto__
null

总结要点

  1. 每个函数都有prototype属性‌,指向原型对象
  2. 每个对象都有__proto__属性‌,指向创建它的构造函数的prototype
  3. 原型链的终点是null
  4. 原型继承是JavaScript的核心继承机制
  5. ES6的class语法本质上是原型继承的语法糖
相关推荐
怕浪猫6 分钟前
第一章、Chrome DevTools Protocol (CDP) 详解
前端·javascript·chrome
Yeats_Liao3 小时前
Feed流系统设计(三):数据模型与存储设计,从表结构到Redis收件箱
java·javascript·redis
我是真菜3 小时前
彻底理解js中的深浅拷贝
前端·javascript
尽兴-5 小时前
4.1 智能体核心:Agent、Sub-Agent、ReAct、规划执行
前端·javascript·react.js·agent·react·subagent
万物更新_5 小时前
vue框架
前端·javascript·vue.js·笔记
Richar5 小时前
Object.freeze()注意事项
前端·javascript
TA远方5 小时前
【HTML】JavaScript Canvas 图像截取与保存完整指南
前端·javascript·html·canvas·截图·截取
Asize5 小时前
JavaScript 数据类型解析:从 null 与 undefined 的迷思到栈堆内存真相
前端·javascript·面试
LDX前端校草6 小时前
position属性值及用法
前端·javascript·面试
晓13136 小时前
【Cocos Creator 3.x】篇——第四章 子系统
前端·javascript·游戏引擎