函數建構子/ .prototype/ new與函數/ 內建的函數建構子/ Object.create
函數建構子 (Function Constructors)
A normal function that is used to construct objects.
The “this” variable points a new empty object, and that object is returned from the function automatically.
一個正常的函數用來建立物件。
當呼叫函數前面放了 new 關鍵字,在執行環境的創造階段被產生的this變數會指向新的空物件,當函數結束執行時,該物件會被函數自動回傳。
function Person(firstname, lastname){// 執行 john 時指向 john 的物件,執行 jane 時指向 jane 的物件
console.log(this) this.firstname = firstname;
this.lastname = lastname; console.log(‘This function is invoked’) //return console.log('yo') 在此就會回傳'yo'而非建立的物件}// Person 會是 john 和 jane 的原型
var john = new Person(‘John’,’Doe’)
var jane = new Person(‘Jame’,’Doe’)*new 是運算子的一種
使用 new 運算子會建立一個新物件,在後面呼叫函數 Person,函數被呼叫時 執行環境會產生 this 給我們,this 變數會指向那個新的空物件記憶體位置。
只要函數使用new運算子的話,不會回傳值, 而是回傳被 new 運算子建立的物件,用 this 變數指向的空物件呼叫函數,無論用 this 變數對空物件做什麼,就會成為那個物件的一部份,也就是會回傳的東西(在此指 john 和 jane)。
使用 new 會自動回傳空物件,但如果設定 return 回傳別的東西(例如console.log(‘yo’)),就會回傳我們設定的東西而非原本會回傳的物件。
函數建構子與「.prototype」
所有的函數都有原型屬性(prototype property),從它是空物件就誕生,除非將函數作為建構子使用,不然它就只是待在那不會用到,但一旦用 new 運算子呼叫函數,它(prototype property)就有意義了。
function Person(firstname, lastname){ this.firstname = firstname;
this.lastname = lastname;}//在 Person 的 prototype 屬性下新增方法 getFullName
Person 的原型屬性,就是用函數建構子建立的物件(john, jane)的原型鏈指向的東西Person.prototype.getFullName = function() {
return this.firstname + " " + this.lastname;};var john = new Person(‘John’,’Doe’)
var jane = new Person(‘Jame’,’Doe’)john.__proto__ = Object { getFullName: getFullName(), … }//呼叫 getFullName,在john中找不到,就會往Person找並執行
john.getFullName() // "John Doe"
函數都有屬性 .prototype.,它從空物件就誕生,可為它新增屬性或方法。
當用 new 關鍵字建立空物件,它會設定空物件(john, jane )的原型(Person.prototype),讓你可以接著呼叫的函數的原型屬性,所以任何你用函數建構子建立的物件,尤其是用 new 關鍵字,被建立時不只有你藉由函數給它的屬性和方法,還有原型,也就是 .prototype 函數的原型屬性。
這個函數的原型屬性(prototype),其實就是用函數建構子建立的物件其原型鏈所指向的東西,所以 john.__proto__ 和 jane.__proto__ 指向 person.prototype 作為它的原型( )。
而為何不直接將 getFullName 建立在 Person 中而是放在 prototype 裡呢?
因為函數就是物件,它們佔據記憶體空間,任何你增加給它們的東西都會佔據記憶體空間,所以若新增 getFullName 給每個物件,表示每個物件都有自己的 getFullName 拷貝,就會佔據更多記憶體空間。
如果有一千個物件就會有一千個 getFullName 方法,但若將 getFullName 增加到原型上,雖然有一千個物件,但只會有一個方法,就效能的觀點來看將屬性和方法放在原型上比較好。
「new 」與函數
若沒在呼叫 Person 函數前加入 new ,函數呼叫依然會執行,但就不會新增空的物件也不會回傳物件,而 Person 中並未設定 return 的東西,因此結果會是 undefined。
建議建構函數都以首字大寫命名,以降低出錯的機會。
function Person(firstname, lastname) {
this.firstname = firstname;
this.lastname = lastname;}var john = Person(“John”, “Doe”) ->undefuined
內建的函數建構子
var a = new Number(3) //輸入 a 回傳 Number { 3 } 為一物件a.__proto__ = Number{ 0 }a.toFixed(2) = "3.00" //可以使用Number.prototype底下的方法
使用 new 建立數字 a 為一物件,它有原型 Number.prototype,所以 Number 物件的方法例如 toPrecision 或 toFixed 都可以取用到。
使用 new 建立字串同理。
var a = new String(“john”) //輸入 a 回傳 String { “john” } 為一物件a.__proto__ = String { "" }a.indexOf('o') = 1
所以當使用 new 建構子要記得是在建立物件,並可以利用 prototype 加入方法,這在建立一些資源庫或框架的額外功能時很有幫助。
在字串原型加入判別長度的方法,所有的字串都能使用String.prototype.isLengthGreaterThan = function(limit) {
return this.length > limit }console.log(“john”.isLengthGreaterThan(3));_____在數字原型加入判別長度的方法,所有的數字都能使用Number.prototype.isPositive = function() {
return this > 0 }var a = new Number(2)
console.log(a.isPositive());*JS會把字串包到物件中,但它不會自動轉換數值為物件,故上面數字使用new建立
*不建議使用 new 建構子來建立純值,因為純值會被轉為物件。
Object.create 與純粹的原型繼承
Object.create會用它的原型建立空物件,它的原型就是傳入 Object.create 的東西,下例建立 john 並利用 Object.create 傳 Person 當作其原型。
var Person = {
firstname: “Default”,
lastname: “Default”,
greet: function() { return “Hi ” + this.firstname;}
}var john = Object.create(Person); //此時john為空物件,proto為Person
在空陣列 john 中建立同樣名稱的屬性或方法
JavaScript引擎會到原型鏈上尋找屬性和方法直到它找到,建立john的firstname和lastname後,john 仍有它的原型可以呼叫,因此執行 john.greet()可以得到'Hi John'john.firstname = “John”;
john.lasttname = “Doe”;
john.greet() // 'Hi John'