[JS201 筆記] 物件導向基礎與 prototype
TWGD / 2019-01-07
這是 Lidemy 課程:[JS201] 進階 JavaScript:那些你一直搞不懂的地方:物件導向基礎與 prototype
課程筆記。
如果有人不小心看到,請不要太嚴格,這只是我上課的隨筆筆記XD 若有錯誤,請不要吝嗇糾正我,謝謝~
ES6 的物件導向範例:
ES6 才有 class 這個關鍵字。
class Dog { // 名稱開頭通常大寫
// 建構子,作為初始化
constructor(name) {
this.name = name;
}
// setter 用來設置值
setName(name) {
this.name = name;
}
// getter 用來存取值
getName() {
return this.name;
}
sayHello() {
console.log('hello' + this.name)
}
}
let cat = new Dog('cat'); // cat 是 Dog 的 instance
cat.getName();
cat.sayHello();
ES5 的物件導向:
function Dog(name) {
var myName = name;
return {
getName: function() {
return myName;
},
sayHello: function() {
console.log('hello' + myName);
}
}
}
var cat = Dog('cat');
cat.getName();
cat.sayHello();
var cat2 = Dog('cat2');
cat2.getName();
cat2.sayHello();
但是以上會有一個問題:cat.getName() !== cat2.getName()
。
每創建一個 instance,就要耗費一個記憶體空間。
因此改用以下的寫法:
// 等同於建構子的作用
function Dog(name) {
this.name = name;
}
// 設定 method
Dog.prototype.getName = function() {
return this.name;
}
Dog.prototype,sayHello = function() {
console.log('hello' + this.name)
}
// new 創建 Dog 的 instance
var cat = new Dog('cat');
console.log(cat); // Dog {name: 'cat'}
cat.sayHello(); // hello cat
var cat2 = new Dog('cat2');
console.log(cat2); // Dog {name: 'cat'}
cat2.sayHello(); // hello cat2
console.log(cat.sayHello === cat2.sayHello) // true
從 prototype 來看「原型鍊」
與 javascript 提供的屬性 __proto__
有關:instance 透過 __proto__
屬性得以連結 prototype
。
從以下例子理解 prototype chain 原型鍊:
function Dog(name) {
this.name = name;
}
Dog.prototype.getName = function() {
return this.name;
}
Dog.prototype.sayHello = function() {
console.log('hello ' + this.name)
}
var cat = new Dog('cat');
var cat2 = new Dog('cat2');
console.log(cat.__proto__ === Dog.prototype) // true
console.log(cat.__proto__.__proto__ === Object.prototype) // true
console.log(cat.__proto__.__proto__.__proto__ === null) // true
cat.sayHello()
/*
prototype chain 原型鍊
呼叫 cat.sayHello() 的時候,找 sayHello() 的順序
1. cat 身上有沒有 sayHello
2. cat.__proto__ 有沒有 sayHello
3. cat.__proto__.__proto__ 有沒有 sayHello
4. cat.__proto__.__proto__.__proto__ 有沒有 sayHello
5. null (找到頂了)
*/
// 順便看一下 Dog 的 __proto__ 是什麼
console.log(Dog.__proto__ === Function.prototype) // true
prototype 的其他應用:
// 自訂 method 讓其他 string 使用
String.prototype.first = function() {
return this[0]
}
var a = '123';
console.log(a.first()); // 1
new 底層做了什麼
先有 call()
的基本觀念:
function test() {
console.log(this);
}
// call 裡面傳什麼東西,this 就會是什麼
test.call('123'); // [String: '123']
以下實作 new 的原理:
function Dog(name) {
this.name = name;
}
Dog.prototype.getName = function() {
return this.name;
}
Dog.prototype.sayHello = function() {
console.log('hello ' + this.name)
}
var cat = new Dog('cat');
/*
實作一個 function newDog(){...},來執行以下程式碼:
var cat2 = newDog('cat2');
cat2.sayHello();
*/
function newDog(name) {
var obj = {}; // 產生一個 obj
Dog.call(obj, name); // call constructor 完成初始化物件
obj.__proto__ = Dog.prototype; // 把 obj 關聯到 Dog 的 prototype
return obj;
}
物件導向的繼承 Inheritance
繼承用在哪裡:若有共同的屬性可以直接繼承上層的,不用重複寫。
super()
的用法。class Dog {
constructor(name) {
this.name = name;
}
sayHello() {
console.log('hello ' + this.name)
}
}
class BlackDog extends Dog {
constructor(name) {
// super() 呼叫上層得已初始化。強制使用 super()。 傳參數進去給上層的 constructor 使用。
super(name); // Dog 的 constructor。
this.sayHello(); // 初始化的時候就 say Hello
}
test() {
console.log('test:', this.name)
}
}
const black = new BlackDog('black'); // hello black
black.test(); // test: black