JS中的原型和原型链 —(一文弄懂超易懂)

135 阅读2分钟

在弄懂原型和原型链之前,先了解几个概念

1.什么是原型对象

每一个函数对象(fn)都有一个特殊的属性叫做原型对象(prototype) ,它指向另一个对象,这个对象(fn.prototype)被称为原型对象,  原型对象是用来共享属性和方法的,即在构造函数的原型对象上定义的方法,通过这个构造函数创造的实例均可调用该方法

image.png

2.constructor的指向

构造函数的原型对象有一个constructor属性指向构造函数本身

       fn.prototype.constuctor = fn  //True

构造函数的实例对象的constructor指向构造函数本身

let a = new fn()
a.constructor = fn

3.隐式原型_proto_

 在js中,每个对象都有一个 _proto_  属性,这个_proto_就被称为隐式原型。

 实例对象当然也是对象,也存在_proto_属性,每个js对象都有一个隐藏的原型对象属性_proto_,它指向创建它的构造函数的原型对象(fn.prototype)

let a= new fn()
a._proto_ = fn.prototype       //True

什么是原型链

我们知道,每个对象都有一个_proto_属性指向其构造函数的原型对象,那么又已知函数fn的原型对象也是对象,那fn的原型对象fn.prototype会不会也有存在隐式原型__proto__指向某个构造函数的原型对象?这种情况是存在的,即fn.prototype这一原型对象是另一个构造函数的实例

// 第一个构造函数 A
function A(name) {
    this.name = name;
}

// 第一个构造函数 B
function B(age) {
    this.age = age;
}

// 让 A 的原型对象是 B 的实例
A.prototype = new B(19);

const obj = new A("John");

console.log(obj.name);  // "John" - 来自构造函数 A
console.log(obj.age);   // 19 - 来自构造函数 B(通过原型继承)

分析上段代码,构造函数A的原型对象是构造函数B的实例,输出obj.name时,根据构造函数A对实例中的name的定义输出即可,但输出obj.age时,构造函数A中并未对age定义,那么他只能向它的构造函数的原型对象(A.prototype)去找,即obj._proto_.age,已知A.prototype.age=19,所有obj._proto_.age即为obj.age=19,这一成了一条链,即为原型链

image.png
每当构造函数A的实例a中找不到某属性的定义时,他就会通过a._proto向A.prototype中寻找,若仍寻找不到再向上层A.prototype._porto寻找,直至到达原型链的终点,即某个对象的_proto_属性指向了Object.prototype这一原型链最上层的终点,因为它是终点,所以它不再有自己的原型对象,所以Object.prototype.proto 指向null。