從今天起我們開始討論javascript的面向對象
面向對象概念理解
面嚮對象語言有個標誌=>它們都具有類的概念,通過類可以創建任意多個具有相同屬性和方法的對象。
面向對象有三大特性
- 封裝
- 繼承
- 多態
但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操作符,以這種方式調用構造函數實際上會經歷以下四個步驟,
- 創建一個新對象
- 將構造函數的作用域賦給新對象(因此this就指向了這個新對象)
- 執行構造函數中的代碼(爲這個新對象添加屬性)
- 返回新對象
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語言精粹》(修訂版)