1. 程式人生 > 其它 >JS類的建立和繼承總結(含ES6 class類)

JS類的建立和繼承總結(含ES6 class類)

1. 類的建立

1.1 ES5中類的建立 (即建構函式的建立)

// 注:ES5中類的建立其實就是建構函式的建立,和函式建立相同,只是函式名首字母一般推薦大寫
// 1. 方法一:直接在建構函式中直接定義屬性和方法
//    優點:可以傳參
//    缺點:每 new 一個例項,就會複製一份建構函式裡面的屬性和方法,費記憶體
function Star(name, sex) {
  this.name = name
  this.sex = sex
  this.role = '明星'
  this.singSong = function () {
    console.log('他唱的歌叫' + song)
  }
}
// 2. 方法二:先建立一個建構函式(類),然後在原型上掛載屬性和方法
//    優點:繼承公共屬性或方法的時候不費記憶體,因為它相當於引用,也就是指向的問題,並非“複製”
//    缺點:定義非公共屬性時費記憶體,而且不高效,即不能夠用傳參來“定製”屬性值
function Star() {}
Star.prototype.name = '劉德華'
Star.prototype.sex = '男'
Star.prototype.role = '明星'
Star.prototype.singSong = function (song) {
  console.log('他唱的歌叫' + song)
}
// 3. 總結及注意:
//    3.1 一般將私有屬性(即屬性值不同的)定義在建構函式(類)中,公共的方法定義在原型上
//    3.2 當建構函式中的屬性或方法和原型上的相同時,建構函式內的優先順序高

// 4. 舉例:
//    4.1 在建構函式中定義“私有”屬性
function Star(name, sex) {
  this.name = name
  this, (sex = sex)
}
//     4.1 在原型上定義公共屬性或方法
//    寫法1:
Star.prototype.role = '明星'
Star.prototype.singSong = function (song) {
  console.log('他唱的歌叫' + song)
}
//    寫法2:
Star.prototype = {
  role: '明星',
  singSong: function (song) {
    console.log('他唱的歌叫' + song)
  }
}

1.2 ES6中類的建立和繼承

// 建立類
class Star {
  // 構造方法,固定寫法
  constructor(name, sex) {
    this.name = name
    this.sex = sex
    this.role = '明星'
  }
  // 公共方法
  getName(name) {
    console.log('他的名字是' + name)
  }
}

// 子類繼承
class LiuDeHua extends Star {
  constructor(name, sex, role) {
    super(name, sex) // super 表示繼承父類的屬性
    this.role = role // 表示子類自己新增的
  }
  sing(song) {
    // 子類自己的方法
    console.log('我唱了' + song)
  }
}

// 下面測試
let ldh = new LiuDeHua('劉德華', '男', '四大天王')
console.log(ldh.name) // 劉德華
ldh.getName('liudehua') // 他的名字是liudehua
ldh.sing('忘情水') // 我唱了忘情水

 

2. ES5中類的繼承

2.1 基於原型鏈繼承

// 1. 定義好的類
function Star(name, sex) {
  this.name = name
  this.sex = sex
}
Star.prototype.role = '明星'
Star.prototype.singSong = function (song) {
  console.log('他唱的歌叫' + song)
}

// --------------------------------------------------------

// 2. 用原型鏈的方式實現繼承
//   2.1 建立一個空的函式(子函式)
function LiuDeHua() {}
//   2.2 讓子函式的原型指向建構函式,相當於在原型上 new 一個建構函式
//       讓子函式的原型上擁有建構函式的全部屬性和方法
LiuDeHua.prototype = new Star('劉德華', '男')
//   2.3 new 一個例項,然後呼叫子函式(子類)的屬性和方法
let ldh = new LiuDeHua()
console.log(ldh.name) // 輸出 劉德華
console.log(ldh.role) //輸出 明星
ldh.singSong('忘情水') // 輸出 忘情水

// 3. 總結
// 3.1 優點:
// (1)簡單,子類例項既是子類的例項也是父類的例項
// (2)父類在原型上新增的方法和屬性都能被子類獲取到

// 3.2 缺點:
// (1)傳參不方便,只能在繼承的時候傳遞引數,無法在建立子類例項的時候傳入引數
// (2)無法實現多繼承(一個子類繼承多個父類)
// (3)原型上的屬性、方法被所有的例項共享
// 1. 定義好的類
function Star(name, sex) {
  this.name = name
  this.sex = sex
}
Star.prototype.role = '明星'
Star.prototype.singSong = function (song) {
  console.log('他唱的歌叫' + song)
}

// 2. 用原型鏈的方式實現繼承
//   2.1 建立一個空的函式(子函式)
function LiuDeHua() {}
//   2.2 讓子函式的原型指向建構函式,相當於在原型上 new 一個建構函式
//       讓子函式的原型上擁有建構函式的全部屬性和方法
LiuDeHua.prototype = new Star('劉德華', '男')
//   2.3 呼叫子函式(子類)的屬性和方法
console.log(LiuDeHua.prototype.name) // 輸出 劉德華
console.log(LiuDeHua.prototype.role) //輸出 明星
LiuDeHua.prototype.singSong('忘情水') // 輸出 忘情水

// 3. 總結
// 3.1 優點:
// (1)簡單,子類例項既是子類的例項也是父類的例項
// (2)父類在原型上新增的方法和屬性都能被子類獲取到

// 3.2 缺點:
// (1)傳參不方便,只能在繼承的時候傳遞引數,無法在建立子類例項的時候傳入引數
// (2)無法實現多繼承(一個子類繼承多個父類)
// (3)原型上的屬性、方法被所有的例項共享

2.2 基於構造(.call .apply)繼承

// 1. 定義好的 Star 類
function Star(name) {
  this.name = name
}
Star.prototype.role = '明星'

// 2. 定義好的 Star 類
function Sing(song) {
  this.sing = function () {
    console.log('他唱的歌有' + song)
  }
}

// ------------------------------------------

// 3. 用構造繼承方式繼承,也就是用 .call .apply,未用原型
function Stars(name, song) {
  Star.call(this, name) // 繼承 Star 建構函式的屬性或方法
  Sing.call(this, song) // 繼承 Sing 建構函式的屬性或方法
}

// 4. new 一個例項,並呼叫屬性和方法
let LiuDeHua = new Stars('劉德華', '忘情水') // new 例項的時候傳參
console.log(LiuDeHua.name) // 輸出 劉德華
console.log(LiuDeHua.role) // undefined,也就是不能繼承原型上的屬性和方法
LiuDeHua.sing() // 輸出 忘情水

// 4. 總結:
//    特點:可以實現多繼承
//    缺點:只能繼承父類例項的屬性和方法,不能繼承原型上的屬性和方法

2.3 組合繼承

 同時使用上面構造繼承原型鏈繼承

優點: 1.傳參方便,可以在建立例項時傳參

            2.可以實現多繼承

缺點: 1.無法繼承父類原型上的屬性和方法 

            2.因為原型上沒有方法,所以無法實現函式複用

2.4 寄生組合繼承(推薦)

// 1. 定義好的類
function Star(name, sex) {
  this.name = name
  this.sex = sex
}
Star.prototype.role = '明星'
Star.prototype.singSong = function (song) {
  console.log('他唱的歌叫' + song)
}

// 2. 寄生組合繼承方式繼承
//    2.1 先用構造繼承,繼承例項上的屬性和方法,不繼承原型上的
function LiuDeHua(name, sex) {
  // Star.call(this, arguments)// 使用 arguments,輸出[Arguments] { '0': '劉德華', '1': '男' }形式
  Star.call(this, name, sex)
}

//    2.2 再用寄生繼承,只繼承原型上的
(function () {
  let temp = function () {}
  temp.prototype = Star.prototype
  LiuDeHua.prototype = new temp()
})()

// 3. new 例項,呼叫屬性和方法測試
let ldh = new LiuDeHua('劉德華', '男')
console.log(ldh.name) // 劉德華
ldh.singSong('忘情水') // 他唱的歌叫忘情水
 
// 4. 總結:
// 通過寄生方式,砍掉父類的例項屬性,這樣,在呼叫兩次父類的
// 構造的時候,就不會初始化兩次例項方法/屬性
// 實現麻煩