JavaScript OOP
Date: 2019/10/20
Lecturer: 土豆
大綱
- OOP介紹
- Class實作
- Prototype
- JavaScript如何實現Class
- Labs
OOP介紹
簡介
OOP = Object Oriented Programming
物件導向
Classes & Objects
Class
Object
兩者的關係
設計圖
實際的東西
實例化
Classes & Objects
Attributes
Methods
Class的組成
Classes & Objects
Class
學生
舉個例子
Object
小明
Attributes
Name
Age
Weight
Height
Methods
Walk
Eat
Sleep
Attributes
Name: 小明
Age: 20
Weight: 77
Height: 188
Methods
Walk
Eat
Sleep
Encapsulation
分工合作,各司其職
Object A
Object B
Public
Attritube 1
Attritube 2
Method 1
Method 2
Private
Attritube 1
Method 1
Public
Attritube 1
Attritube 2
Method 1
Method 2
Private
Attritube 1
Method 1
Interface
Getter, Setter, Statics
Getter
讓別的Object取得自己的Property的Interface
Setter
讓別的Object可以設定自己的Property的Interface
Static
不需要實例化Class即可直接使用
Inheritance
站在巨人的肩膀上
Class
學生
Attributes
Name
Age
Weight
Height
Methods
Walk
Eat
Sleep
Class
好學生
Class
壞學生
Methods
Study
Methods
Fight
Overriding
騎在你爸爸頭上
Class
學生
Methods
Walk (正常走)
Eat (一般吃法)
Sleep (一般睡法)
Class
懶學生
Methods
Walk (一搖一擺)
Eat (狼吞虎嚥)
Sleep (呼呼大睡)
一樣名稱
不同動作
Class實作
十分地簡單
class Rabbit {
constructor(type) {
this.type = type;
}
speak(line) {
console.log(`The ${this.type} rabbit says '${line}'`);
}
}
let killerRabbit = new Rabbit("killer");
let blackRabbit = new Rabbit("black");
拆成兩部分來看
constructor()
methods
先來看看this是在this什麼
this.type指的是"實例出"的object裡面叫做type的property
//let type = "what??";
class Rabbit {
constructor(type) {
this.type = type;
}
speak(line) {
console.log(`The ${type} rabbit says '${line}'`);
}
}
let killerRabbit = new Rabbit("killer");
console.log(killerRabbit.type)
killerRabbit.speak();
刪掉this看看,並試著在class外定義一個type
constructor
constructor裡面的東西會在新建object時跑一次
class Apple {
constructor() {
console.log("An apple a day keeps doctor away.");
}
}
let a = new Apple();
methods
你可以這樣在class中定義method
class Apple {
constructor() {
console.log("An apple a day keeps doctor away.");
}
run() {
console.log("Your apple keeps you away.");
}
}
let a = new Apple();
a.run();
咦? 那attribute去哪了?
現在辦不到,以後可能辦的到
你現在只能在constructor或是methods裡面定義
Inheritance
class Apple {
constructor() {
console.log("You create an apple!");
}
run() {
console.log("Your apple run away.");
}
}
class PoisionApple extends Apple {
constructor() {
super();
this.type = "Poision";
console.log("This is a poision apple!");
}
}
let a = new PoisionApple();
透過extends來繼承
super()代表呼叫父類別的constructor
overriding
// class Apple ...
class PoisionApple extends Apple {
constructor() {
super();
this.type = "Poision";
console.log("This is a poision apple!");
}
run() {
console.log("Your apple runs away, and kill a potato with poision.");
}
}
let a = new PoisionApple();
a.run();
在子類別中定義一樣名稱的method來覆蓋父類別的method
setter, getter
class Apple {
constructor() {
this.first_name = "";
this.last_name = "";
console.log("You create an apple!");
}
run() {
console.log("Your apple run away.");
}
get name() {
return this.first_name + " " + this.last_name;
}
set name(str) {
let temp = str.split(" ");
this.first_name = temp[0];
this.last_name = temp[1];
}
}
let a = new Apple();
a.name = "John Doe";
console.log(a.first_name);
console.log(a.last_name);
console.log(a.name);
在method前面加上set, get
statics
class Apple {
// ...
static listAllSize() {
return ["small", "medium", "big"];
}
}
console.log(Apple.listAllSize());
在method前面加上static
注意這邊沒有任何實例化,但是卻可以呼叫class裡面的method
相對於dynamic被實例化時才放到記憶體中
static代表一開始就在記憶體中
instanceof
class Apple {
// ...
}
class PoisionApple extends Apple {
// ...
}
let a = new PoisionApple();
console.log(a instanceof PoisionApple);
console.log(a instanceof Apple);
let b = new Apple();
console.log(b instanceof PoisionApple);
console.log(b instanceof Apple);
instanceof是一個operator
可以查看object是否是某一個class的實例,包含父類別
So easy, right?
Prototype
你剛剛寫的那個不是Class
JavaScript沒有Class
JavaScript沒有Class
JavaScript沒有Class
啊我剛剛寫的是甚麼?
是東拼西湊出來的假貨
害我要解釋老半天...
只是個語法糖(Syntactic sugar)
為什麼沒有Class
因為一開始的應用單純
但是這老兄,不想設計Class卻設計了繼承......
所以才有了Prototype這東西
Brendan Eich
所以什麼是Prototype
let empty = {};
console.log(empty.toString);
console.log(empty.toString());
先來跑跑看這段code
不覺得哪裡怪怪的嗎?
明明就沒有定義過toString這個Property阿
Prototype Chain
Object.prototype
Your Object
let empty = {};
console.log(Object.getPrototypeOf(empty) == Object.prototype);
為了證明我沒有唬爛你,94如此神奇
Your Another Object
console.log(Object.getOwnPropertyNames(Object.prototype));
來看看Object.prototype提供了什麼
Prototype Chain
Object.prototype
Your Object
Your Another Object
Calling toString()
Not Found
Not Found
Found!!
Call it
Prototype Chain
Object.prototype
Function.prototype
Your Function
來看看更多的chain
console.log(Object.getOwnPropertyNames(Function.prototype));
看看Function.protype提供了啥
Prototype Chain
Object.prototype
Array.prototype
Your Array
來看看更多的chain
console.log(Object.getOwnPropertyNames(Array.prototype));
看看Array.protype提供了啥
用Object當作Prototype創建另一個Object
let protoRabbit = {
type: "none",
speak: function(line) {
console.log(`The ${this.type} rabbit says '${line}'`);
}
};
let killerRabbit = Object.create(protoRabbit);
killerRabbit.type = "killer";
killerRabbit.speak("SKREEEE!");
let sleepyRabbit = Object.create(protoRabbit);
sleepyRabbit.type = "sleepy";
sleepyRabbit.speak("zzzzzzz");
Object.create()
JavaScript如何實現Class
JavaScript class = constructor function + prototype property
// constructor function
function Rabbit(type) {
console.log("You create a new Rabbit!");
this.type = type;
}
// prototype property
Rabbit.prototype.speak = function(line) {
console.log(`The ${this.type} rabbit says '${line}'`);
};
// create a new Rabbit object
let weirdRabbit = new Rabbit("weird");
然後使用new去創建新的object
直接來看看怎麼做
他老兄看到Java有new,所以他也想要
阿就沒有設計Class是要new什麼?
設計概念
那就只好把function拿來new了
然後我:
來看看怎麼用function創建出object
constructor function
function Dog(name) {
this.name = name;
this.type = "哈士奇";
this.speak = function() {
console.log(`我是${this.name},我要吃罐頭`);
}
}
let dogA = new Dog("Jack");
let dogB = new Dog("Dogy");
dogA.speak();
dogB.speak();
object的property不共用
constructor function
function Dog(name) {
this.name = name;
this.type = "哈士奇";
this.speak = function() {
console.log(`我是${this.name},我要吃罐頭`);
}
}
let dogA = new Dog("Jack");
let dogB = new Dog("Dogy");
dogB.type = "柴犬";
console.log(dogA.type);
console.log(dogB.type);
每個object都有這個,功能一模一樣
問題來了
this.speak = function() {
console.log(`我是${this.name},我要吃罐頭`);
}
// ...
console.log(dogA.speak === dogB.speak);
但是卻要各自佔掉一個記憶體空間
一個優秀的工程師不能忽略這件事情!!
function被建立時,會自動建立一個叫prototype的property
Prototype就可以派上用場了
let foo = function() {
return "nothing";
}
console.log(Object.getOwnPropertyNames(foo));
這個prototype就是一個空的object
console.log(typeof(foo.prototype));
console.log(foo.prototype);
需要共用的東西就全部丟到這個prototype裡面
使用new創建新的object時,會自動繼承這個prototype
function Dog(name) {
this.name = name;
this.type = "哈士奇";
}
Dog.prototype.speak = function() {
console.log(`我是${this.name},我要吃罐頭`);
}
let dogA = new Dog("Jack");
let dogB = new Dog("Dogy");
console.log(Object.getPrototypeOf(dogA) === Dog.prototype);
console.log(Object.getPrototypeOf(dogB) === Dog.prototype);
console.log(dogA.speak === dogB.speak);
再來複習一次
// constructor function
function Rabbit(type) {
console.log("You create a new Rabbit!");
this.type = type;
}
// prototype property
Rabbit.prototype.speak = function(line) {
console.log(`The ${this.type} rabbit says '${line}'`);
};
// create a new Rabbit object
let weirdRabbit = new Rabbit("weird");
跟我念一遍~
真正的實現方式是 constructor function + prototype property
JavaScript的class是語法糖
Labs
Lab1: A Vector Type
利用class實作一個二維向量的type,要求如下
Properties
- x
- y
Methods
- plus: 兩個向量相加
- minus: 兩個向量相減
- length: 取得向量的長度,這個要做成getter
Lab1: A Vector Type
// Your code here.
console.log(new Vec(1, 2).plus(new Vec(2, 3)));
// → Vec{x: 3, y: 5}
console.log(new Vec(1, 2).minus(new Vec(2, 3)));
// → Vec{x: -1, y: -1}
console.log(new Vec(3, 4).length);
// → 5
使用範例
Lab1: A Vector Type
class Vec {
constructor(x, y) {
this.x = x;
this.y = y;
}
plus(vec) {
return new Vec(this.x + vec.x, this.y + vec.y);
}
minus(vec) {
return new Vec(this.x - vec.x, this.y - vec.y);
}
get length() {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
}
// Your code here.
console.log(new Vec(1, 2).plus(new Vec(2, 3)));
// → Vec{x: 3, y: 5}
console.log(new Vec(1, 2).minus(new Vec(2, 3)));
// → Vec{x: -1, y: -1}
console.log(new Vec(3, 4).length);
// → 5
ANS
Lab2: Borrowing A Method
let data = {one: true, two: true, hasOwnProperty: true};
// Fix this call
console.log(data.hasOwnProperty("one"));
console.log(data.hasOwnProperty("three"));
之前我們使用過hasOwnProperty來檢查一個object當中有無某個property
現在我們把hasOwnProperty override掉了,請把它修好
Lab2: Borrowing A Method
let data = {
one: true,
two: true,
hasOwnProperty: function(str) {
return Object.prototype.hasOwnProperty.call(this, str);
}
}
console.log(data.hasOwnProperty("one"));
console.log(data.hasOwnProperty("three"));
ANS
Lab3: Stack
請利用class實作一個stack的class,要求如下
Properties
- items: 一個array代表stack中的值
Methods
- push
- pop
- peek: 印出最頂端的值
- isEmpty: 這個stack是否為空
- size: getter
Lab3: Stack
// Your code...
let s = new Stack();
console.log(s.items);
// []
s.push(1);
s.push(2);
console.log(s.pop());
// 2
console.log(s.isEmpty());
// false
s.push("a");
s.push(true);
console.log(s.items);
// [1, 'a', true]
console.log(s.size);
// 3
使用範例
Lab3: Stack
class Stack {
constructor() {
this.items = [];
}
push(item) {
this.items.push(item);
}
pop() {
return this.items.pop();
}
peek() {
return this.items[this.items.length - 1];
}
get isEmpty() {
if(this.items.length == 0) {
return true;
} else {
return false;
}
}
get size() {
return this.items.length;
}
}
let s = new Stack();
console.log(s.items);
// []
s.push(1);
s.push(2);
console.log(s.pop());
// 2
console.log(s.isEmpty);
// false
s.push("a");
s.push(true);
console.log(s.items);
// [1, 'a', true]
console.log(s.size);
// 3
ANS
感謝聆聽
JavaScript OOP
By Sam Yang
JavaScript OOP
- 756