基础
__proto__和prototype的区别和关系
在了解原型之前,必须先弄清几个概念:
对象
js中的对象分为两种:
-
普通对象
- 普通对象:有__proto__属性(指向其构造函数的原型对象),没有prototype属性。
- 原型对象:(Person.prototype 原型对象),原型对象还有一个constructor属性(指向构造函数对象)
-
函数对象
- 通过new Function()创建的都是函数对象。函数对象拥有__proto__、prototype属性。Function, Object, Array, Date, String, 自定义函数
-
原型对象
每创建一个函数都会有一个prototype属性,这个属性是一个指针,指向一个对象。原型对象包含特定类型的所有实例共享的属性和方法。
来看看一张比较容易看得懂的图,深度理解对象的原型:
- 在JS里,万物皆对象。
方法(Function)是对象,方法的原型(Function.prototype)是对象。
对象具有__proto__属性,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这样保证了实例能够访问在构造函数原型中定义的属性和方法。
- 方法(Function)
方法也是对象,除了和其他对象一样拥有__proto__属性之外,还有自己特有的属性—-原型属性(prototype)。这个属性是一个指针,指向一个对象(及原型对象),这个对象包含了所有实例共享的属性和方法。原型对象也有一个属性,叫做constructor,这个属性包含了一个指针,指回原构造函数。
再来看看上面那张图:
- Foo() 构造函数
构造函数Foo()的原型属性Foo.prototype指向原型对象,在原型对象里有共有的属性和方法,所有构造函数声明的实例(这里是f1, f2)都可以共享这个方法。
- 原型对象Foo.prototype
Foo.prototype保存这实例共享的方法,有一个指回构造函数的指针constructor.
- 对象实例f1, f2
f1, f2是对象Foo的实例,对象的实例有__proto__属性,指向对象的构造函数的原型对象,这样就可以访问原型对象的所有方法了。
BTW,Foo()构造函数除了是方法,也是对象(函数对象),也有__proto__属性,那么这个__proto__指向谁呢?
函数的__proto__属性指向函数的构造函数的原型对象。函数的构造函数就是Function,因此,函数的__proto__属性指向Function.prototype.
除了Foo(), Function()和Object()都是一样的道理。
原型对象也是对象,那它(比如,Foo.prototype,Function.prototype)的__proto__属性指向谁呢?
同理,指向它的构造函数的原型对象,这里是Object.prototype。
最后,Object.prototype的__proto__属性指向null。
总结:
1. 对象有属性__proto__,它指向对象的构造函数的原型对象。
2. 函数有prototype属性,函数的prototype属性指向函数的原型对象,因为函数也是对象,函数的__proto__同样指向函数的构造函数的原型对象。
原型链
首先,看一张图了解一下实例、原型对象和构造函数之间的关系。
从上图可以看出,每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针(constructor),而实例对象都包含一个指向原型对象的__prototype__内部指针。
如果让原型对象等于另一个类型的实例,那么此时的原型对象将包含一个指向另一个原型对象的(proto)指针,另一个原型对象也包含指向另一个构造函数的指针(constructor)。加入
另一个原型又是另一个类型的实例。。。。。。这就构成了实例与原型的链条。也就是原型链。
看图:
举例说明:
function Animal() {
this.type = 'animal';
}
Animal.prototype.getType = function() {
return this.type;
}
function Dog() {
this.name ='wangwang';
}
Dog.prototype = new Animal();
var wangcai = new Dog();
wangcai.getType(); // 'animal'
// 原型链的关系:
wangcai.__proto__ === Dog.prototype
Dog.prototype.__proto__ === Animal.prototype
Animal.prototye.__proto__ === Object.prototype
Object.prototype.__proto__ === null
js中实现继承主要依靠原型链查找
原型继承(prototype-based inheritance)的步骤大概是这样:
-
每一个函数F都有一个原型对象(prototype)F.prototype
-
每一个函数都可以通过new 关键字来创建一个类构造函数,new F()会产一个一个对象O
-
在调用对象的某个属性和方法时,比如O.xxx, 首先会查找这个对象自身有没有这个属性或者方法,没有就会去(这个对象的构造函数的原型对象中查找),也就是找O的构造函数F的原型对象F.prototype.xxx。
-
原型对象F.prototype.xxx也是一个对象,查找F.prototype.xxx会重复步骤3.