javascript 中各類繼承方式的優缺點

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;

五 es6中的class

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

相關文章
相關標籤/搜索