# Constructor 建構式 > 也就是用來**建構物件的函式**。 > 透過函式建立物件的樣板,減少重複的程式碼。 ## 前言:物件導向程式設計 >**物件導向程式設計(Object-oriented programming,OOP)** 每一個物件都能夠接受資料、處理資料並將資料傳達給其它物件,與傳統(直接對電腦下指令)的思想剛好相反,加強程式的靈活性和可維護性,並且在大型專案設計中廣為應用。 - 以 `類別` 為基礎 (class-based) 的物件導向。 - 以 `原型` 為基礎 (prototype-based) 的物件導向。 >前者有C++、Java、Python,而Javascript是屬於後者。 ## 為什麼需要建構式? - 程式碼重複性降低 - 易於維護與擴充 - 節省記憶體空間 ```+ //典型建構式寫法 function Person (firstName,lastName){ this.firstName = firstName; this.lastName = lastName; } let john = new Person("John","Wu"); let Amy = new Person("Amy","Lin"); let Kiki = new Person("Kiki","Hello"); console.log(john); ``` ### new 關鍵字 - 是運算子(operators)的一種。 - 只能由function建立。 - 【1】建立一個新的空白物件。 - 【2】將Person函式裡面的屬性名稱和屬性值設為原型。 - 【3】執行Person函式,將this綁定到新物件上。(這個過程稱為實體化) ![](https://i.imgur.com/SI0Ygfj.png) #### new有害說與物件實體化 >JavaScript長期以來就有反對使用new運算符用於實體化物件的言論,主要的理由是語言本身設計上的缺陷。 - 容易與一般函式混用時造成混亂(通常第一個字首會大寫,但還是不易區分)。 - 如果沒有使用new來實體化物件,程式不會報錯,但會導向不同的結果。 - 執行函式也有可能會隱藏對於物件實體的複雜的生成過程。 ```+ //防止錯誤的範例寫法 function Player(name, age) { if (this instanceof Player){ this.name = name this.age = age }else{ return new Player(name, age) } } const aPlayer = Player('Inori', 16) const bPlayer = new Player('Gi', 16) ``` ### ES5 Object.create - 更直觀使用物件原型(prototypal inheritance)的方式。 - 真的很舊的瀏覽器可能會無法使用(需改寫成polyfill的形式)。 - 物件變更屬性設定更自由。 ```+ let Person = { firstName: 'Default', lastName: 'Default', getFullName: function () { return this.firstName + " " + this.lastName; } } let john = Object.create(Person); console.log(john); console.log(john.firstName); //輸出的結果會得到一個空物件,但是繼承了Person的屬性和方法。 john.firstName="wu" //原型鏈會尋找最外層的屬性 console.log(john.firstName); ``` ![](https://i.imgur.com/SJswj7B.png) ### ES6 類別定義 >在其他的語言,類別是創建物件的藍圖,但在JS,類別本身是被建立出來的物件。 - 骨子裡仍然是以原型為基礎的物件導向!(並非以類別為基礎) - 提供更簡潔的語法來作物件建立與繼承。 - 讓熟悉以類別為基礎的物件導向程式語言的開發者使用。 ```+ class Person { constructor(firstName,lastName) { this.firstName = firstName; this.lastName = lastName; } getFullName: function () { return this.firstName + " " + this.lastName; } } const john = new Person('John',"Wu"); ``` #### 再來看看繼承怎麼寫: ```javascript= function Person(name) { this.name = name; const state = 'Taiwan'; this.getFrom = () => `${this.name} from ${state}.`; } //傳統建構式繼承 function Employee(name, position) { // 將 this 送給 Person 建立 properties Person.call(this, name); this.position = position; // public properties this.getPosition = () => `${this.name}'s position is a ${this.position}.`; } const luck = new Employee('Luck', 'Front-end'); console.log(luck.getFrom()); // Luck from Taiwan. console.log(luck.getPosition()); // Luck's position is the Front-end. -------------------------------------------------------------------------- //ES6 Class繼承 class Employee extends Person { constructor(name, position) { // 用 super 呼叫 extends 指定的 class super(name); this.position = position; } getPosition() { return `${this.name}'s position is a ${this.position}.`; } } const luck = new Employee('Luck', 'Front-end'); console.log(luck.getFrom()); // Luck from Taiwan. console.log(luck.getPosition()); // Luck's position is the Front-end. ``` ### 練習題目 ```+ function Person(name) { this.name = name; } var person = Person('John'); console.log(person); // undefined console.log(person.name); // Uncaught TypeError: Cannot read property 'name' of undefined var person = new Person('John'); console.log(person); // Person { name: "John" } console.log(person.name); // "john" ``` ## 參考資料 https://www.youtube.com/watch?v=xSu7TbPPy34&ab_channel=%E5%BD%AD%E5%BD%AD%E7%9A%84%E8%AA%B2%E7%A8%8B https://wcc723.github.io/javascript/2017/12/18/javascript-constructor/#%E5%BB%BA%E6%A7%8B%E5%BC%8F https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part4/prototype.html https://medium.com/enjoy-life-enjoy-coding/javascript-es6-%E4%B8%AD%E6%9C%80%E5%AE%B9%E6%98%93%E8%AA%A4%E6%9C%83%E7%9A%84%E8%AA%9E%E6%B3%95%E7%B3%96-class-%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95-23e4a4a5e8ed