这一篇将介绍js继承的几种方式。
原型继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| let Super = function(name = 'eric') { this.name = name; this.getName = function() { return this.name; } } Super.prototype.hello = function() { alert('Hello, ' + this.name + '!'); } let Sub = function(sex = 'male') { this.sex = sex; } Sub.prototype = new Super('eric'); Sub.prototype.constructor = Sub let sub1 = new Sub('male') sub2 = new Sub('female');
console.log(sub1.getName()); console.log(sub1.hasOwnProperty('name')) console.log(sub1.getName === sub2.getName) console.log(sub1.hello())
|
可以看出,父元素的方法和属性都得到了复用。但是子类实例没有自己的属性。父元素的原型也被继承了。
构造函数继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| let Super = function(name = 'eric') { this.name = name; this.getName = function() { return this.name; } } Super.prototype.hello = function() { alert('Hello, ' + this.name + '!'); } let Sub = function(name, sex) { Super.call(this, name); this.sex = sex; } let sub1 = new Sub('eric', 'male'); let sub2 = new Sub('ada', 'female'); console.log(sub1.name) console.log(sub1.hasOwnProperty('name')) console.log(sub1.getName === sub2.getName) console.log(sub1.hello())
|
子类的每个实例都有自己的属性(name), super相当于把父元素的属性和方法传给子类(不包括原型的方法)。这些方法和属性属于子类自身的了。
上面两种组合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| let Super = function(name = 'eric') { this.name = name; this.getName = function() { return this.name; } } Super.prototype.hello = function() { alert('Hello, ' + this.name + '!'); } let Sub = function(sex = 'male') { Super.call(this, 'eric') this.sex = sex; } Sub.prototype = new Super('eric'); Sub.prototype.constructor = Sub let sub1 = new Sub('male') sub2 = new Sub('female');
console.log(sub1.getName()); console.log(sub1.hasOwnProperty('name')) console.log(sub1.getName === sub2.getName) console.log(sub1.hello())
|
综合了上面两种的优点,既让子类有了自己的属性,也实现了父类和原型方法的继承。
因为父类构造函数被执行了两次,子类的原型对象(Sub.prototype)中也有一份父类的实例属性(name),而且这些属性会被子类实例(sub1,sub2)的属性覆盖掉(即通过sub1.name访问不到Sub.prototype上的name属性),也存在内存浪费。
寄生组合式继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| let Super = function(name = 'eric') { this.name = name; this.getName = function() { return this.name; } } Super.prototype = { constructor: Super, hello() { alert('Hello, ' + this.name + '!'); } } let Sub = function(sex, name) { Super.call(this, name); this.sex = sex; }
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub; let sub1 = new Sub('male') sub2 = new Sub('female');
console.log(sub1.getName()); console.log(sub1.hasOwnProperty('name')) console.log(sub1.getName === sub2.getName) console.log(sub1.hello())
|
就相当于用call把父元素的属性和方法给子类,然后父元素的原型给子类,这样子类可以用父元素的原型里面的方法。
class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| class Super { constructor(props = { name: 'eric' }) { console.log(props); this.name = props.name; } setName(name) { this.name = name; } getName() { return this.name; } } class Sub extends Super { constructor(props) { super(props = { name: 'bob' }); this.sex = props.sex; } } let sub1 = new Sub({ name: 'eric', sex: 'male' }) let sub2 = new Sub({ name: 'eric', sex: 'female' })
console.log(sub1.hasOwnProperty('name')) sub1.setName('ada'); console.log(sub1.getName(),sub2.getName()) console.log(sub1.getName === sub2.getName) console.log(Sub.prototype.sex)
|
可以看出es6 的class是组合继承的特点。