JS中的面向?qū)ο缶幊讨^承機(jī)制

一.面向?qū)ο缶幊?/strong>
面向?qū)ο缶幊?OOP--Object Oriented Programming)是一種抽象方式創(chuàng)建模型的編程方式。繼承,封裝,多態(tài)是OOP的三大基本特征。許多主流編程語(yǔ)言都支持OOP。例如Java,c++中通過(guò)new調(diào)用‘類’來(lái)創(chuàng)造實(shí)例,但是學(xué)習(xí)了JS的同學(xué)知道ES6之前中沒(méi)有類的概念,那么JS怎么實(shí)現(xiàn)面向?qū)ο缶幊痰哪兀?br> JS實(shí)現(xiàn)OOP是通過(guò)原型鏈實(shí)現(xiàn)的,原型鏈的概念在這里不詳細(xì)講述,大家記住一個(gè)例子即可:

function._proto_ ==== Function.prototype

JS也通過(guò)new來(lái)創(chuàng)建實(shí)例,但是后面調(diào)用的不是類,而是構(gòu)造函數(shù)。
下面我們通過(guò)封裝繼承一個(gè)‘類’的例子來(lái)詳細(xì)闡述。
二.ES5實(shí)現(xiàn)封裝 繼承一個(gè)類
1.封裝
我們知道寶馬,奔馳,奧迪等都是汽車(chē),汽車(chē)這個(gè)概念就是一個(gè)類,抽象出來(lái)代表了日常生活中的許多具體事物。

//汽車(chē) 構(gòu)造函數(shù)
function Car (name,color){
this.name = name;
this.color = color;
}

2.prototype模式
汽車(chē)的屬性有很多,不僅僅是名字和顏色,如果全部寫(xiě)在構(gòu)造函數(shù)里,創(chuàng)建實(shí)例時(shí)再去一一地調(diào)用太過(guò)于麻煩,于是JS規(guī)定了一個(gè)prototype模式,每個(gè)構(gòu)造函數(shù)都有一個(gè)prototype屬性指向原型,“類”里面一些共有的屬性可以放在原型里面。例如汽車(chē)類的車(chē)型,動(dòng)能方式等屬性都是共有屬性可以放在原型里。

Car.prototype.type = 'car';

Car.prototype.getType = function () {
  return this.type;
};

Car.prototype.setType = function (newType) {
  this.type = newType;
  return newType;
};

Car.prototype.getName = function () {
  return this.name;
};

Car.prototype.setName = function (newName) {
  this.name = newName;
  return newName;
};

3.new關(guān)鍵字
JS中通過(guò)new關(guān)鍵字創(chuàng)建實(shí)例。

 var car1 = new Car('寶馬','red');
console.log(car1.type) // 'car'

這里,我們思考一個(gè)問(wèn)題:使用new創(chuàng)建實(shí)例,new到底做了些什么呢?
使用new創(chuàng)建實(shí)例,實(shí)際上是幫助我們減少了四行代碼:
1.new創(chuàng)建了臨時(shí)對(duì)象,可以使用this訪問(wèn)到臨時(shí)對(duì)象
2.new也幫我們r(jià)eturn了這個(gè)臨時(shí)對(duì)象
3.new指定了原型的名字--prototype
4.new創(chuàng)建實(shí)例,自動(dòng)綁定原型。

car1._proto_ === Car.prototype

4.繼承
我們實(shí)現(xiàn)一個(gè)‘子類’,繼承‘汽車(chē)類’

function SUV ( price) {
 Car.call(this, 'suv');   // 將 Car 類的屬性和方法賦值給 SUV
  this.price = price;
}
SUV.prototype = new Car('suv');// 繼承汽車(chē)類的原型鏈


SUV.prototype.getPrice = function () {
  return this.price;
};
SUV.prototype.setPrice = function (newPrice) {
  this.price = newPrice;
  return newPrice;
};
//創(chuàng)建子類實(shí)例
var suv1 = new SUV(1000);
console.log(suv1.getPrice());//1000

三. ES6實(shí)現(xiàn)封裝繼承一個(gè)類

1.class封裝一個(gè)類

ES6中為了讓JS與主流面向?qū)ο缶幊陶Z(yǔ)言更相似,引入class(類)的概念,這其實(shí)是一個(gè)語(yǔ)法糖,本質(zhì)上,ES6 的類只是 ES5 的構(gòu)造函數(shù)的一層包裝,因?yàn)閏lass實(shí)現(xiàn)的ES5中也能實(shí)現(xiàn)。

//類的基本語(yǔ)法
class Car{
constructor(){
}
Car = Car.prototype.constructor
}
var car  = new Car()

class的實(shí)質(zhì)是一個(gè)函數(shù),里面默認(rèn)必須定義一個(gè)構(gòu)造函數(shù),如果不寫(xiě),默認(rèn)添加一個(gè)空的構(gòu)造函數(shù)。
原型prototype在class上同樣也適用,類本身就指向構(gòu)造函數(shù),使用時(shí)也是new一個(gè)類實(shí)例。
類的寫(xiě)法也可以寫(xiě)成表達(dá)式的形式

const BCar = class Car{
    constructor(){
   }
      }
/*注意,在這里BCar才是類的真正名字, Car只能在類內(nèi)部使用*/

關(guān)于this,類中的this默認(rèn)指向類的實(shí)例。

2.繼承一個(gè)類

ES6中通過(guò)extends關(guān)鍵字繼承父類。
與es5不同的是,class中不存在變量提升,默認(rèn)子對(duì)象必須在父對(duì)象之后定義。

//SUV繼承Car類
class Car{
constructor(color,type){
   this.color = color;
   this.type = type;
}
}
class SUV extends Car{
constructor(color,type,price){
  super(color,type){
    this.price =price}}

}

子類也必須擁有一個(gè)默認(rèn)的構(gòu)造函數(shù)
super關(guān)鍵字
子類在構(gòu)造函數(shù)內(nèi)通過(guò)super調(diào)用父類的構(gòu)造函數(shù),否則的話新建子類實(shí)例會(huì)報(bào)錯(cuò)。這也是與ES5不同之處,子類是繼承了父類的this對(duì)象,然后進(jìn)行修改。在super內(nèi)部的this指向的是子類,然而靜態(tài)方法中的super指向的是父類。
static關(guān)鍵字
與其他語(yǔ)言相似,如果在一個(gè)方法前添加static關(guān)鍵字,表示這個(gè)一個(gè)靜態(tài)方法,實(shí)例不繼承該方法,而是需要通過(guò)類來(lái)調(diào)用。同時(shí)需要注意,靜態(tài)方法包含this關(guān)鍵字,這個(gè)this指的是類,而不是實(shí)例。
類的prototype與proto

class Car{
constructor(color,type){
   this.color = color;
   this.type = type;
}
}
class SUV extends Car{
constructor(color,type,price){
  super(color,type){
    this.price =price}}

}
//////
SUV._proto_ = Car
SUV.prototype._proto_ =  Car.prototype

實(shí)例的proto

var car1 = newCar('red', 'fast');
var suv1 = new SUV('red', 'fast', 2018);
/////////
car1._prpto_._prpto_ =  car1._prpto_

3.提供私有方法和私有屬性

我們知道C++中的類有私有方法和私有屬性,但是ES6中的類沒(méi)有提供私有方法(僅供類內(nèi)部使用,外部無(wú)法訪問(wèn))只能變相通過(guò)別的方法來(lái)提供私有方法和私有屬性
--1.私有方法
將私有方法的名字命名為一個(gè)Symbol值。

const carO = Symbol('carO');
const carT= Symbol('carT');

export default class Car{

  // 公有方法
  test(car) {
    this[carO](car);
  }

  // 私有方法
  [carO](car) {
    return this[carT] = car;
  }

  // ...
};

--2.私有屬性
在屬性名之前,使用#表示私有屬性

class Car {
  #price;

  constructor(price) {
   this.#price
  }

  get price() { return #price }
  set price(value) { ##price = +value }
}
////price是私有屬性,類外部訪問(wèn)不了

以上是JS中的面向?qū)ο缶幊痰暮?jiǎn)單介紹,如有錯(cuò)誤,歡迎指出。
參考鏈接:
Javascript繼承機(jī)制的設(shè)計(jì)思想--阮一峰

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容