js原型与原型链
一、JS原型与原型链
(1)JS原型
①函数对象
所有引用类型(Object,Array,Date,Function),都拥有__ proto__(隐式原型)属性,属性值为一个普通的对象。
所有函数都拥有prototype属性(显示原型),为函数所特有的属性。
原型对象:即prototype属性指向的对象叫做原型对象,当申明一个函数的时候,编译器会自动帮你创建一个与之对应的对象称为原型对象。 每个函数都有一个prototype属性,它是一个指针,指向一个对象,这个对象包含了所有实例共享的属性和方法。
②构造函数
首先先构造一个函数。
1 |
|
往原型对象里面添加成员。
1 |
|
③实例化对象
通过new关键字,构建p1,p2实例对象。
1 |
|
此时p1,p2可以访问构造函数的原型上的方法,并且p1.eat === p2.eat。
1 |
|
但注意如果没有使用往原型对象里面添加成员的方法,直接在构造函数内添加方法,在通过实例化对象调用该方法,此时二者不相等!
1 |
|
这是因为,构造函数的定义方法在实例对象上都创建一遍,上方代码p1和p2都有名为study的方法,但是这两个方法不是同一个Function的实例。实例共享的属性和方法都放在原型对象上,有效解决构造函数内存资源浪费 + 全局变量污染。
④ __proto__
属性
__proto__
: 属于实例对象,可以让实例对象访问原型对象。
1 |
|
⑤constructor属性
constructor属性,属于原型对象,指向构造函数。
1 |
|
p1.__proto__.constructor
可以直接写为p1.constructor
,p1可以直接访问原型对象。
(2)js原型链
①引用一个例子
原型链:每一个对象都有原型,而原型也是对象,也会有自己的原型。以此类推形成链式结构,称之为原型链。
观察以下代码输出结果。
1 |
|
引用p1.eat()
,p1的构造函数的原型对象有eat方法,正常输出。但是p1的构造函数的原型对象上没有learn和toString方法,引用p1.learn()
,输出报错,调用p1.toString()
输出却不报错。这是因为,当调用p1.toString()
方法时,p1的构造函数的原型对象上没有toString方法,但是p1的构造函数的原型对象的原型对象上有toString方法。
检查p1原型的原型。
检查p1原型的原型的原型。
发现原型链终点为null
。
②画出原型链。
二、总结
(1)、我们需要牢记两点:①__proto__
和constructor
属性是对象所独有的;②prototype
属性是函数所独有的,因为函数也是一种对象,所以函数也拥有__proto__
和constructor
属性。
(2)、prototype
属性的作用就是让该函数所实例化的对象们都可以找到公用的属性和方法,即p1.__proto__
=== Person.prototype
。
(3)、__proto__
属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__
属性所指向的那个对象(父对象)里找,一直找,直到__proto__
属性的终点null,再往上找就相当于在null上取值,会报错。通过__proto__
属性将对象连接起来的这条链路即我们所谓的原型链。
(4)、constructor
属性的含义就是指向该对象的构造函数,所有函数(此时看成对象了)最终的构造函数都指向Function。