原型对象
Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象,我们叫这个对象为原型对象。这个对象包含了该类型所有实例共享的属性和方法。
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function () {
console.log("你好,我是" + this.name)
}
Person.prototype.sayBye = function () {
console.log('再见!');
}
const zs = new Person('张三');
zs.sayHello();
zs.sayBye();
console.log(Person.prototype)
Person.prototype是构造函数的原型对象- 所有通过
new Person()创建的实例都会继承这个原型对象
让我们再创建一个构造函数来验证一下,就喊张三的好朋友李四来帮忙吧!
const ls = new Person('李四');
ls.sayHello();
ls.sayBye();
console.log(zs.__proto__ === ls.__proto__); // true
看吧!通过构造函数创建的实例会继承这个原型对象的
下面让我们再来介绍一下原型对象的constructor属性
每个原型对象都有一个constructor属性,它的作用是指向该原型对象的构造函数
我们可以简单理解为:指向它的父对象
知道了它的作用,那它有什么用处?别急,让我带你看看下面的这段代码
function Person(name) {
this.name = name;
}
Person.prototype = {
}
console.log(Person.prototype.constructor) //Person
奇怪,constructor属性不是指向父对象Person吗?怎么会指向Object?
这是因为这种写法相当于给原型对象采取对象属性赋值,但这样会覆盖构造函数原型对象原本的内容,所以修改后的constructor属性就不指向当前的构造函数了。这个时候,就需要constructor闪亮登场了!
function Person(name) {
this.name = name;
}
Person.prototype = {
constructor: Person
}
console.log(Person.prototype.constructor) //Object
返回控制台一看,constructor属性又重新指向当前构造函数了。
通过对上面代码的分析:我们发现构造函数还可以创建实例对象,构造函数还有有一个原型对象,原型对象里可以放置一些公共的属性和方法,但是为什么实例对象可以访问原型对象里的属性或方法?这就牵扯到我们今天的另一个主题了对象原型
对象原型
每个对象实例都有一个内部属性[[Prototype]](在浏览器中可通过__proto__访问),我们称之为对象原型。
还是用上面的那段代码
console.log(Person.prototype)
console.log(zs.__proto__)
console.log(zs.__proto__ === Person.prototype) //true
// 现代JavaScript推荐使用
console.log(Object.getPrototypeOf(zs) === Person.prototype); // true
让我们看看控制台打印的信息
哇哦!一模一样!学到了学到了。
之所以实例对象可以访问原型对象里的属性和方法,就是因为有原型对象的存在。
简单理解就是:原型对象指向对象原型
关键点:
__proto__是实例访问原型链的入口Object.getPrototypeOf()是标准访问方式
注意:__proto__对象原型里也有一个constructor,指向创建该实例对象的构造函数
下面让我们来梳理一下这三者之间的关系
原型链
基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,这种原型对象的链状关系就叫做原型链。
直接上图
关键点:
- 实例通过
__proto__访问原型链。 - 原型链的查找从实例对象自身开始,通过
__proto__逐级向上访问,终点是Object.prototype(其__proto__为null)
从上面的图片中我们可以总结一下原型链的查找规则:
1.当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
2.如果没有就查找它的原型(也就是 proto 指向的 prototype 原型对象)。
3.如果还没有就查找原型对象的原型(Object 的原型对象)。
4.依此类推一直找到 Object 为止(null)。
5.proto 对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。
6.可以使用 instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
扩展
ES6的class语法本质仍是基于原型链的语法糖:
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise`);
}
}
class Dog extends Animal {
constructor(name) {
super(name);
}
speak() {
console.log(`${this.name} barks`);
}
}
const rex = new Dog('Rex');
// 验证关系
console.log(rex.__proto__ === Dog.prototype); // true
console.log(Dog.prototype.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.constructor === Animal); // true
结语
感谢大家的观看,如果对您有帮助的话,请在评论区随便说两句吧,这对作者非常重要。也欢迎大家点赞关注支持,我会在后面继续更新的。