javascript中实现继承的方式有不少种,通常都是经过原型链和构造函数来实现。下面对各类实现方式进行分析,总结各自的优缺点。
let Super = functioin(name = 'eric') { this.name = name; this.getName = function() { return 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()); // eric console.log(sub1.hasOwnProperty('name')) // false 说明是继承而来的属性 console.log(sub1.getName === sub2.getName) // true,复用了方法
优势:父类的方法(getName)获得了复用。javascript
缺点:同理父类的属性(name)也是复用,即子类实例没有本身的属性。java
let Super = function(name = 'eric') { this.name = name; this.getName = function() { return 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) // 'eric' console.log(sub1.hasOwnProperty('name')) // true 说明不是继承而来,是本身的属性 console.log(sub1.getName === sub2.getName) // false 方法没有获得复用
优势:子类的每一个实例都有本身的属性(name),不会相互影响。es6
缺点:可是继承父类方法的时候就不须要这种特性,没有实现父类方法的复用。浏览器
let Super = function(name = 'eric') { this.name = name; } Super.prototype = { constructor: Super, getName() { return this.name; } } let Sub = function(sex) { 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.name); // 'eric' console.log(sub1.hasOwnProperty('name')); // true 本身的属性 console.log(sub1.getName === sub2.getName); // true 复用了方法 console.log(Sub.prototype) // { name: "eric" } console.log(sub1) // { name: "eric", sex: "male" }
优势:继承了上述两种方式的优势,摒弃了缺点,复用了方法,子类又有各自的属性。函数
缺点:由于父类构造函数被执行了两次,子类的原型对象(Sub.prototype)中也有一份父类的实例属性(name),并且这些属性会被子类实例(sub1,sub2)的属性覆盖掉(即经过sub1.name访问不到Sub.prototype上的name属性),也存在内存浪费。this
let Super = function(name = 'eric') { this.name = name; } Super.prototype = { constructor: Super, getName() { return this.name; } } let Sub = function(sex, name) { Super.call(this, name); this.sex = sex; } // 组合继承的缺点就是在继承父类方法的时候调用了父类构造函数,从而形成内存浪费, // 如今只要解决了这个问题就完美了。那在复用父类方法的时候, // 使用Object.create方法也能够达到目的,没有调用父类构造函数,问题解决。 Sub.prototype = Object.create(Super.prototype); // 固然这个地方也可使用Object.setPrototypeOf(Sub.prototype, Super.prototype) // 由于更改一个对象的隐士原型(__proto__)对浏览器和js引擎都是很慢对操做,因此建议使用Object.create()建立一个具备指定原型对象的新对象 Sub.prototype.constructor = Sub;
class Super() { constructor(props = { name: 'eric' }) { this.name = props.name; } setName(name) { this.name = name; } getName() { return this.name; } } class Sub extends Super { constructor(props) { super(props = { sex: 'male' }); // 建立实例,继承父类属性和方法 this.sex = props.sex; } } let sub1 = new Sub({ name: 'eric', sex: 'male' }) let sub2 = new Sub({ name: 'eric', sex: 'female' }) sub1.setName('ada'); console.log(sub1.getName(),sub2.getName()) // ada,eric,属性没复用,各自实例都有本身的属性。 console.log(sub1.getName === sub2.getName) // true; 复用了父类的方法 console.log(Sub.prototype.sex) // undefined // 子类原型对象上没有父类构造函数中赋值的属性,不是组合式继承
由以上结果能够看到es6中的class只不过是一种语法糖,经过上面的验证得知符合寄生组合继承的特色,但这只是猜想,class具体都作了哪些操做还不是很清楚,后面有时间,对class作一下研究。prototype