new
首先,new
是一個操做符,它能夠用來建立兩種對象的實例,一種是用戶定義的對象類型,另外一種則是擁有構造函數的內建對象類型。javascript
建立用戶定義的對象須要兩個步驟:java
new
來建立對象實例。示例一:函數
var Person = function(personName) { this.name = personName; };
這是一個典型的經過編寫函數來定義對象類型的範例,咱們能夠這樣來表述其行爲:this
Person
函數定義了一種對象類型,其 類型名稱 就叫 Person。
在使用Person
函數建立對象實例的時候能夠傳入變量personName
,該變量會成爲對象實例的一個屬性,這個屬性的名字叫name
。
爲對象實例定義name
屬性的過程發生在Person
函數的函數體內;this
即指代將被建立的對象實例。prototype
值得初學者注意的是,在現實中你更多地會看到這樣的代碼:翻譯
var Person = function(name) { this.name = name; };
有些人會搞不清楚究竟哪個 name
纔是對象的屬性,在這裏詳細解釋以下:code
function(name)
裏的 name
是待傳入參數的名字,一般被稱做:形式參數(Formal Parameter),或簡稱 形參 ——由於它只是表明參數的形式而並不是真正傳入的參數(後者則被稱做:實際參數(Actual Parameter),或簡稱 實參)。this.name
的 name
是對象的屬性名字。= name
的 name
仍是形參,和 1. 裏的 name
等價;這就是所謂的 參數傳遞,或傳參。示例二:orm
var albert = new Person('Albert'); albert.name; // "Albert" albert['name']; // "Albert"
這是承接示例一,使用定義好的 Person
對象類型來實例化對象的範例,這個範例的表述相對容易一些:對象
定義一個變量
albert
,而後實例化一個新的Person
類型的對象,並將變量albert
指向這個新的對象。繼承
若是你對 形參 和 實參 還不夠清楚的話,看到這裏就應該徹底明瞭了。保險起見再加以解釋以下:
new Person('Albert')
中的 'Albert'
即對應着示例一中 function(name)
中的 name
,同時也是接下來一行中等號右邊的 name
。'Albert'
就是 實際參數,name
就是 形式參數。示例二中還演示了兩種對象屬性的獲取方法,分別爲 object.property
和 object['property']
。前一種比較經常使用,不事後一種因爲能夠用字符串來訪問對象屬性,所以在某些場合下很是有用(好比說用字符串傳遞了對象的屬性)。
回到 new
的話題。
當 new Person('Albert')
執行的時候,會有以下事情發生:
Person
並繼承 Person.prototype
的全部屬性。這就是 原型繼承;Person
被調用並傳入指定的參數(示例二中的 'Albert'
),而後 this
被綁定給新建立的對象。另外,若構造函數不須要參數,則 new Person
等價於 new Person()
;new
表達式的結果;反之,若構造函數內顯式定義了返回值,則該返回值爲整個 new
表達式的結果。關於第三點,舉例示之:
示例三:
var Person = function(name) { return { name: 'Mr. ' + name } }; var albert = new Person('Albert'); albert.name; // "Mr. Albert"
var Person = function(name) { return { rawName: name, getName: function(gender) { if (gender === 'male') { return 'Mr. ' + name; } else { return 'Mrs. ' + name; } } } }; var albert = new Person('Albert'); albert.getName('male'); // "Mr. Albert" albert.getName('female'); // "Mrs. Albert"
var Person = function(name, gender) { return { rawName: name, name: (function() { if (gender === 'male') { return 'Mr. ' + name; } else { return 'Mrs. ' + name; } }()) } }; var albert = new Person('Albert', 'male'); albert.rawName; // "Albert" albert.name; // "Mr. Albert" var annie = new Person('Annie', 'female'); annie.rawName; // "Annie" annie.name; // "Mrs. Annie"
示例三演示了三種看起來類似但實際上具備顯著差別的對象類型定義和對象實例化的例子:
第一種:在 new Person('Albert')
時返回自定義的對象,而不是默認由 new
建立的新對象。在這個自定義對象裏,沒有簡單地把參數 name
賦給屬性 this.name
,而是作了進一步的修改。這種修改很顯然是很是簡單但卻不夠靈活,爲了改進它,看下面兩個例子:
第二種:一樣返回自定義對象,這一次定義了兩個屬性,一個是 rawName
,保存實例化時傳遞的參數;另外一個是 getName
,它是一個函數聲明,所以不能直接用 albert.getName
或 albert['getName']
來訪問(只會返回函數聲明自己,但不會有返回值)。不過你能夠用albert.getNam('male')
或 albert['getName']('male')
的方式來執行這個函數並求得結果,這就是所謂的 方法。
若是不想用方法調用,但仍然但願像方法聲明體內那樣作一些邏輯判斷是否能夠呢?能夠,繼續看第三種:
第三種:這一次 name
屬性又能夠像之前那樣直接訪問了,緣由是 name
指向的函數聲明使用了 IIFE(Immediately Invoked Function Expression) 技巧,該技巧使得函數聲明直接轉變成了函數表達式(並即刻執行)。咱們知道,函數表達式是可以直接返回值的,而函數聲明則須要執行(調用)才能返回值,因而 name
屬性得到了返回值,就能夠像原來那樣直接訪問了。
這種屬性定義方式有時被稱之爲 計算後屬性(Computed Property),顧名思義:不是直接返回實例化時傳遞的值,而是對值進行了必定的處理(計算)以後才返回。
屬性與方法:不少人都覺得對象有 屬性 和 方法,其中屬性是能夠直接訪問到值的,而方法是須要執行才能得到值的。但有的時候也會聽到「方法也是屬性」這樣的說法,這是爲何呢?
其實緣由在於對術語的翻譯不夠準確。英文裏的property
和attribute
都被咱們翻譯爲屬性,然而在談及對象時這二者是不一樣的。在一個對象裏,attribute
和method
被統稱爲property
,直接保存值的property
稱之爲attribute
,保存函數聲明能夠用來執行的property
纔是method
。在用中文描述時很容易把兩種「屬性」搞混,須要注意分辨清楚。
補充說明:本文發表出去以後,有人感謝我幫他分清了 屬性 和 方法 在一些書中的歧義性,也有人拿着別的書來向我表示疑惑。以再版的 Object Oriented in JavaScript 爲例,該書中在講解面向對象基礎的時候,明確地指出:保存數據的屬性叫作 Property,保存行爲的屬性叫作 Method,根本就不使用 Attribute。這卻是也簡單明瞭,這樣一來就不存在 Method 也是 Property 一說了。
老實說,對此我也不知該如何迴應。不一樣的書用不一樣的術語,不一樣的做者也有不一樣的理解,我沒有「統一業界術語」的能量,因此也只能把它們一一列舉出來。對於初學者若形成理解上的誤差我表示道歉,總而言之你要記住:一個對象有兩類東西:一類記錄數據,另外一類記錄方法(方法老是作一件什麼事,其中也包括返回新的數據),至於這兩類在不一樣的情境中叫法不一,也就要靠你本身去分辨了。