JS物件導向程式設計(二):建構函式

justcool發表於2018-11-22

一、什麼是建構函式?

  建構函式 ,是一種特殊的方法。主要用來為物件成員變數賦初始值,總與new運算子一起使用在建立物件的語句中,對於JavaScript的內建物件Number()、String()、Boolean()、Object()、Array()、Function()、Date()、RegExp()、Error()等都是建構函式;

建構函式的特點:

  1. 建構函式的首字母大寫,用來區分於普通函式
  2. 內部使用的this物件,來指向即將要生成的例項物件
  3. 使用New來生成例項物件

舉個栗子:

function Person(name, age, job){ 
     this.name = name; 
     this.age = age; 
     this.job = job; 
     this.sayHello = function(){ 
        console.log(this.name+":Hello!"); 
     }; 
} 
var person1 = new Person("lilei", 26, "Teacher"); //例項物件 person1
var person2 = new Person("xiaom", 27, "Doctor");  //例項物件 person2
複製程式碼

  建構函式與其他函式的唯一區別,就在於呼叫它們的方式不同。建構函式畢竟也是函式,不存在定義建構函式的特殊語法。任何函式,只要通過 new 操作符來呼叫,那它就可以作為建構函式

二、建構函式與物件

  上一節我們說到在JavaScript中,幾乎所有的事物都是物件:物件只是帶有屬性和方法的特殊資料型別
  但是基本型別值不是物件,因而從邏輯上講它們不應該有方法,實際上我們建立 String 型別時後臺自動做了一些處理:

  1. 建立 String 型別的一個例項;
  2. 在例項上呼叫指定的方法;
  3. 銷燬這個例項。
var s1 = new String("some text"); 
var s2 = s1.substring(2); 
s1 = null; 
複製程式碼

  基本型別值不是物件,只有引用型別是物件,但是 我們可以讓String成為引用型別:

var s1 = new String("some text"); 
複製程式碼

  當我們手動new一個字串s1的時候,s1既是String又是引用型別,所以他是一個物件

  引用型別與基本包裝型別的主要區別就是物件的生存期。使用 new 操作符建立的引用型別的例項, 在執行流離開當前作用域之前都一直儲存在記憶體中。而自動建立的基本包裝型別的物件,則只存在於一 行程式碼的執行瞬間,然後立即被銷燬;

三、建立例項物件

要建立 Person 的新例項,必須使用 new 操作符,呼叫建構函式建立物件經過了以下幾個過程:

  1. 建立一個新物件;
  2. 將建構函式的作用域賦給新物件(因此 this 就指向了這個新物件);
  3. 執行建構函式中的程式碼(為這個新物件新增屬性);
  4. 返回新物件;

  在前面例子中,person1 和 person2分別儲存著Person的一個不同的例項。這兩個物件都有一個constructor(建構函式) 屬性,該屬性指向 Person:

console.log(person1.constructor == Person); //true 
console.log(person2.constructor == Person); //true 
複製程式碼

四、判斷物件型別

  在JS中判斷一個變數的型別經常會用 typeof 運算子,但是在使用 typeof 運算子來判斷引用型別時,無論引用的是什麼型別的物件,它都返回"object"。所以在判斷物件的型別時我們可以使用 instanceof 運算子:

console.log(person1 instanceof Object); //true 
複製程式碼

五、 建構函式缺點

  使用建構函式時每個方法都要在每個例項上重新建立一遍:

function Person(name, age, job){ 
     this.name = name; 
     this.age = age; 
     this.job = job; 
     this.sayHello = function(){ 
        console.log(this.name+":Hello!"); 
     }; 
     //this.sayHello = new Function('console.log(this.name+":Hello!")')
} 
複製程式碼

new Function與宣告函式在邏輯上是一樣的,所以每次例項化Person物件時,sayHello 方法也是一個新例項,所以:

 console.log(person1.sayName == person2.sayName); //false 
複製程式碼

注意:person1.sayName 與person1.sayName()不一樣,person1.sayName()是指函式返回值,person1.sayName是指函式本身;

  建立兩個完成同樣任務的 Function 例項的確沒有必要,因此,我們可以通過把函式定義轉移到建構函式外部來解決這個問題。例如:

function Person(name, age, job){ 
     this.name = name; 
     this.age = age; 
     this.job = job; 
     this.sayHello = sayHello; 
} 
function sayHello(){ 
      console.log(this.name+":Hello!"); 
} 
複製程式碼

   這樣將 sayName 屬性設定成指向全域性函式 sayName() 的指標。sayName()只被例項化一次; 但是如果物件需要定義很多方法,那麼就要定義很多個全域性函式。而且在全域性作用域中定義的函式實際上只被某個物件呼叫,這樣不符合全域性函式的理念。我們這個自定義的引用型別也絲毫沒有封裝性可言。好在這些問題可以通過使用原型模式來解決。所以下一節《 prototype(原型)》


文章參考:

《JavaScript 高階程式設計》中文譯本 第三版

相關文章