再说说__proto__和prototype以及js的继承
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了再说说__proto__和prototype以及js的继承相关的知识,希望对你有一定的参考价值。
1.__proto__和prototype
JS中的原型链已经是一个老生常谈的问题,毕竟也是JS 这门语言的特色之一了。
首先“万物皆对象“,虽然这句话一直有争议,但是有它的道理的,null类型这些的争论这里就不说了。
对象中有个属性__proto__,被称为隐式原型,这个隐式原型指向构造改对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。这个实例可能是如图中的new Foo()出来的实例。
构造该对象的f1,f2构造函数是fuction Foo(),它的原型是Foo.prototype,那么f1,f2就指向了构造该对象的构造函数的原型,也就是Foo.prototype,那么构造函数Foo()它的proto指向哪里了,还是找它的构造函数,它的构造函数是Function(),那么它的proto就指向了Fuction.prototype,沿着proto这条路最上就是Object.prototype,Object的proto就是null了。
刚刚说的对象有个proto属性,方法也是对象,方法中除了有proto之外(这个proto指向构造该函数/对象的构造原型,也就是上一层了),还有prototype,这个属性就是原型属性,他是一个指针,指向一个对象,这个对象就叫原型对象,这里放着包含所有实例共享的属性和方法,这个原型对象里面有一个属性constructor,这个属性也包含一个指针,指回了原构造函数。
1.构造函数Foo()构造函数的原型属性Foo.prototype指向了原型对象,在原型对象里有共有的方法,所有构造函数声明的实例(这里是f1,f2)都可以共享这个方法。
2.原型对象Foo.prototypeFoo.prototype保存着实例共享的方法,有一个指针constructor指回构造函数。
3.实例f1和f2是Foo这个对象的两个实例,这两个对象也有属性__proto__,指向构造函数的原型对象,这样子就可以像上面1所说的访问原型对象的所有方法。
4.构造函数Foo()除了是方法,也是对象,它也有__proto__属性,指向谁呢?指向它的构造函数的原型对象。函数的构造函数不就是Function嘛,因此这里的__proto__指向了Function.prototype。其实除了Foo(),Function(), Object()也是一样的道理。原型对象也是对象,它的__proto__属性,又指向谁呢?同理,指向它的构造函数的原型对象。这里是Object.prototype.最后,Object.prototype的__proto__属性指向null。
5.对象有属性__proto__,指向该对象的构造函数的原型对象。
方法除了有属性__proto__,还有属性prototype,prototype指向该方法的原型对象。
6.再看图。
2.继承
1.父类的实例作为子类的原型
function Animal(name) {
// 属性
this.name = name || "Animal";
// 实例方法
this.sleep = function() {
console.log(this.name + '正在睡觉!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃' + food);
};
function Cat() {
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
var cat = new Cat();
console.log(cat.name);
console.log(cat.eat('fish'));
console.log(cat.sleep());
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
**cat.__proto__ === Cat.prototype //true**
构造cat对象的构造函数是Cat(),它的原型是Cat.prototype,那么隐式原型__proto__就指向构造该对象的构造函数的原型对象,那么cat的__proto__就指向的是Cat.prototype。
**Cat.prototype.__proto__ === Animal.prototype //true**
原型对象也是对象,是对象就有__proto__,Animal的实例返回给了这个原型对象,那么这个原型对象的隐式原型__proto__就指向的是构造该对象的构造函数的原型,我们看看这个原型对象的构造函数是谁
那么Animal()构造函数的原型对象就是Animal.prototype了。自然就有上面true的结果了。
这种方法的继承的缺点:
- 是来自原型对象的引用属性是所有实例共享的
- 创建子类实例时,无法向父类构造函数传参
- 不能多继承
2.构造继承
没有用到原型,使用父类的构造函数来增强子类实例,等于直接是复制父类的实例属性给子类。
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
特点:
解决了1中,子类实例共享父类引用属性的问题
创建子类实例时,可以向父类传递参数(通过call的后面参数)
可以实现多继承(call多个父类对象)
缺点:
实例并不是父类的实例,只是子类的实例
只能继承父类的实例属性和方法,不能继承原型属性/方法
无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
以上是关于再说说__proto__和prototype以及js的继承的主要内容,如果未能解决你的问题,请参考以下文章
原型链继承中的prototype__proto__和constructor的关系
对象中prototype与__proto__与从cinstructor的作用和区别
关于prototype和__proto__ 以及 constructor的文字总结