談談JavaScript中的function constructor和new關鍵字

您是否曾困惑於 Javascript 中的new關鍵字呢?是否曾想理解關於 function 和 constructor 的區別是什麼呢?javascript

大多數 Javascript 的新開發者不太想要使用new關鍵字,由於這會讓代碼寫的像是 Java 而且在使用上會形成一點混亂;在這篇文章我會盡可能嘗試着去理清這些東西,並解釋它是如何運做的。java

談談function constructor

constructor 翻爲構造器但爲了讓您以後更好地理解,會直接使用 constructor;在 Javascript 中任何一個函數(function)均可以被看成 constructor;Javascript 並無明確的區分二者,也就是說 function 能夠被看成 constructor 或者看成通常函數調用。git

constructor 的用法就是 function 搭配new關鍵字:github

function Person ( ) { 
  this . firstName =  'Jay' ; 
  this . lastName =  'Chou' ; 
}

var person =  new  Person ( ) ; 
console . log ( person ) ;
複製代碼

接着咱們把person呼叫出來看,會獲得以下的結果,經過new它會幫咱們創建一個對象,而後裏面有Person這個function裏面的內容,而且變成了屬性名稱和屬性值:chrome

進一步來看new 讓這個過程發生了什麼

當使用new命令時,它後面的函數依次執行下面的4個步驟:數組

  1. 建立一個空對象,做爲將要返回的對象實例。
  2. 將這個空對象的原型,指向構造函數的prototype屬性。
  3. 將這個空對象賦值給函數內部的this關鍵字。
  4. 若是函數沒有return其餘對象,那麼new表達式中的函數調用會自動返回這個新對象。

也就是說,當咱們使用new這個關鍵字時,實際上會先有一個空的對象被創建。app

接着People這個構造函數會被執行,這個空對象的原型,指向了People.prototype函數

咱們知道當函數執行的時候,在execution context中會有this被創建,而當咱們使用new的時候,函數裏面的this會被指定成剛剛所創建的那個空對象ui

因此當執行People這個function,執行到this.firstNamethis.lastName時,由於this如今指稱的是那個空對象,因此其實是在幫這個空對象賦予屬性名稱和屬性值。this

在這樣的過程當中,只要這個構造函數People 沒有指定return爲其餘對象,它就會直接返回給咱們這個新創建的對象

接着讓咱們經過一段代碼來更清楚的瞭解這個執行的過程:

function Person ( ) { 
  this . firstName =  'Jay' ; 
  this . lastName =  'Chou' ; 
  console . log ( '這個函數有被執行' ) ;
}

var person =  new  Person ( ) ; 
console . log ( person ) ;
複製代碼

這時候在chrome 中呼叫出來的結果以下,說明了當咱們使用new 在構造對象的時候People 這個function 確實有被執行:

經過new 會幫咱們創建一個空的對象

如今我把咱們上面的代碼改爲這樣:

function Person ( ) { 
  console . log ( this ) ; 
}

var person =  new  Person ( ) ; 
// console.log(person);
複製代碼

這時候代碼返回的結果以下,表示的確在執行這段代碼的過程當中幫咱們創建了一個新的空對象:

函數的最後若return其餘對象,則原新對象內容會被覆蓋

如今,讓咱們把本來的代碼稍微作以下修改:

function Person ( ) { 
  this . firstName =  'Jay' ; 
  this . lastName =  'Chou' ; 
  return   { "RETURN" : "本來this的內容就不會被返回" } ; 
}

var person =  new  Person ( ) ; 
console . log ( person ) ;
複製代碼

構造函數的內部若return其餘對象,new命令會返回return語句指定的對象,將原新對象內容覆蓋掉;不然,就會無論return語句,返回this對象。返回的結果以下:

手寫一個new實現

function create() {
	// 建立一個空的對象
    var obj = new Object(),
	// 得到構造函數,arguments中去除第一個參數
    Con = [].shift.call(arguments);
	// 連接到原型,obj 能夠訪問到構造函數原型中的屬性
    obj.__proto__ = Con.prototype;
	// 綁定 this 實現繼承,obj 能夠訪問到構造函數中的屬性
    var ret = Con.apply(obj, arguments);
	// 優先返回構造函數返回的對象
	return ret instanceof Object ? ret : obj;
};
複製代碼

使用這個手寫的new

function Person() {...}

// 使用內置函數new
var person = new Person(...)
                        
// 使用手寫的new,即create
var person = create(Person, ...)
複製代碼

代碼原理解析:

  1. new Object() 的方式新建了一個對象obj
  2. 取出第一個參數,就是咱們要傳入的構造函數。此外由於 shift 會修改原數組,因此 arguments 會被去除第一個參數
  3. obj 的原型指向構造函數,這樣obj就能夠訪問到構造函數原型中的屬性
  4. 使用 apply,改變構造函數 this 的指向到新建的對象,這樣 obj 就能夠訪問到構造函數中的屬性
  5. 返回 obj

function constructor 的實際應用

由上面的方法,咱們能夠經過function的方式來創建一個新的對象,若是咱們想要創建出同屬性名稱但不一樣屬性值的對象內容,咱們能夠把對象的屬性值變成參數,如此就能經過此function constructor創建出許多不一樣的對象:

function Person ( firstName , lastName ) { 
  this . firstName = firstName ; 
  this . lastName = lastName ; 
}

var person1 =  new  Person ( 'Jay' ,  'chou' ) ; 
console . log ( person1 ) ; 
var person2 =  new  Person ( 'Jane' ,  'chou' ) ; 
console . log ( person2 ) ;
複製代碼

如此,咱們就能夠經過同一個構造函數創建出不少不一樣的對象:

此外,咱們會把根據構造器(constructor)所創建出來的對象稱做是實例(instance,這在以前的文章裏也有提過。

注意!若是咱們忘了加上new關鍵字

這裏有一個地方咱們須要很是留意,若是你在撰寫代碼的過程中,忘記加上new這個關鍵字的話,好比:

function Person ( ) { 
  this . firstName =  'Jay' ; 
  this . lastName =  'Chou' ;  
}

var person =  Person ( ) ; 
console . log ( person ) ;
複製代碼

如此,由於JavaScript不知道你是要執行這個函數,仍是要根據這個function去創建object,因次最後會返回undefined的結果。

小結

  • 其實構造函數(function constructor)就是普通的function,只是咱們能夠經過這個function 來創建對象。
  • 經過在function 前面加上new 這個運算符,它會把函數中this 這個關鍵字創建成一個新的對象,而後若是你沒有在該函數的內部指定返回出其它對象的話,它就會自動返回這個新的對象給你。

那又是如何經過function constructors 來設定咱們的原型(prototype)呢?讓咱們在下一篇文章來談吧!

若是以爲文章對你有些許幫助,歡迎在個人GitHub博客點贊和關注,感激涕零!

相關文章
相關標籤/搜索