js如何面向物件程式設計?(一)類與物件
原文:https://www.jianshu.com/u/30146fc58ef8
上帝說要有光!於是有了光。
JavaScript在ES6
之前語法上還沒有"類",JavaScript的開發者們在黑暗中苦苦摸索,最終有了屬於js風格的面向物件程式設計風格。
面向過程&面向物件
面向過程就是分析出解決問題所需要的步驟,然後用函式把這些步驟一步一步實現,使用的時候一個一個依次呼叫就可以了。
面向物件是把構成問題事務分解成各個物件,建立物件的目的不是為了完成一個步驟,而是為了描敘某個事物在整個解決問題的步驟中的行為
例子:大象裝進冰箱的問題
如果我們是按這個命題去思考:大象裝進冰箱分幾步?
那這思維就是面向過程的,虛擬碼實現:
// step1:開啟冰箱
openFridge();
// step2:把大象塞進去
pushIntoFridge(elephant);
// step3:關閉冰箱
closeFridge();
如果我們這麼思考:冰箱是什麼,有什麼行為?大象是什麼? 大象能裝進去嗎?
,我們先把冰箱抽象出來,便是面向物件
的思維:
//抽象出'類'才是面向物件的重點
class Fridge { //冰箱類
open(){}
close(){}
accommodate(something){}
}
class Elephant {} //大象類
const fridge = new Fridge(); //構造一個冰箱
const elephant = new Elephant(); //構造一隻大象
fridge.accommodate(elephant); //冰箱裝大象
類與物件的關係
類是對事物的抽象,物件是類所描述的具體事物。類與物件的關係就像汽車設計圖與汽車實車的關係:
面向物件(OOP)的程式設計思維便是基於類與物件的程式設計。"面向物件"在軟體工程的概念中有三個特徵:封裝、繼承、多型。
- 封裝:即是對所描述事物的抽象過程,將其行為和屬性存放於"類";
- 繼承:方便被傳宗接代,父類可以方便的被子類"佔有"它的屬性和方法,子類還可以擁有一些父類的沒有的新技能;
- 多型:同一操作作用於不同的物件,可以有不同的解釋,產生不同的執行結果。JavaScript由於自身語言的特徵,沒有這一特徵。
JavaScript裡的"類"
js是基於物件的語言,裡面的任何東西幾乎都是物件。例如:
var arr = [1,2]; //字面量寫法,實際上等同於:var arr = new Array(1, 2);
console.log(arr);
陣列型別的變數實際上是一個Array
類的例項。我們所熟知的Object
型別資料、window
、document
、DOM節點都是物件。
類
js在實現"類"時是用的建構函式
或者使用關鍵字class
(ES6新增),建構函式
的編寫就是上述特徵之一的封裝
。
原型與原型鏈
JavaScript的"類"有一個物件叫"原型",在類的屬性prototype
可訪問到,原型
可定義該類所有例項(物件)所擁有的方法和屬性,所以我經常們可以把例項所共有的屬性或方法存在prototype
屬性(下面會談到)。
我們還是以"汽車"為例來解釋,那麼在程式碼中如何描述"汽車"?從國產某車企李總的話中有感而發:
汽車就是四個輪子加兩個沙發
另外,汽車肯定是會跑的。所以js的簡單實現"汽車"的類如下:
// ES5寫法(Es5和ES6選其一寫法)
function Car(){
this.wheel = 4;
this.safa = 2;
}
Car.prototype.drive = function(){
console.log('Wow~')
}
//ES6寫法 (js終於有真正的"類"了)
class Car {
constructor() {
this.wheel = 4;
this.safa = 2;
}
drive(){
console.log('Wow~')
}
}
現在"造"一輛車並讓它"跑"起來:
var car = new Car();
console.log(car); // => Car {wheel: 4, safa: 2}
car.drive(); // =>'Wow~'
再來看一下打印出來的"car"物件可以發現他還有個屬性__proto__
:
在絕大多數瀏覽器裡物件的__proto__
屬性所指向的物件便是Car
類的原型,也就是:
car.__proto__ === Car.prototype; // => true
Car
的原型裡有我們上面定義的"drive"方法,以及有constructor
屬性用來表明類的建構函式。
需要注意的它還有個__proto__
,這個如何解釋?
上面說到原型本身也是物件,是物件就有原型,然後就形成了原型鏈,原型鏈的末端是null
:
//Car類的原型指向是Object類的原型,
//也就是說car的原型物件是Object的例項,說起來有點繞...
car.__proto__.__proto__ === Object.prototype; // => true
// 原型鏈的結尾
car.__proto__.__proto__.__proto__ // => null
他們的關係如下圖:
感謝閱讀!歡迎評論一起探討。