今天讲讲原型与原型链,一步到位

2,590 阅读5分钟

原型

前言

在JavaScript中,原型(prototype)是一个非常重要且独特的概念,它在对象创建和继承方面发挥着关键作用。理解原型及其相关的机制有助于更好地理解JavaScript的对象模型,以及如何设计和使用对象和继承。

原型

通常了解JavaScript 中的原型时,会将其分为两种类型:函数原型prototype(显示原型)和对象原型__proto__(隐式原型)

函数原型:

在 JavaScript 中,每个函数都有一个特殊的属性称为 prototype,它是函数被创建时自动生成的一个对象。函数原型对象用于存储可以被函数的所有实例共享的属性和方法。

function Person(name) {
    this.name = name;
}

// Person 函数的原型对象
Person.prototype.sayHello = function() {
    console.log('Hello, my name is ' + this.name);
};

const person1 = new Person('Alice');
const person2 = new Person('Bob');

person1.sayHello(); // 输出:Hello, my name is Alice
person2.sayHello(); // 输出:Hello, my name is Bob

函数原型与其实例对象的关系

  1. 实例对象可以修改显示继承的属性和方法,但无法修改原型上的属性和方法(隐式继承的属性)

    实例对象通过原型链继承了函数原型上的属性和方法。这意味着,如果原型上的属性是可写的,实例对象是可以修改它们的值的,但是它们不能直接修改原型对象上的属性。如果尝试直接给原型对象上的属性赋值,实际上是在给实例对象新增一个同名的属性,而不是修改原型上的属性。

    function Person(name) {
        this.name = name;
    }
    
    Person.prototype.sayHello = function() {
        console.log('Hello, my name is ' + this.name);
    };
    
    const person1 = new Person('Alice');
    const person2 = new Person('Bob');
    
    // 修改实例对象的属性值
    person1.name = 'Charlie';
    
    // 不能修改原型上的属性值
    person1.sayHello = function() {
        console.log('Hi, I am ' + this.name);
    };
    
    person1.sayHello(); // 输出:Hi, I am Charlie
    person2.sayHello(); // 输出:Hello, my name is Bob
    
  2. 实例对象无法给原型新增属性

    实例对象无法直接给原型对象新增属性。任何试图给实例对象的原型对象添加属性的尝试,都将在实例对象上创建一个新的属性,而不会影响到原型对象本身。

    function Person(name) {
        this.name = name;
    }
    
    const person1 = new Person('Alice');
    const person2 = new Person('Bob');
    
    // 试图给实例对象的原型对象新增属性
    person1.age = 30;
    
    // 但实际上在实例对象上创建了一个新的属性,而不是在原型上新增
    console.log(person1.age); // 输出:30
    console.log(person2.age);//underfined
    
  3. 实例对象无法删除原型上的属性

    实例对象无法直接删除原型对象上的属性。任何试图删除实例对象原型链上的属性的尝试,都将失败,因为删除操作只会影响到实例对象自身的属性。

    function Person(name) {
        this.name = name;
    }
    
    Person.prototype.age = 30;
    
    const person1 = new Person('Alice');
    const person2 = new Person('Bob');
    
    // 试图删除原型对象上的属性
    delete person1.name;
    
    // 但实际上只是删除了实例对象上的属性,原型对象上的属性并未受影响
    console.log(person1.name); // underfined
    console.log(person2.name);//Bob
    

对象原型

每个对象都有一个特殊的内部属性 __proto__,它指向该对象的原型。原型可以是另一个对象或者 null

let person1 = new Person('Alice');
console.log(person1.__proto__ ); // Person.prototype

函数原型与对象原型的关系

函数原型和对象原型之间有一个重要的关系:通过函数原型,我们可以在创建的对象之间共享方法和属性。

当你创建一个新的对象时,JavaScript 引擎会自动将对象的 __proto__ 属性设置为该构造函数的 prototype 属性的值。

例如:

let person2 = new Person('Bob');
console.log(person2.__proto__ === Person.prototype); // true

constructor 属性

在原型链中,constructor 属性指的是指向创建对象的构造函数的引用。每个对象都有一个 constructor 属性,它引用了创建该对象的构造函数。

示例:

function Person(name) {
    this.name = name;
}

const person1 = new Person('Alice');

// person1 的 constructor 属性指向 Person 构造函数
console.log(person1.constructor === Person); // 输出: true

综上所述如图:

image.png

原型链

什么是原型链?

原型链是 JavaScript 中用于实现继承和属性查找的一种机制。每个对象都有一个原型对象(prototype),通过原型对象可以实现属性和方法的继承。当访问一个对象的属性或方法时,如果对象本身不存在这个属性或方法,JavaScript 引擎会沿着原型链向上查找,直到找到相应的属性或方法或者到达原型链的顶端(Object.prototype)为止。

原型链的继承关系

在 JavaScript 中,对象之间的继承关系通过原型链来实现。例如:

javascriptCopy Code
// 定义一个动物类
function Animal(name) {
  this.name = name;
}

// 在动物类的原型上添加方法
Animal.prototype.sleep = function() {
  console.log(this.name + " is sleeping");
};

// 定义一个狗类
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("Woof! Woof!");
};

// 创建一个狗的实例
var dog1 = new Dog("Buddy", "Labrador");

// 调用狗的方法
dog1.sleep(); // 输出: Buddy is sleeping
dog1.bark(); // 输出: Woof! Woof!

在这个例子中,Dog 类继承了 Animal 类的属性和方法,通过原型链的方式实现了继承关系。

属性查找过程

当访问对象的属性或方法时,JavaScript 引擎会沿着原型链向上查找,直到找到相应的属性或方法或者到达原型链的顶端。如果找不到相应的属性或方法,则返回 undefined

所有对象都有原型嘛(考点)

这个答案肯定是不对的!

除了Object 本身之外,所有对象都有原型

  • Object 的实例:通过 new Object() 创建的对象,以及通过对象字面量创建的对象(如 {}),其原型是 Object.prototype。

    示例:

    const obj = {};
    console.log(obj.__proto__ === Object.prototype); // 输出: true
    
  • 函数的实例:函数也是对象,通过 new Function() 创建的函数对象,其原型是 Function.prototype。

    示例:

    function myFunction() {}
    console.log(myFunction.__proto__ === Function.prototype); // 输出: true
    
  • 数组的实例:通过 new Array()创建的数组对象,其原型是 Array.prototype。

    示例:

    const arr = [];
    console.log(arr.__proto__ === Array.prototype); // 输出: true
    
  • 其他内置对象:例如 Date、RegExp、Error 等对象的实例,它们的原型分别是 Date.prototype、RegExp.prototype、Error.prototype。

    示例:

    const date = new Date();
    console.log(date.__proto__ === Date.prototype); // 输出: true
    

Object.prototype 是原型链的顶端

  • Object.prototype 是原型链的顶端对象,它的原型是 null。

    示例:

    console.log(Object.prototype.__proto__); // 输出: null
    

使用 Object.create() 创建没有原型的对象(重点)

  • 如果你想创建一个没有原型的对象,可以使用 Object.create(null)

    示例:

    const noProtoObj = Object.create(null);
    console.log(noProtoObj.__proto__); // 输出: undefined
    

最后

搞懂原型,好好学习,天天向上!

image.png