原型与原型链
我们主要从三个方面说原型和原型链,主要是 prototype,__proto__,constructor。
受限我们创建一个构造函数,用于后续生成实例。
function Person() {}
Person.prototype.name = 'Tom';
var person1 = new Person();
console.log(person1.name); // Tom
prototype
每个函数都有一个 prototype 属性,可以在 prototype 上挂载属性和方法,那么一个函数的 prototype 具体指向哪里呢?
其实,函数的 prototype 具体指向一个对象,这个对象就是后续引用该构造函数创建的实例的原型。
原型:每个对象(null 除外)在创建时都会关联另外一个对象,这个对象就是我们所说的原型,每个对象都会从原型上继承属性和方法。
用一张图表示构造函数和实例原型就是如图所示。
prototype 引出了构造函数和实例原型的关系,我们如果想看实例和原型关系,这个时候就要引出 __proto__ 属性了。
__proto__
每个函数有自己的 prototype,每个对象(null 除外)也有个隐式的属性 __proto__,这个属性会指向该对象的原型。
function Person() {}
var person1 = new Person();
console.log(person1.__proto__ === Person.prototype); // true
person1 这个对象,是我们通过构造函数 Person 实例化出来的对象,上面说 Person.prototype 是一个对象,所有通过 Person 实例化出来的对象,原型都指向 Person.prototype。所以我们就得到了上面的结果。
这样,我们就能将上面的原型图进行进一步更新:
其实 __proto__ 属性并不是所有的浏览器都支持,我们在使用 person1.proto 的时候,可以理解成使用了 Object.getPrototypeOf(person1),获取对象的原型。
constructor
构造函数通过的 new 方法创建实例,通过 prototype 指向原型。
实例可以通过 __proto__ 指向原型,其实原型也有属性可以指向构造函数,这个时候就要用到 constructor 属性了,每个原型都有一个 constructor 属性,指向关联的构造函数。
function Person() {}
var person1 = new Person();
console.log(Person === Person.prototype.constructor); // true
我们把原型图进一步进行更新:
一个简单的原型链是这样,但是,原型是一个对象,我们之前说每个对象都有自己的 __proto__ 属性,所以当我们使用原型的 `proto 属性时,还会继续找原型对应的原型。
原型链
function Person() {}
Person.prototype.name = 'Tom';
var person1 = new Person();
person1.name = 'Jerry';
console.log(person1.name); // Jerry
delete person1.name;
console.log(person1.name); // Tom
当我们给原型添加一个 name 值,并且给实例 person1 添加一个 name 值之后,如果输出 person1.name 会直接输出 person1 的 name。
当我们把 person1 的 name 属性删除之后,输出 person1.name 并不会报错,反而会输出原型上的 name 值。
这就是因为如果对象上没有某个属性或者方法时,会从对象的原型上去找,如果该对象的原型上没有的话,会逐层接着往上找,直到找到顶层位置。
这个时候原型图就更加丰富了:
当我们找到 Object.prototype 上之后就不会往后找了,打印一下 Object.prototype.proto,就知道为什么了。
console.log(Object.prototype.__proto__ === null);
因为 Object.prototype 的原型是 null,null 就表示“没有对象”,没有值,所以 Object.prototype 再往上就没有原型了,也就是说 Object.prototype 就是顶层的原型。
当对象使用一个方法或者属性时,如果当前对象有,那就直接使用,如果没有会一直找到顶层原型上(Object.prototype)。
将原型图再完整一下,也是我们最后的最终版:
我们都是使用 __proto__ 去查找,通过 __proto__ 将原型对象关联起来,这就是原型链。
总结一下原型链的四条规则:
- 函数都要自己的 prototype 属性,指向该构造函数创建的实例的原型。
- 对象都有
__proto__属性,指向原型,也就是指向其构造函数的 prototype。 - 原型的 constructor 属性,都指向关联的构造函数。
- 引用类型上如果没有某个属性或者方法时,会通过
__proto__逐层向上去找,知道顶层原型(Object.prototype)。
参考:
阅读完后两部曲
- 喜欢的小伙伴点个赞吧,感觉对身边人有帮助的,麻烦动动手指,分享一下。非常感谢各位花时间阅读完,同时很感谢各位的点赞和分享。
- 希望各位关注一下我的公众号吧,新的文章第一时间发到公众号,公众号主要发一些个人随笔、读书笔记、还有一些技术热点和实时热点,并且还有非常吸引人的我个人自费抽奖活动哦~