浅谈Javascript的原型和原型链
date
Jan 21, 2022
slug
1
status
Published
tags
Learn
summary
Javascript的原型和原型链
type
Post
Book

图中
Parent()是构造函数,p1是Parent()构造出来的实例对象。前置知识
- 想要弄清楚原型和原型链,这几个属性必须搞清楚:
__proto__、prototype、constructor。
- 函数是对象的一种
- 任何函数都可以作为构造函数,当函数被
new调用时,该函数就成为一个构造函数。
举个例子:
var Parent = {
}
//定义一个函数,此时它只是一个普通函数
var p1 = new Parent();
//此时Parent()不是一个普通函数,它是一个构造函数。因为我们通过new关键字调用它。__proto__、constructor、属性是对象独有的。但函数也是对象,所以函数也有这两个属性。
prototype属性是函数独有的
prototype属性
为了方便举例,我们模拟一个场景。父类比作师傅,子类比作徒弟。师傅收徒弟,徒弟还可以收徒弟。然后徒弟可以得到师傅传授的武功,然后徒弟再传给自己的徒弟。师傅想要传授给徒弟的武功就放到“prototype”这个属性里面。徒弟徒孙们就去这里学习武功。prototype属性可以看成是一块特殊的存储空间,存储着提供给子孙后代使用的方法和属性。
prototype是函数独有的属性,从图中可以看到它指向另一个对象。我们可以把这个对象看成是这个函数的原型,通过这个对象定义的方法和属性都可以继承到后代。proto属性
问题来了,p1怎么知道它的原型对象上有这个方法和属性呢?

从图中可以看出
__proto__属性相当于通往prototype的路。而这条路也是通往该对象的原型对象(也可以理解成父对象)。p1.__proto__ === Parent.prototype; //true__proto__通常称为隐式原型,prototype通常称为显式原型。一个对象的隐式原型指向了该对象的构造函数的显式原型。Parent.prototype.__proto__ === Object.prototype //true来到这里,有一个比较绕的点要注意。我们知道一个对象的隐式原型指向该对象的构造函数的显式原型,但构造函数的原型本身也是对象,那么它的隐式原型指向哪呢?从上面的代码可以看出,构造函数的原型对象上的隐式原型对象是指向Object的原型对象。由此可以验证一个结论,万物继承自
Object.prototype。这也说明了为什么我们实例化一个对象,并且可以调用对象上没有的属性和方法。//我们并没有在Parent定义任何方法属性,但是我们可以调用
p1.toString();//hasOwnProperty 等等的一些方法在这里我们就要引出原型链这个概念,当我们调用
p1.toString()的时候,先在p1对象本身寻找,没有找到则通过p1.__proto__找到原型对象Parent.prototype,也没有找到,又通过Parent.prototype.__proto__找到上一层原型对象Object.prototype。在这一层找到了toString()
方法,返回给p1对象使用。但如果在Object.prototype也没有找到的话,就在Object.prototype.__proto__上找,但Object.prototype.__proto__==null,所以就返回undefined。Constructor属性
constuctor属性是让“徒弟”、“徒孙”们知道谁创造了自己,这里可不是找”师傅“,而是找自己的父母。追溯到头就是Function()。

constructor是对象才有的属性,从图中可以看出:对象的constructor是指向它的构造函数,而函数的原型对象则是指向它的本身。要注意的一点是:Object的构造函数指向的是Function()