1.类与实例
1.1 类的声明
function Animal() { this.name = 'name'; }
1.2 ES6中类的声明
class Animal2{ constructor() { this.name = name; } }
1.3 类的实例化
new Animal(); //没有参数的话,括号可以不要
2.类与继承
2.1 借助构造函数实现继承
function Parent1() { this.name = 'parent1'; } function Child1() { Parent1.call(this); this.type = 'child1'; }
缺点:只实现了部分继承,继承不到父类原型上的方法。
测试代码如下,运行时会报错。child1没有继承到父级原型上的方法。
function Parent1() { this.name = 'parent1'; } Parent1.prototype.say = function() {} function Child1() { Parent1.call(this); this.type = 'child1'; } console.log(new Child1(), new Child1().say());
2.2 借助原型链实现继承
这种方式可以解决第一种方法无法继承父级对象原型上方法的问题。
function Parent2() { this.name = 'parent2'; } function Child2() { this.type = 'child2'; } Child2.prototype = new Parent2();
缺点:改一个对象属性,另一个也跟着改变。原型对象是共用的。
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); //[1,2,3,4] [1,2,3,4]
2.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);
2.4 组合继承的优化一
优化:父类构造函数只执行了一次
function Parent4() { this.name = 'Parent4'; } function Child4() { Parent4.call(this); this.type = 'child4'; } Child4.prototype =Parent4.prototype;
缺点:不能区分是直接由parent4实例化的还是child4实例化的。
用instanceof判断:
function Parent4() { this.name = 'Parent4'; } function Child4() { Parent4.call(this); this.type = 'child4'; } Child4.prototype =Parent4.prototype; var s4 = new Child4(); var s5 = new Child4(); console.log(s4 instanceof Child4, s4 instanceof Parent4); //true true
用constructor构造器判断,它是Parent4的实例。这显然不是我们想要的。此时不难判看出,上一个组合方法中也会是这样的结果。它们直接拿的是父类的实例,它们没有自己的constructor,它的constructor是从父类实例继承的,也就是原型链的上一级拿来的,所以是父类的constructor。
console.log(s4.constructor); // Parent4对象
2.5 组合继承优化二
function Parent5() { this.name = 'Parent5'; } function Child5() { Parent5.call(this); this.type = 'child5'; } Child5.prototype =Object.create(Parent5.prototype); Child5.prototype.constructor = Child5;
通过创建中间对象的方法,就把两个原型对象区分开,这个中间对象还具备一个特性,它的原型对象是父类的原型对象。这样在原型链上就连起来了,通过再给child5这个原型对象上的constructor作修改,那么就能正确区分父类和子类构造函数了。
至此,问题就全部解决了。
var s7 = new Child5(); console.log(s7 instanceof Child5, s7 instanceof Parent5); console.log(s7.constructor);
控制台打印结果: