# ES6 class
---
### What is Class?
- 類別(class):設計藍圖
- 物件(object):實際蓋好的房子
- 兩者關係:設計藍圖(類別)決定房子應該怎麼蓋,決定幾台電梯、幾間房間、走道如何設計。實際蓋好的房子(物件)是照著設計藍圖所蓋出來的房子,人只能照設計藍圖的設計使用這間房子。
[保哥的OOP-Basis-What-is-class-and-object](https://blog.miniasp.com/post/2009/08/27/OOP-Basis-What-is-class-and-object)
---

---
### ES6 Class ?
- 一個語法糖
- 基於prototype實現
- 更簡潔的方式建立物件與處理繼承
---
### prototype chain ?

---
**ES6**
```javascript=
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
toString() {
return '(' + this.name + ',' + this.age + ')';
}
}
var p = new Person('Mia', 18);
```
---
**ES5**
```javascript=
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.toString = function () {
return '(' + this.name + ',' + this.age + ')';
}
var p = new Person('Mia', 18);
```
---
**討論**
與上方的ES5有何區別
```javascript=
function Person(name, age) {
this.name = name;
this.age = age;
this.toString = function () {
return '(' + this.name + ',' + this.age + ')';
};
}
var p = new Person('Mia', 18);
```
---
## ES6 class 解決了什麼問題?
- extends可以更優雅的寫繼承,而非使用
- ``Object.create(..)``
- ``.prototype ``
- ``__proto__``
- `` Object.setPrototypeOf(..)``
- 簡潔的 super 達到相對多型的目的
- extends 擴充內建函數``Array RegExp ``
- class 語法只能指定方法,不能設定屬性,這避免開發者誤將屬性(狀態或資料)放在類別中造成的共用問題
*多型(Polymorphism)是指父類別可透過子類別衍伸成多種型態,而父類別為子類別的通用型態,再透過子類別可覆寫父類別的方法來達到多型的效果,也就是同樣的方法名稱會有多種行為。*
---
# **ES6 class的坑**
---
定義一個``class``
```javascript=
class C {
constructor() {
this.num = Math.random();
}
rand() {
console.log( "Random: " + this.num );
}
}
```
---
透過``prototype``改變了``class``裡面的方法
```javascript=
var c1 = new C();
c1.rand(); "Random: 0.4324299..."
//覆寫..
C.prototype.rand = function() {
console.log( "Random: " + Math.round( this.num * 0 ));
};
var c2 = new C();
c2.rand(); "Random:0"
c1.rand(); "Random:0"
```
---
定義一個``class``並透過``prototype``設定``count++``
```javascript=
class C {
constructor() {
C.prototype.count++;
//`this.count` 通過委託如我們期望的那樣工作
console.log( "Hello: " + this.count );
}
}
```
---
增加共享属性``count``
```javascript=
// 增加共享属性
C.prototype.count = 0;
var c1 = new C();
// Hello: 1
var c2 = new C();
// Hello: 2
c1.count === 2; // true
c1.count === c2.count; // true
```
---
分離的遮蔽屬性
```javascript=
class C {
constructor(id) {
// 噢,一個坑,我們用實例上的屬性值遮蔽了`id()`方法
this.id = id;
}
id() {
console.log( "Id: " + this.id );
}
}
var c1 = new C( "c1" );
c1.id(); // TypeError -- `c1.id`現在是字串"c1"
```
---
[[HomeObject]]
```javascript=
class P {
foo() { console.log( "P.foo" ); }
}
class C extends P {
foo() {
super();
}
}
var c1 = new C();
c1.foo(); // "P.foo"
var D = {
foo: function() { console.log( "D.foo" ); }
};
var E = {
foo: C.prototype.foo
};
// E 链接到 D 来进行委托
Object.setPrototypeOf( E, D );
E.foo(); // "P.foo"
```
---
```javascript=
var D = {
foo: function() { console.log( "D.foo" ); }
};
// E 链接到 D 来进行委托
var E = Object.create( D );
// 手动绑定 `foo` 的 `[[HomeObject]]` 到
// `E`, 因为 `E.[[Prototype]]` 是 `D`,所以
// `super()` 是 `D.foo()`
E.foo = C.prototype.foo.toMethod( E, "foo" );
E.foo(); // "D.foo"
```
---
## 建立子類別extends
---
#### 擴展Date
```javascript=
class myDate extends Date {
constructor() {
super();
}
getFormattedDate() {
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
return this.getDate() + '-' + months[this.getMonth()] + '-' + this.getFullYear();
}
}
```
---
#### 擴展 null
```javascript=
class nullExtends extends null {
constructor() {}
}
Object.getPrototypeOf(nullExtends); // Function.prototype
Object.getPrototypeOf(nullExtends.prototype) // null
new nullExtends(); //ReferenceError: this is not defined
```
---
## super
---
Using super in classes
```javascript=
class Banner extends Component {
constructor(props) {
// ReferenceError,使用this前super需要先被调用!
//這裡呼叫super()是調用父類的constructor(props);
this.something;
}
};
```
---
Super-calling static methods
```javascript=
class Human {
constructor() {}
static ping() {
return 'ping';
}
}
class Computer extends Human {
constructor() {}
static pingpong() {
return super.ping() + ' pong';
}
}
Computer.pingpong(); // 'ping pong'
```
---
### React 的class component?
```javascript=
class Banner extends Component {
state = {
openAtStart: true,
autoToggle: false,
transition: true,
transitionClass: "",
};
static defaultProps = {
openAtStart: true, // [boolean] true | false
autoToggle: false, // [boolean|number] true | false | 3000
transition: true,
whenTransition:function(){
console.log('Transition!!!!')
}
};
```
---
## 沒有constructor && super ?
```javascript=
class Banner extends Component {
stste={
openAtStart: true,
autoToggle: false,
transition: true,
transitionClass: "",
classText: "",
currentClass: OPENED,
imgClass:null,
whenTransition: function () {
console.log(' default callback !!!! ')
},
}
}
```
---
其實是babel幫你加了,或new(產生實體)的時候自動幫你補上
根據[ ES.next 類提案](https://github.com/tc39/proposal-class-fields)
```javascript=
class Banner extends Component {
constructor(...args) {
super(...args);
_defineProperty(this, "stste", {
openAtStart: true,
autoToggle: false,
transition: true,
transitionClass: "",
}
}
```
---
## static ?
```javascript=
class say {
static hello(name){
console.log('hello',name)
}
}
say.hello('Q_Q') // hello Q_Q
//如果沒有static
say.hello('Q_Q') // say.hello is not a function
```
---
**不會被實例繼承,而是直接通過類來調用**

---
靜態屬性目前來說有兩種解決方案,一種是使用ES7的Class Properties標準,可以使用static關鍵字來定義靜態屬性
```javascript=
// ES7語法方式
class Video extends React.Component {
static defaultProps = {
autoPlay: false,
maxLoops: 10,
}
render() { ... }
}
```
---
另一種是定義到類別原本的定義外面:
```javascript=
// ES6語法方式
class Video extends React.Component {
constructor(props) { ... }
render() { ... }
}
Video.defaultProps = { ... }
```
---
**曾經用class集中管理api**
```javascript=
export default class ProductAPI {
static getProduct(page = 1){
return axios({
method:'GET',
url:`${apiUrl}/api/${dbName}/products/?page=${page}`,
})
}
static addProduct(){
}
}
```
---
如何使用?
```javascript=
import ProductAPI from '../../api/API-Product';
getProduct = (e) => {
ProductAPI.getProduct().then((response)=>{
this.setState({
productArray:response.data.products
})
})
}
```
---
## 參考資料
- [MDN](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Classes/extends)
- [Summer](https://cythilya.github.io/2018/10/28/es6-class/)
- [pjchender](https://pjchender.blogspot.com/2016/07/javascript-es6classes.html)
- [You-Dont-Know-JS](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/apA.md)
- [overreacted](https://overreacted.io/zh-hant/why-do-we-write-super-props/)
[The constructor is dead, long live the constructor!](https://hackernoon.com/the-constructor-is-dead-long-live-the-constructor-c10871bea599)
---
## 聽了想睡覺系列
---

---
## 如何實現一個super()
---
```javascript=
let animal = {
name: "Animal",
eat() {
alert(`${this.name} eats.`);
}
};
let rabbit = {
__proto__: animal,
name: "Rabbit",
eat() {
// that's how super.eat() could presumably work
this.__proto__.eat.call(this); // (*)
}
};
rabbit.eat(); // Rabbit eats.
```
---
```javascript=
let animal = {
name: "Animal",
eat() {
alert(`${this.name} eats.`);
}
};
let rabbit = {
__proto__: animal,
eat() {
// ...bounce around rabbit-style and call parent (animal) method
this.__proto__.eat.call(this); // (*)
}
};
let longEar = {
__proto__: rabbit,
eat() {
// ...do something with long ears and call parent (rabbit) method
this.__proto__.eat.call(this); // (**)
}
};
longEar.eat(); // Error: Maximum call stack size exceeded
```
---

---
## ```[[HomeObject]]```
---
```javascript=
let animal = {
name: "Animal",
eat() { // [[HomeObject]] == animal
alert(`${this.name} eats.`);
}
};
let rabbit = {
__proto__: animal,
name: "Rabbit",
eat() { // [[HomeObject]] == rabbit
super.eat();
}
};
let longEar = {
__proto__: rabbit,
name: "Long Ear",
eat() { // [[HomeObject]] == longEar
super.eat();
}
};
longEar.eat(); // Long Ear eats.
```
---
### Thank you! :sheep:
You can find me on
- GitHub
- or email me
- Office
{"metaMigratedAt":"2023-06-14T18:32:22.682Z","metaMigratedFrom":"Content","title":"ES6 class","breaks":true,"contributors":"[{\"id\":\"e4ff6191-5eb9-4d87-a833-008dbafb5a9c\",\"add\":13061,\"del\":4131}]"}