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