JS類的建立和繼承總結(含ES6 class類)
阿新 • • 發佈:2022-05-05
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. 總結:
// 通過寄生方式,砍掉父類的例項屬性,這樣,在呼叫兩次父類的
// 構造的時候,就不會初始化兩次例項方法/屬性
// 實現麻煩