原型链
首先就是对原型链的认识:
原型链是 JavaScript 中用于实现对象继承的一种机制。它是基于对象之间的原型关系构建的。而要认识原型链还要先认识一下 原型(prototype)、构造函数(constructor)和 (__proto__
) 因为这三个 东西 共同构成了 对象之间的原型链关系。
原型(prototype)
原型(prototype): 每个js的对象 都有一个原型对象,原型是该对象 通过继承获取属性和方法的一个链接。通过这个原型 ,我们可以使用共享的方法和属性,并实现对象之间的继承。通过这个属性,我们可以添加方法和属性到函数的原型对象上。
原型对象:通过 .prototype 访问到的就是原型对象。当我们创建一个函数时,JavaScript会自动为该函数创建一个原型对象,并将其赋值给这个函数的 prototype 属性。这个原型对象是一个普通的对象,可以添加属性和方法。
// 自定义一个函数
function say() {
console.log(1);
}
// 自定义一个构造函数 Person
function Person(name, age) {
this.name = name;
this.age = age;
}
// 通过 原型 prototype 来 将say() 添加到 原型对象上
Person.prototype.say = say;
// 原型对象
console.log(Person.prototype);
// 打印结果 添加到了原型对象上
// Object
// say: ƒ say()
// constructor: ƒ Person(name, age)
// [[Prototype]]: Object
构造器(constructor)
构造器(constructor):它存在于所有对象的原型上。这个属性引用了创建该对象的构造函数。
当我们使用构造函数创建一个对象时,JavaScript 会自动在该对象的原型上添加一个 constructor
属性,并将其指向构造函数本身。通过这个属性,我们可以获取到创建对象的构造函数。
。
隐式属性__proto__
隐式属性__proto__ :
是 每个对象都有的一个隐藏属性,它指向了该对象的原型(prototype)隐式函数是 访问对象原型的一种便捷方式。通过__proto__
属性我们可以在对象间沿着原型链访问 和查找属性和方法。
认识完这三个东西,下面就可以了解原型链了
原型链
可以简单的理解为 对象和原型的链接,每一个对象都有原型,而原型本身又是对象,原型又有原型,形成的一种链式结构。
在 JavaScript 中,每个对象 都有一个隐式属性__proto__
来指向它的原型对象(prototype),当我们访问一个对象的属性或者方法时,如果该对象没有这个属性或者方法,js就会沿着原型链向上查找,知道找到匹配的属性或者方法,或者找到原型链的顶端。
原型链的关系可以通过对象实例的 __proto__
属性来表示。每个对象实例都有一个隐式引用指向其构造函数的原型对象,而构造函数的原型对象又有一个隐式引用指向其父级构造函数的原型对象,以此类推,形成了一个层层链接的原型链。
通过原型链,子对象可以继承父对象的属性和方法。当我们在子对象上访问属性或方法时,如果子对象自身没有定义,它就会沿着原型链查找,直到找到父对象中具有相应属性或方法的位置。
原型链的概念可以帮助我们理解 JavaScript 中的继承和属性共享的机制。它使得我们可以使用原型对象来定义和共享通用的方法和属性,从而提高代码的可重用性和效率。但是需要注意,过深或过长的原型链可能会影响性能,因此在设计对象继承关系时需要谨慎使用。
查找规则
-
先查找当前对象, 当前对象有就使用当前对象的方法
-
当前对象没有再逐层在原型链上查找, 最先找到那个就使用哪个
-
如果找到null都没找到就报错
简单的一个原型链
// 自定义一个函数
function say() {
console.log(1);
}
// 自定义一个构造函数 Person
function Person(name, age) {
this.name = name;
this.age = age;
}
// 通过 原型 prototype 来 将say() 添加到 原型对象Person上
Person.prototype.say = say;
// 原型对象
console.log(Person.prototype);
// Object
// say: ƒ say()
// constructor: ƒ Person(name, age)
// [[Prototype]]: Object
// constructor 原型对象中 构造器 指向的就是构造函数
console.log(Person.prototype.constructor == Person); //true
// 实例化对象 p1
let p1 = new Person("张三", 12);
// p1 的构造器 指向的就是Person
console.log(p1.constructor); // Person
// __proto__ 隐式对象
// 实例化对象p1隐式属性 指向的是 构造函数的 原型对象
console.log(p1.__proto__ == Person.prototype); //true
用一张图来表示:
当我们写一个构造函数,用这个构造函数来实例化对象,这个实例化对象就会有隐式属性,这个隐式属性 指向的就是 构造函数的原型对象,而构造函数 和 原型对象 通过 原型 和 构造器 牢牢绑在一起。 我们写的构造函数 也有隐式属性 ,所有的函数都是通过Function构建函数创建出来的对象。指向的就是 Function的原型对象。
补充: 原型链的尽头
相信大家肯定都听过一句话 JavaScript中万物皆对象 那么这句话是怎么得出来的呢?
在JavaScript 中的函数 是引用类型 也就是对象类型 ,既然是对象,也就是通过 构造函数创建出来的,所有的函数都是通过Function构建函数创建出来的对象。可以说 函数的尽头就是Function构建函数
只要是函数,就会有prototype属性,而通过原型属性,可以访问原型对象,Function函数的prototype属性指向Function原型对象 ƒ () { [native code] }
只要有原型对象就有constructor属性。Function原型对象的constructor指向它对应的构造函数
Function.__proto__ 的尽头 就是Function原型对象ƒ () { [native code] }
而Function原型对象也是 一个对象,也会有隐式属性,指向 Object原型对象,
Object 的原型对象 通过隐式 属性,指向的就是 null 可以说 原型对象的尽头是 null
用一张图来解释:
总结一下:
所有的构造函数的 隐式属性__proto__ 都指向 上一级的 原型对象
Function构造函数 的隐式属性 指向的是 Function的原型对象 终点就是 Function的原型对象
所有的原型对象 指向的 就是Object原型对象 终点是 NULL
而构造函数 和 原型对象 通过 原型 (prototype) 和 构造器 (constructor) 互相 指向