一個普通的函數被用於建立一個類物件時,它就被稱作建構函式,或者構造器。(為方便理解,你可以將JavaScript中構造器的建立理解為其他語言中的類的建立,目的就是利用它通過new來實列一個物件)
function Person(){ //... } //當做普通函數呼叫 var obj=Person(); //建構函式呼叫 var obj=new Person();
建構函式的特點:
在書寫規範上,我們習慣將建構函式名稱的首字母大寫。
通過new來建立一個物件。
無需在內寫入return也會有返回值,而且返回的是一個物件。
利用建構函式建立一個js物件
建構函式建立物件(方法寫在建構函式裡,缺點:建構函式每執行一次, 就會建立一次方法。)
function Person(name,age,gender){ this.name=name; this.age=age; this.gender=gender; // 方法寫在裡面 this.sayName=function(){ console.log(this.name); } } function Dog(name,age){ this.name=name; this.age=age; } var obj=new Person("張三",18,"男"); var obj1=new Person("李四",16,"男"); var dog=new Dog("樂樂",2); obj.sayName(); obj1.sayName(); console.log(obj); console.log(obj1); console.log(dog);
建構函式建立物件(方法寫在建構函式外,缺點: 方法為全域性方法,汙染全域性。)
function Person(name,age,gender){ this.name=name; this.age=age; this.gender=gender; this.sayName=fun; //方法寫在外面 } function fun(){ console.log(this.name); } function Dog(name,age){ this.name=name; this.age=age; } var obj=new Person("張三",18,"男"); var obj1=new Person("李四",16,"男"); var dog=new Dog("樂樂",2); obj.sayName(); obj1.sayName(); console.log(obj); console.log(obj1); console.log(dog);
建構函式建立物件改造(方法通過原型物件建立)
原型物件:prototype
我們所建立的每一個函數,解析器都會向函數中新增一個prototype屬性。
指向建構函式的原型物件,我們可以通過__proto__來存取該屬性。
建構函式.prototype.xxx , xxx可以是變數,可以是方法。執行過程中會先去物件中找方法或者變數, 找不到就會去原型裡尋找。
function Person(name,age,gender){ this.name=name; this.age=age; this.gender=gender; } function Dog(name,age){ this.name=name; this.age=age; } /*為person新增統一的方法, 到原型物件中*/ Person.prototype.sayName=function(){ console.log(this.name); } var obj=new Person("張三",18,"男"); var obj1=new Person("李四",16,"男"); var dog=new Dog("樂樂",2); obj.sayName(); obj1.sayName(); console.log(obj); console.log(obj1); console.log(dog);
執行結果:
學習每一個概念,不僅要知道它是什麼,還要知道為什麼,以及解決什麼樣的問題。
舉個例子,我們要錄入一年級一班中每一位同學的個人資訊,那麼我們可以建立一些物件,比如:
var p1 = { name: 'zs', age: 6, gender: '男', hobby: 'basketball' }; var p2 = { name: 'ls', age: 6, gender: '女', hobby: 'dancing' }; var p3 = { name: 'ww', age: 6, gender: '女', hobby: 'singing' }; var p4 = { name: 'zl', age: 6, gender: '男', hobby: 'football' }; // ...
像上面這樣,我們可以把每一位同學的資訊當做一個物件來處理。但是,我們會發現,我們重複地寫了很多無意義的程式碼。比如 name、age、gender、hobby 。如果這個班上有60個學生,我們得重複寫60遍。
這個時候,建構函式的優勢就體現出來了。我們發現,雖然每位同學都有 name、gender、hobby這些屬性, 但它們都是不同的,那我們就把這些屬性當做建構函式的引數傳遞進去。而由於都是一年級的學生,age 基本都是6歲,所以我們就可以寫死,遇到特殊情況再單獨做處理即可。此時,我們就可以建立以下的函數:
function Person(name, gender, hobby) { this.name = name; this.gender = gender; this.hobby = hobby; this.age = 6; }
當建立上面的函數以後, 我們就可以通過 new 關鍵字呼叫,也就是通過建構函式來建立物件了。
var p1 = new Person('zs', '男', 'basketball'); var p2 = new Person('ls', '女', 'dancing'); var p3 = new Person('ww', '女', 'singing'); var p4 = new Person('zl', '男', 'football'); // ...
此時你會發現,建立物件會變得非常方便。所以,雖然封裝建構函式的過程會比較麻煩,但一旦封裝成功,我們再建立物件就會變得非常輕鬆,這也是我們為什麼要使用建構函式的原因。
在使用物件字面量建立一系列同一型別的物件時,這些物件可能具有一些相似的特徵(屬性)和行為(方法),此時會產生很多重複的程式碼,而使用建構函式就可以實現程式碼複用
。
先說一點基本概念。
function Animal(color) { this.color = color; }
當一個函數建立好以後,我們並不知道它是不是建構函式,即使像上面的例子一樣,函數名為大寫,我們也不能確定。只有當一個函數以 new 關鍵字來呼叫的時候,我們才能說它是一個建構函式。就像下面這樣:
var dog = new Animal("black");
以下我們只討論建構函式的執行過程,也就是以 new 關鍵字來呼叫的情況。
我們還是以上面的 Person 為例。
function Person(name, gender, hobby) { this.name = name; this.gender = gender; this.hobby = hobby; this.age = 6; } var p1 = new Person('zs', '男', 'basketball');
此時,建構函式會有以下幾個執行過程:
1)當以 new 關鍵字呼叫時,會建立一個新的記憶體空間,標記為 Animal 的範例。
2)函數體內部的 this 指向該記憶體
通過以上兩步,我們就可以得出這樣的結論。
var p2 = new Person('ls', '女', 'dancing'); // 建立一個新的記憶體 #f2 var p3 = new Person('ww', '女', 'singing'); // 建立一個新的記憶體 #f3
每當建立一個範例的時候,就會建立一個新的記憶體空間(#f2, #f3),建立 #f2 的時候,函數體內部的 this 指向 #f2, 建立 #f3 的時候,函數體內部的 this 指向 #f3。
3) 執行函數體內的程式碼
通過上面的講解,你就可以知道,給 this 新增屬性,就相當於給範例新增屬性。
4)預設返回 this
由於函數體內部的this指向新建立的記憶體空間,預設返回 this ,就相當於預設返回了該記憶體空間,也就是上圖中的 #f1。此時,#f1的記憶體空間被變數p1所接受。也就是說 p1 這個變數,儲存的記憶體地址就是 #f1,同時被標記為 Person 的範例。
以上就是建構函式的整個執行過程。
建構函式執行過程的最後一步是預設返回 this 。言外之意,建構函式的返回值還有其它情況。下面我們就來聊聊關於建構函式返回值的問題。
1)沒有手動新增返回值,預設返回 this
function Person1() { this.name = 'zhangsan'; } var p1 = new Person1();
按照上面講的,我們複習一遍。首先,當用 new 關鍵字呼叫時,產生一個新的記憶體空間 #f11,並標記為 Person1 的範例;接著,函數體內部的 this 指向該記憶體空間 #f11;執行函數體內部的程式碼;由於函數體內部的this 指向該記憶體空間,而該記憶體空間又被變數 p1 所接收,所以 p1 中就會有一個 name 屬性,屬性值為 ‘zhangsan’。
p1: { name: 'zhangsan' }
2)手動新增一個基本資料型別的返回值,最終還是返回 this
function Person2() { this.age = 28; return 50; } var p2 = new Person2(); console.log(p2.age); // 28 p2: { age: 28 }
如果上面是一個普通函數的呼叫,那麼返回值就是 50。
3)手動新增一個複雜資料型別(物件)的返回值,最終返回該物件
直接上例子
function Person3() { this.height = '180'; return ['a', 'b', 'c']; } var p3 = new Person3(); console.log(p3.height); // undefined console.log(p3.length); // 3 console.log(p3[0]); // 'a'
再來一個例子
function Person4() { this.gender = '男'; return { gender: '中性' }; } var p4 = new Person4(); console.log(p4.gender); // '中性'
大小寫都可以
如果不會出錯,那麼,用new和不用new呼叫建構函式,有什麼區別?
1)使用new操作符呼叫函數
例子:
function Person(name){ this.name = name; this.say = function(){ return "I am " + this.name; } } var person1 = new Person('nicole'); person1.say(); // "I am nicole"
用new呼叫建構函式,函數內部會發生如下變化:
建立一個this變數,該變數指向一個空物件。並且該物件繼承函數的原型;
屬性和方法被加入到this參照的物件中;
隱式返回this物件(如果沒有顯性返回其他物件)
用偽程式來展示上述變化:
function Person(name){ // 建立this變數,指向空物件 var this = {}; // 屬性和方法被加入到this參照的物件中 this.name = name; this.say = function(){ return "I am " + this.name; } // 返回this物件 return this; }
可以看出,用new呼叫建構函式,最大特點為,this物件指向建構函式生成的物件,所以,person1.say()會返回字串: 「I am nicole」。
小貼士:如果指定了返回物件,那麼,this
物件可能被丟失。
function Person(name){ this.name = name; this.say = function(){ return "I am " + this.name; } var that = {}; that.name = "It is that!"; return that; } var person1 = new Person('nicole'); person1.name; // "It is that!"
2)直接呼叫函數
如果直接呼叫函數,那麼,this物件指向window,並且,不會預設返回任何物件(除非顯性宣告返回值)。
還是拿Person函數為例,直接呼叫Person函數:
var person1 = Person('nicole'); person1; // undefined window.name; // nicole
可見,直接呼叫建構函式的結果,並不是我們想要的。
3)小結
為了防止因為忘記使用new關鍵字而呼叫建構函式,可以加一些判斷條件強行呼叫new關鍵字,程式碼如下:
function Person(name){ if (!(this instanceof Person)) { return new Person(name); } this.name = name; this.say = function(){ return "I am " + this.name; } } var person1 = Person('nicole'); console.log(person1.say()); // I am nicole var person2 = new Person('lisa'); console.log(person2.say()); // I am lisa
【相關推薦:】
以上就是什麼是建構函式?詳解JavaScript中的建構函式的詳細內容,更多請關注TW511.COM其它相關文章!