1、类的声明
2、生成实例
3、如何实现继承
4、继承的几种方式
1、类的声明有哪些方式
<script type="text/javascript"> //类的声明 function Animal() { this.name = 'name' } // es6中的class的声明 class Animal2 { constructor() { this.name = name } } </script>
2、怎么实例化这个对象
console.log(new Animal(), new Animal2());
3、js有几种继承方式
1)
/** * 借助构造函数实现继承 */ function Parent1() { this.name = 'parent1' } function Child1() { Parent1.call(this); this.type = 'child1'; } console.log(new Child1());
这样就实现了一个继承了。重点看这个Parent1.call,他改变了js运行的上下文,通过这个调用,改变了Parent1的this指向。也就是父类的所有属性都指向了子类的这个实例
缺点:这个构造函数是有自己的原型链的,也就是有自己的prototype属性,虽然说Parent1的属性指向了Child1这个实例,但是他的prototype并没有被Child1所继承。比如说
function Parent1() { this.name = 'parent1' } Parent1.prototype.say = function(){};
function Child1() { Parent1.call(this); this.type = 'child1'; } console.log(new Child1());
看下,是没有say方法的,说明不会继承父类的原型链,所以说,这种继承只是部分继承,如果父类还有一些方法,就拿不到
2)
/** * 借助原型链实现即成 */ function Parent2() { this.name = "parent2"; } function Child2() { this.type = "child2" } Child2.prototype = new Parent2(); console.log(new Child2());
这个方法弥补第一种方式的不足,我们说任何一个构造函数都有一个prototype这个属性的,这个属性的作用是访问他的实例能访问到原型对象上。
如图,我们看到Child2的__proto__指向的是Parent2的原型对象,prototype是Child2的一个属性,这个属性是个对象,将new Parent2()这个对象赋值给了Child2,那么new一个Child2后,访问Child2.name,在Child2里面没有找到,就会去__proto__找,也就是prototype找,也就是Parent2这个实例找,这样就实现了继承
这个继承方式的缺点:在Parent2里面添加,this.play = [1,2,3];,然后new多个Child2的实例,发现他们都指向了同一个play。当s1改变了play,第二个对象跟着被改变了
比如
/** * 借助原型链实现即成 */ function Parent2() { this.name = "parent2"; this.play = [1,2,3] } function Child2() { this.type = "child2" } Child2.prototype = new Parent2(); var s1 = new Child2(); var s2 = new Child2(); s1.play.push(4); console.log(s1.play,s2.play);
3)
/** * 组合方式(结合构造函数 和 原型链的优点) */ function Parent3 () { this.name = 'parent3'; } function Child3 () { Parent3.call(this); this.type = 'child3' } Child3.prototype = new Parent3();
我们再加上方法,看看是否能避免上面的问题呢
/** * 组合方式(结合构造函数 和 原型链的优点) */ function Parent3 () { this.name = 'parent3'; this.play = [1,2,3]; } function Child3 () { Parent3.call(this); this.type = 'child3' }
Child3.prototype = new Parent3(); var s3 = new Child3(); var s4 = new Child3(); s3.play.push(4); console.log(s3.play,s4.play);
这个时候发现就不一样了,避免了eg2的缺点,这种组合方式结合了优点,弥补了缺点,这是通常实现继承的方式
缺点:执行了两次父类的原型链,第一次Parent2.call()。第二次 new Parent3()
4)
/** * 组合继承的优化 */ function Parent4 () { this.name = 'parent4'; this.play = [1,2,3]; } function Child4 () { Parent4.call(this); this.type = 'child4' } Child4.prototype = Parent4.prototype; var s5 = new Child4(); var s6 = new Child4();
这样父类的原型链只执行了一次,但是还剩下一个问题,s5,s6都是父类的实例,没有自己的实例,prototype里面有个contron指明是哪个的实例,而子类的prototype拿的直接是父类的prototype,所以当然拿的是父类的构造函数
/** * 组合继承的优化2 */ function Parent5 () { this.name = 'parent5'; this.play = [1,2,3]; } function Child5 () { Parent5.call(this); this.type = 'child5' } Child5.prototype = Object.create(Parent5.prototype); Child5.prototype.constructor = Child5;
Object.create方法创建原型对象,Parent5.prototype就是create方法的一个参数,一层一层往上找实现了继承,同时完成了继承,这个就是实现继承的完美方式