JavaScript基础---原型对象概念详解(仔细理解一下)
对象原型
每一个对象都有一个内置的 proto 属性指向构造它的函数prototype属性. 而构造函数的prototype.constructor 则指向构造函数本生。一个对象的属性的寻找过程由以下几个部分组成:
- 寻找对象属性的数据描述符(writable, value)或存取描述符(getter, setter),如果查询到了,则返回 对应的值。如果查询不到,则进入第2步骤。
- 寻找对象属性的值是否有被显示定义 (可以通过 Object.getOwnPropertyNames)检测,如果对象属性定义了,则返回定义的值。 如果没有,则进入第3步骤。
- 寻找对象的隐藏原型__proto__对象的属性,规则同1,2步骤。如果还未找到,则重复第3步骤, 直到__proto__ 为null 为止。
我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype。
1 | function Person() { //创建构造函数 |
每一个构造函数的prototype是惟一的,他们是不相等的。这个属性对应着一个对象,这个对象就是我们所谓的原形对象。
下面我们画图来分析一下:
这里prototype属性就是保存原型对象的地址,在MyClass函数里有一个属性指向原型对象。
如果函数作为普通函数调用prototype没有任何作用
当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的 原型对象,我们可以通过__proto__来访问该属性。
1 | function MyClass() { //创建构造函数 |
通过__proto__来访问该属性,而且该属性和 MyClass.prototype指向的对象的同一个。不仅仅是mc,我再创建一个mc2,它也有__proto__,它指向的也是prototype。
只要通过MyClass创建的对象,他们都有一个隐含属性指向原形对象。
画图来理解一下:
原型对象就相当于一个公共区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中的公有的内容,统一设置到原型对象中。
看下列代码:
1 | function MyClass() { //创建构造函数 |
我们向MyClass的原型对象中添加属性a,我们并没有向对象mc中添加a,此时mc中没有a,他就会向原型对象中寻找。
当我们访问对象的一个属性时,会现在对象自身中寻找,如果有则直接使用,如果没有,则会去原型对象中寻找,如果找到则会直接使用。
再看,当我们想mc中添加a,如下:
1 | function MyClass() { //创建构造函数 |
因为mc中有a,则会优先使用自身中的a。所以会输出“我是mc中的a”
再看:
1 | function MyClass() { //创建构造函数 |
当我们查看mc2中的a时,返回的是123,这是因为此时mc2中没有a,自身没有,它会向它的原型对象中去寻找a。
到现在你应该能感觉到了,原型对象就是一个公共区域,所有对象(这里的所有对象指的是同一个构造函数产生的对象)都能访问到原型对象里面的属性。
既然属性能访问到,那我们来试试方法。
1 | function MyClass() { //创建构造函数 |
显而易见,方法也是能访问到的,进一步说明了原型对象就是一个公共区域。
以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的 原型对象中,这不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法
补充说明:
前面我们说到:我们向MyClass的原型对象中添加属性a,我们并没有向对象mc中添加a,那这样的话我们可以使用 in运算符 来检查一下。
什么是in运算符:
- 通过该运算符可以检查一个对象中是否含有指定的属性
如果有则返回true ,没有则返回false
- 语法:
“属性名” in 对象
???这也不对鸭,mc中不是没有a吗?怎么会返回true呢?。1
2
3
4
5
6
7
8
9function MyClass() { //创建构造函数
}
var mc = new MyClass(); //通过构造函数MyClass创建一个对象mc
var mc1 = new MyClass(); //通过构造函数MyClass创建一个对象mc1
MyClass.prototype.a = 123; //向MyClass的原型对象中添加属性a
console.log(mc.a); //123
console.log("a" in mc); // true
这是因为使用in检查对像中是否有某个属性时,如果对象中没有但是原型中有,也会返回true。
可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性。使用该方法时,只有对象自身中含有属性时才会返回true。
1 | function MyClass() { //创建构造函数 |
1 | function MyClass() { //创建构造函数 |
那又有疑问了,mc中有hasOwnProperty()吗?好像我们没有添加,那mc的原型对象里有hasOwnProperty()吗?好像我们也没有添加。测试一下:
1 | function MyClass() { //创建构造函数 |
那连mc原型对象里也没有,那hasOwnProperty()到底是谁的?
我们想一下,原型对象也是对象,那原形对象也没有原型对象呢?我们试一下:
1 | function MyClass() { //创建构造函数 |
看来这个hasOwnProperty()是属于原型对象的原型对象。
说明一下:
原型对象也是对象,所以它也有原型,
- 当我们使用一个对象的属性或者方法时,会先在自身中寻找,如果有直接使用。
- 如果自身中没有则去原型对象中寻找,如果原型对象中有,则使用。
- 如果没有则去原型的原型中寻找,直到找到Object对象的原型。
- Object对象的原型没有原型,如果在Object中依然没有找到,则会返回Undefined。
一个对象的属性的寻找过程由以下几个部分组成:
- 寻找对象属性的数据描述符(writable, value)或存取描述符(getter, setter),如果查询到了,则返回 对应的值。如果查询不到,则进入第2步骤。
- 寻找对象属性的值是否有被显示定义 (可以通过 Object.getOwnPropertyNames)检测,如果对象属性定义了,则返回定义的值。 如果没有,则进入第3步骤。
- 寻找对象的隐藏原型__proto__对象的属性,规则同1,2步骤。如果还未找到,则重复第3步骤, 直到__proto__ 为null 为止。
愿你的坚持终有收获。
Use this card to join the candyhome and participate in a pleasant discussion together .
Welcome to aqing's candyhome,wish you a nice day .