js中的原型以及原型链

305 阅读4分钟

JS中的原型和原型链

今天带大家了解一下前端js中一个比较基础但也很关键的内容原型和原型链

一、原型:对象背后的“共享工具箱”

1. 原型到底是什么?

在JavaScript中,每个对象(除了null)在创建时,都会默认关联另一个对象,这个被关联的对象就是“原型”。你可以把它理解成一个“共享工具箱”——里面存放着一些属性和方法,供关联的对象直接使用,不用每个对象都单独定义一遍。

比如我们创建一个对象let obj = { name: 'test' },虽然没给它写toString()方法,但调用obj.toString()时却能正常执行。这是因为obj的原型里自带了这个方法,obj直接“借用”了过来。

2. 原型的3个关键角色:prototype、__proto__和constructor

要搞懂原型,必须分清这三个“符号”:

• prototype:只有函数才有这个属性,它指向一个对象(即“原型对象”)。当这个函数被用作构造函数时,用new创建的实例,其原型就是这个prototype指向的对象。

proto:每个对象(包括函数)都有这个属性(非标准但浏览器普遍支持),它是对象的“原型指针”,直接指向该对象的原型。

• constructor:原型对象自带的属性,默认指向该原型对应的构造函数。比如Person.prototype.constructor会指向Person。

举个例子

function Student(){}

    Student.prototype.school = "中学";

    const student = new Student();
    
    console.log(student.school);

    Student.prototype.school = "小学";
    console.log(student.school);
3. 原型的出现是为了解决什么问题?

早期的JavaScript没有“类”的概念,如果想让多个对象共享方法,只能手动给每个对象复制一遍,既浪费内存和时间又不好维护。

原型的设计完美解决了这个问题:把需要共享的方法放在构造函数的prototype里,所有实例通过原型链就能访问到,不用重复定义。

4. 原型的数据类型与特殊情况

从数据类型上讲,原型是一个“对象”(准确说是Object类型,除了Object.prototype的原型是null)。

但并不是所有数据类型都有原型:

• 基本数据类型(number、string、boolean等):

通常是没有原型的。

• 引用数据类型(object、array、function等):

都有原型,对象通过 __proto__指向其构造函数的prototype

而函数本身既是对象也是构造函数,有自己的protoype属性,同时通过__proto__指向Function.prototype

• null和undefined:没有原型。

二、原型链:对象属性的“溯源之路”

1. 什么是原型链?

既然原型本身也是对象,那它也有自己的原型——这样一层套一层,就形成了一条链式结构,这就是原型链。

打个比方:就像你在家找剪刀,先翻自己的抽屉(对象自身),找不到就去客厅抽屉(对象的原型),再找不到就去储物间(原型的原型),直到找到或确认家里没有(原型链终点)。

2. 原型链的查找规则

当我们访问一个对象的属性或方法时,JS会按以下步骤查找:

  1. 先检查对象自身是否有该属性/方法,有则直接使用;

  2. 若无,就通过__proto__查找其原型;

  3. 若原型中没有,继续查找原型的原型(proto.proto);

  4. 以此类推,直到找到Object.prototype(它的__proto__是null,即原型链的终点);

  5. 若全程没找到,属性返回undefined,方法则报错“xxx is not a function”。

例子

const vehicle = {
  wheels: 4,
  start() {
    return "Engine started";
  }
};

const car = {
  brand: "Toyota",
  __proto__: vehicle 
};

console.log(car.wheels);    
console.log(car.start());   
console.log(car.brand);     

三、原型链如何支撑JS的继承机制?

JavaScript的继承是靠原型链继承——让子类的实例通过原型链访问父类的属性和方法,实现代码复用。

继承的核心逻辑

让子类的原型指向父类的实例(或父类的原型),这样子类实例的原型链就能“挂靠”到父类的原型上,从而共享父类的方法。

原型链继承的优势在于灵活——不需要预先定义“类”的关系,通过修改原型链就能动态改变继承关系。这也符合JavaScript“动态语言”的特性,让对象能在运行时灵活共享和扩展功能。

例子

function Animal(){}

        Animal.prototype.eat = function(){
            console.log("动物吃东西")
        };

        function Dog(){}

        Dog.prototype = new Animal();

        Dog.prototype.constructor = Dog;

        Dog.prototype.bark = function(){
            console.log("狗叫")
        };

        const dog = new Dog();

        dog.bark();
        dog.eat();

总结

原型就像对象的“共享基因库”,原型链则是基因的“传递链条”,而继承就是通过这条链条实现的“基因传承”。理解prototype、__proto__和constructor的关系,看清原型链的查找规则,你会发现JS的对象机制其实很精妙。

最后

希望路过的前端的大佬可以给一点指导意见,谢谢。