在 ES6 之後,開始有了 Class 這個語法糖可以使用,可以比較看看兩種不同版本的物件導向寫法:
class Dog {
constructor(name) {
this.name = name;
}
sayHello() {
console.log(this.name, ' Hello');
}
}
const happy = new Dog('happy');
happy.sayHello();
const white = new Dog('white');
white.sayHello();
function Dog(name) {
this.name = name;
}
Dog.prototype.sayHello = function() {
console.log(this.name, ' Hello');
}
const happy = new Dog('happy');
happy.sayHello();
const white = new Dog('white');
white.sayHello();
在 ES5 時,通常是把 function 當成 constructor 來用,稱為「 建構函式 」,而共用的 method 則會放在 prototype 新增,可以避免在每個 instance 重複新增內容一樣的 method。
而關鍵在於 new
關鍵字,用 new
出來的 function, JS 會在背後幫你做好一連串的機制。
下例是 ES5 的 OOP 寫法:
function Dog(name) {
this.name = name;
}
Dog.prototype.sayHi = function() {
console.log(this.name, 'Hi');
}
const white = new Dog('white');
用上面這個例子,來描述關鍵字 new
在背後到底做了什麼?
接下來以一個自己寫的 function newDog('yello')
,來模擬 new Dog('yello')
在做的事:
const yello = newDog('yello');
white.sayHi();
yello.sayHi();
首先目的是通過自己寫的 newDog
產生一個 instance yello
,希望這個 instance 會有 跟用 new
出來的 instance white
有一樣的屬性跟方法。
所以目標放在如何寫出 newDog()
來模擬 new
這個關鍵字背後做的事情。
function newDog(name) {
const obj = {}; // 1.
Dog.call(obj, name); // 2.
obj.__proto__ = Dog.prototype; // 3.
return obj; // 4.
}
/*
new 背後做的事:
1. 產生一個新 object => obj
2. 把 Dog 當作 constructor,將 this 指向 obj,同時把參數 name 丟進去
=> 因為 Dog 只有一個參數所以用 call,如果是兩個以上的參數就可以用 apply 放陣列
3. 將 obj.__proto__ 指向 Dog.prototype
4. 回傳 obj
*/
在 ES6 使用繼承時,當要改寫 子類別的 constructor 時,記得要先用 super()
父類的 constructor,不然會跳出錯誤:Must call super constructor in derived class before accessing 'this' or returning from derived constructor
。
class Dog {
constructor(name) {
this.name = name;
}
sayHi() {
console.log(this.name, 'Hi');
}
}
class TaiwansDog extends Dog {
constructor(name) {
super(name); // => 記得要先 super()
this.sayHi();
}
}
const white = new Dog('white');
const black = new TaiwansDog('black');
之前這篇筆記:什麼是閉包 Closure 與實際應用 裡,Closure 最後一個 wallet
範例是說明 Closure 可以達到封裝的目的,讓我想到之前看 YT 上的 JavaScript OOP 教學。
var money = 100;
function add(num) {
money += 1;
}
function deduct(num) {
if (num > 10) {
num -= 10;
}
else {
money -= num;
}
}
add(1);
deduct(30);
console.log(money); // 91
在 ES5 要實現 Class 中的私有屬性,也是在 function
( 建構函式 )內部宣告一個變數,之後再利用用 getter
& setter
去存取 private property。
那後來又想著,那到底要如何在 ES6 中的 Class 實現 private 屬性呢?
查了一下 Private properties in JavaScript ES6 classes,第 2, 3 個回答得到不少啟發,尤其是第 3 個列舉了不同方法跟說明,認識到 Symbols
類型,雖然不是真正意義上的 private,但還是滿酷的!
想說練習一下,直接把 wallet
範例把多餘的 method
刪掉,改成以下三種:
很標準的 ES5 封裝方法。
function Wallet(init) {
let money = init;
this.getMoney = function() {
return money;
}
};
const wallet = new Wallet(100);
console.log(wallet.getMoney());
console.log(wallet.money); // => 存取不到,真正的 private
需要存取 private property 的 method 都只能放在 constructor 裡面,是可以正常運作,但感覺不太對勁
class Wallet {
constructor(num) {
var _money = num;
this.getMoney = () => _money;
this.setMoney = (newNum) => _money = newNum;
}
};
const wallet = new Wallet(100);
console.log(wallet.getMoney());
console.log(wallet._money); // => 存取不到,真正的 private
就是看著範例用,沒有深究,只覺得新語法很酷
var money = Symbol();
class Wallet {
constructor(num) {
this[money] = num;
}
getMoney() {
return this[money];
}
};
const wallet = new Wallet(100);
console.log(wallet.getMoney());
console.log(wallet[money]); // => 還是存取得到,不是真正的 private
所以在 ES6 實現 private property 最方便的寫法是什麼?
沒有。
對,真的就是沒有。
而如果 ES7 的支援性越來越高,要實現 private property 就有更方便的 #
關鍵字可以用,但現在 ES6 還沒有看到通用的方法,最簡單的方法其實可以再 private property or method 的名稱加上 _
前綴,同事們可以很簡單看出這是個私有屬性,沒事不要去動它。
( 以上內容大部分是 程式導師實驗計畫第三期 的學習筆記,如有錯誤歡迎糾正,非常感謝 🤓 )
Written on August 11th, 2019 by Yakim shu