談一談javascript面向對象

原文鏈接

從今天起我們開始討論javascript的面向對象

面向對象概念理解

面嚮對象語言有個標誌=>它們都具有類的概念,通過類可以創建任意多個具有相同屬性和方法的對象。
面向對象有三大特性

  1. 封裝
  2. 繼承
  3. 多態

但JS中對象與純面嚮對象語言中的對象是不同的

JS中的對象:
無序屬性的集合,其屬性可以包含基本值、對象或者函數。

可以簡單理解爲JS的對象是一組無序的值,其中的屬性或方法都有一個名字,根據這個名字可以訪問相映射的值(值可以是基本值/對象/方法)。

創建對象的基本方法

我們前面在講原型鏈的時候說過,兩種創建對象的方法

對象字面量(對象直接量)

這是最快的
一個🌰:

  const hero = {
    name:"歐陽鋒",
    nickname:"西毒",
    doSth:function(){
      console.log('靈蛇杖法');
    }

⚠️創建對象的屬性名並不強制使用引號包裹,除了以下幾種情況

  • 屬性名中包含空格
  • 屬性名中包含連字符(中劃線)
  • 屬性名中包含保留字
const obj={
    "go home":"包含了空格",
    "go-home":"包含了連字符",
    "for":"這是保留字"
}

new 實例化一個對象

通過new運算符創建並實例化一個新對象,new後面是一個構造函數

const hero = new Object()
hero.name = "歐陽鋒"
hero.nickname = "西毒"
hero.doSth = function () {
    console.log('靈蛇杖法');
}

兩種創建方法是一樣的
創建對象通過以上兩種方式似乎足夠了,但是當場景稍微複雜一點,問題就顯現出來了
當我門創建很多結構相同的對象時,會產生大量的重複代碼,爲了解決這個問題,出現了一個解決方案

工廠模式

工廠模式抽象了創建具體對象的過程,因爲javascript無法創建類,開發人員就發明了一種函數,用函數來封裝以特定接口創建對象
一個🌰:

function createHero(name,nickname,doSth) {
    const obj = new Object()
    obj.name=name
    obj.nickname=nickname
    obj.doSth = function () {
        console.log(doSth);
    }
    return obj
}
const hero1 = createHero("歐陽鋒","西毒","靈蛇杖法")
const hero2 = createHero("黃藥師","東邪","碧海潮生曲")
console.log(hero1)

看下輸出:

hero1和hero2都直接繼承自Object實例,工廠模式就是像工廠一樣來創建對象,創建的每一個對象都是通過new Object()來創建的
後來,開發人員有發現了更好的模式

構造函數模式

我們之前討論過,通過使用自定義構造函數來實例化對象

function Hero(name, nickname, doSth) {
    this.name = name
    this.nickname = nickname
    this.doSth = function () {
        console.log(doSth);
    }
}
const hero3 = new Hero("歐陽鋒","西毒","靈蛇杖法")
const hero4 = new Hero("黃藥師","東邪","碧海潮生曲")
console.log(hero3);

注意⚠️:創建自定義構造函數,函數名首字母大寫,用來和非構造函數進行區分
我們繼續看下輸出:

hero3是通過Hero實例化出來的,所以hero3先繼承自Hero
要創建Hero的新實例,必須使用new操作符,以這種方式調用構造函數實際上會經歷以下四個步驟,

  1. 創建一個新對象
  2. 將構造函數的作用域賦給新對象(因此this就指向了這個新對象)
  3. 執行構造函數中的代碼(爲這個新對象添加屬性)
  4. 返回新對象

hero3和hero4都是Hero的實例,同時也是Object的實例
instanceof用於判斷一個變量是否某個對象的實例

console.log(hero3 instanceof Hero);//=>true
console.log(hero3 instanceof Object);//=>true
console.log(hero4 instanceof Hero);//=>true
console.log(hero4 instanceof Object);//=>true

屬性和方法(公有&私有)

👆的🌰中,我們將屬性和方法綁定在了構造函數Hero中的this上,hero3和hero4都可以訪問這些屬性
綁定在this上的屬性我們稱之爲公有屬性
綁定在this上的方法我們稱之爲公有方法
也就是說通過構造函數Hero實例化出來的對象是可以方位公有屬性和公有方法的
既然有公有屬性和公有方法,就一定會有私有屬性和私有方法
我們做一下調整

function Hero(name, nickname, doSth) {
    let test = "私有屬性"
    function method(){console.log("私有方法");} 
    this.name = name
    this.nickname = nickname
    this.doSth = function () {
        console.log(doSth);
    }
}
const hero3 = new Hero("歐陽鋒","西毒","靈蛇杖法")
const hero4 = new Hero("黃藥師","東邪","碧海潮生曲")
console.log(hero3);

看下輸出:

在hero3中是不存在構造函數的私有屬性和私有方法的
如果我們這創建完構造函數後,追加一下屬性和方法,會怎麼樣呢?試試看

    function Hero(name, nickname, doSth) {
      let test = "私有屬性"
      function method(){console.log("私有方法");} 
      this.name = name
      this.nickname = nickname
      this.doSth = function () {
        console.log(doSth);
      }
    }
    Hero.localAttr="測試屬性"
    Hero.localMethod=function(){
      console.log('測試方法');
    }
    Hero.prototype.proAttr="原型屬性"
    Hero.prototype.proMethod=function(){
      console.log('原型方法');
    }
    const hero3 = new Hero("歐陽鋒","西毒","靈蛇杖法")
    const hero4 = new Hero("黃藥師","東邪","碧海潮生曲")
    console.log(hero3);
    console.log('localAttr測試屬性:',hero3.localAttr);
    console.log("localMethod測試方法:",hero3.localMethod);
    console.log("proAttr原型屬性:",hero3.proAttr);
    console.log("proMethod原型方法:",hero3.proMethod);

看輸出:

創建完實例對象後,
通過.運算符添加的屬性是類靜態公有屬性(實例化的對象無法訪問)
通過.運算符添加的方法是類靜態公有方法(實例化的對象無法訪問)
通過原型鏈添加的屬性是公有屬性(實例化的對象可以訪問)
通過原型鏈添加的方法是公有方法(實例化的對象可以訪問)

今天就到這裏,明天不見不散

收集整理了一套js進階教程,公衆號後臺回覆“js進階”即可領取

圖片描述
參考文獻:
《javascript高級程序設計》(第三版)
《javascript設計模式》
《javascript語言精粹》(修訂版)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章