JavaScript中的function能夠有如下兩種用法:
一是作「普通邏輯代碼容器」,也就是咱們一般意義上的函數、方法,和咱們C/C++裏的函數沒什麼大分別,只是寫法稍有不一樣、用法更加靈活;
二是作對象,有的地方叫它函數對象,其用法和做用有點相似C++裏的class(類)。
下面來詳細說說這兩種用法。
1、 function用做普通函數
function用做普通函數的定義方法以下:
function functionName([argument1] [, argument2] [..., argumentN]){
[statements]
}
具體寫法有如下兩種:
1. 定義式:
如:javascript
它的使用方法以下:html
2. 聲明式:
如:java
須要說明的是:
1. 用做普通函數時,function幾乎能夠在腳本的任何地方定義,但推薦在一個HTML文檔的<head></head>區域裏定義,這樣能夠保證若是另外一個腳本須要當即使用這裏聲明的函數時,就能夠當即使用它。
2. 上述兩種具體寫法在重複定義的時候也有一些差異,以下
若作以下函數定義:瀏覽器
獲得結果是app
若作以下函數定義:函數
那麼會獲得另外一種結果:this
在採用定義式建立同名函數時,後建立的函數會覆蓋先建立的函數。這種差異是因爲JavaScript解釋引擎的工做機制所致使的。因爲註冊函數時,後定義的函數重寫了先定義的函數,所以不管調用語句位於何處,執行的都是後定義的函數。相反,對於聲明式建立的函數,JavaScript解釋引擎會像對待任何聲明的變量同樣,等到執行調用該變量的代碼時纔會對變量求值。所以當執行第一個example()調用時,example函數的代碼就是首先定義代碼;而當執行第二個example()調用時,example函數的代碼又變成了後來定義的代碼。
固然,好的習慣是不要這樣寫,也不要試圖利用「聲明式」的這種機制來投機取巧。不過,函數重載除外,可是,javascript裏好像並無函數重載這種寫法吧?spa
2、函數對象.net
1.基本概念
在JavaScript中,function還能夠被用作對象(或者竊覺得該叫作類更合適)。這也許聽起來很怪異也很難理解,但考慮到JavaScript既然是一種面向對象的語言,那麼它裏面總得能夠實現類和對象吧?看看下面的用法就知道了——用function來實現類和對象倒也真無可厚非。
首先要說明一下的是與function有密切關係的this這個東西。「JavaScript在解析代碼時,會爲聲明或定義的函數指定調用對象。所謂調用對象,就是函數的執行環境。」也就是說,在函數體中,能夠以this關鍵字來使用它的調用對象(關於this的具體用法,另做討論,詳見下篇)。「若是函數體內有以關鍵字this聲明的變量,則this引用的就是調用對象。」
下面就來看看做爲函數對象的function一般是怎麼寫的:prototype
上面的代碼就定義了一個函數對象,其意義與C++中的class類似,它的構造函數就是這個函數Animal。其實看起來跟上面的普通函數沒什麼分別,換句話說,按照上面介紹的普通函數定義方法寫,結果就會獲得一個函數對象,竊覺得JavaScript中其實只存在函數對象,不存在咱們傳統意義上的「函數」,只是它的使用方法靈活多樣,能夠按照咱們傳統的使用方法functionName(…)直接調用,也能夠按下面的方法做爲對象使用:
2.函數對象建立過程
函數怎麼又成了對象了呢?它是怎麼構造的呢?先來了解一下JavaScript裏的函數對象都有什麼吧~簡單地說,JavaScript裏的函數對象最初包含一個默認的構造函數,函數名是Object,同時,還有個成員(屬性)——__proto__,與大名鼎鼎的prototype屬性相關(用於實現JavaScript裏的繼承),關於prototype的用法,另做討論,詳見後文。
瞭解了這些,再看看上面這個dog對象的構造過程吧~
「建立dog的對象的過程以下:首先,new運算符建立一個空對象({}),而後以這個空對象爲調用對象調用函數Animal」(也就是跟傳統意義上的對象構造過程相同,調用它的構造函數進行初始化)「,爲這個空對象添加兩個屬性sort和character,接着,再將這個空對象的默認constructor屬性修改成構造函數的名稱(即Animal;空對象建立時默認的constructor屬性值是Object),而且將空對象的__proto__屬性設置爲指向Animal.prototype——這就是所謂的對象初始化。最後,返回初始化完畢的對象。這裏將返回的新對象賦值給了變量dog。」
3.直接實例化的寫法
函數對象的定義、實例化過程也能夠簡化以下:
上面的代碼中,dog是個對象,它有name、age兩個屬性,還有個成員函數(也是個對象,就是咱們的函數對象)shout。這裏的shout的定義方法就是作了簡化——直接被function賦值。
對象也能夠借用其餘對象的方法:
這裏須要強調的是,每一個函數對象都有兩個特殊的方法——call和apply,用它們能夠動態指定函數或方法的調用對象:
從這裏想到,是否是咱們能夠用call或apple函數來替代上面的方法進行函數對象的實例化呢?答案是否認的。讓咱們來從這個角度進一步分析一下函數對象的構造過程,以便加深理解:
若是咱們企圖這麼寫來達到函數對象實例化的效果:
那麼,「表面上看,這兩行代碼與var dog = new Animal(」mammal」,」four legs」);是等價的,其實卻不是。雖然經過指定函數的執行環境可以部分達到初始化對象的目的,例如空對象dog確實得到了sort和character這兩個屬性「:
然而,「最關鍵的是新建立的dog對象失去了經過Animal.prototype屬性繼承其餘對象的能力。只要與前面採用new運算符調用構造函數建立對象的過程對比一下,就會發現,new運算符在初始化新對象期間,除了爲新對象添加顯式聲明的屬性外,還會對新對象進行了一番「暗箱操做」——即將新對象的constructor屬性重寫爲Animal,將新對象的__proto__屬性設置爲指向Animal.prototype。雖然手工「初始化對象」也能夠將dog.constructor重寫爲Animal,但根據ECMA262規範,對象的__proto__屬性對開發人員是隻讀的,對它的設置只能在經過new運算符建立對象時由JavaScript解釋引擎替咱們完成。」
看看這樣作的後果:若是不能正確設置對象的__proto__屬性,那麼就意味着默認的繼承機制會失效:
事實上,雖然在Firefox中,__proto__屬性也是可寫的:
但這樣作只能在Firefox中行得通。考慮到在兼容多瀏覽器,必須依賴於new運算符,才能實現基於原型的繼承。
https://www.cnblogs.com/lidabo/archive/2012/01/06/2313933.html