JavaScript之函數

概述

JavaScript函數是參數化的:函數的定義會包括一個稱爲形參的標識符列表,這些參數在函數題中像局部變量一樣工作。


除了實參,每次調用還會擁有另一個值——本次調用的上下文——這就是this關鍵字的值

如果函數掛載載一個對象上,作爲對象的一個屬性,那就稱它爲對象的方法。當通過這個對象來調用函數時,該對象就是此次調用的上下文,也就是該函數的this值。用於初始化一個新創建的對象的函數稱爲構造函數


在JavaScript裏,函數即對象,程序可以隨意操控它們。

JavaScript的函數可以嵌套在其它函數中定義,這樣它們就可以訪問它們被定義時所處的所用於中的任何變量。這件意味着JavaScript構成一個閉包。


函數定義

函數使用function關鍵字來定義,他可以用在函數定義表達式或者函數聲明語句。


函數表達式可以包含名稱,這在遞歸地時候非常有效

var f = function fact(x) {
   if(x<=1) return 1;
  else retyrb x*fact(x-1);
}
以表達式方式定義的函數,函數名是可選的。


函數名稱通常是動詞或以動詞爲全追的數組。

有一些函數是用作內部函數或私有函數,這種函數通常以一條下劃線爲前綴。


函數聲明式的函數可以在定義前調用,而函數定義表達式型的不可以。

沒有返回值的函數有時候被稱爲過程


函數聲明語句並非真正的語句,但ES規範允許它們作爲頂級語句。它們可以出現在全局代碼或其它函數中,但不能出現在循環、條件判斷等語句中。該限制僅適用於以語句聲明形式定義的函數。函數定義表達式可以出現在JavaScript代碼的任何地方


函數調用

根據ES3和非嚴格的ES55函數調用規定,調用的上下文(this)是全局變量,但是在嚴格模式下,上下文爲undefined


一個方法無非是個博存在一個對象的屬性裏的JavaScript函數。

函數表達式本身就是一個屬性訪問表達式,這意味着該函數被當作一個方法,而不是普通函數來調用

任何函數只要作爲方法調用實際上都會傳入一個隱式的實參——這個實參是一個對象,方法調用的母體就是這個對象。


當方法並不需要返回值時,最好直接返回this。如果在設計的API中一致採用這種方式(每個方法都返回this),使用API就可以進行鏈式調用風格的編程。


this是一個關鍵字,不是變量,不是屬性名——JavaScript語法不允許給this賦值

和變量不同,關鍵字this沒有作用域限制,嵌套的函數不會從嗲用它的函數中繼承this。嵌套函數作爲函數調用,其this不是全局對象就是undefined。如果想訪問外部的this,需要保存到一個變量


如果函數或者方法之前帶有關鍵字new,那麼久構成構造函數調用。

若構造函數無形參,JavaScript構造函數調用的語法是允許省略實參列表和圓括號的。


構造函數調用創建一個新的空對象,這個對象繼承自構造函數的prototype屬性。構造函數試圖初始化這個新創建的對象,並將這個對象用作其調用上下文,因此構造函數可以使用this關鍵字來引用這個新創建的對象。

構造函數通常不使用return關鍵字。如果構造函數顯式地使用return語句返回一個對象,那麼調用表達式的值就是這個對象。如果構造函數使用return語句但沒有制定返回值,或者返回一個原始值,那麼這時會將忽略返回值,同時使用這個新對象作爲調用結果。


call()方法使用它自有的實參列表作爲函數實參,apply()方法則要求參數以數組形式傳入


函數的實參和形參

JavaScript中的函數定義並未制定函數形參類型,函數調用也未對傳入的實參值做任何類型檢查。實際上,JavaScript函數調用伸直不檢查傳入參數的個數。


可選實參應當放在實參列表的最後。當前面的實參修養掉過時,可以傳進一個null作爲佔位符。


當調用函數的時候傳入實參個數超過了定義函數時形參的個鵝湖上,沒有辦法直接獲得未命名值的引用。arguments時指向實參對象的引用,而實參對象時一個類數組對象


這種函數可以接受任意個數的實參,被稱爲“不定實參函數”

不定實參函數的實參個數不能爲0,arguments[]讀寫嘴適合的應用場景時在這樣一類函數中,這類函數包括固定個數的命名和必須參數,以及隨後個數不定的可選實參


在非嚴格模式下,當一個函數包含若干形參,實參對象的數組元素時函數形參所對應實參的別名,可以通過arguments進行修改


在非嚴格模式下,函數裏的argumens僅僅是一個標識符,在嚴格模式下,變成了一個保留字。嚴格模式下無法使用arguments作爲形參名或局部變量名,也不能給arguments賦值


在ES5嚴格模式下,對於callee和caller屬性的讀寫操作都會產生類型錯誤。在非嚴格模式下,callee屬性指代當前正在執行的函數


除非函數時一個只用一到兩次,否則贏得添加實參類型檢查邏輯,因爲寧願程序自傳入非法值時報錯,也不願非法值導致程序執行時報錯。


作爲值的函數

在JavaScript中,函數不僅是一種語法,也是值。所以可以將函數複製給變量,存儲在對象的屬性火數組元素中,作爲參數傳入另一個參數。


當函數作爲對象屬性調用的時候,函數就被稱爲方法


當函數需要一個靜態變量來在調用時保持某個值不變,最方便的方式就是給函數定義屬性,而不是定義全局變量

uniqueInteger.couter = 0;

function uniqueInteger() {
    return uniqueInteger.counter++;
}

P180有非常好的例子


作爲命名空間的函數

在函數中聲明的變量在整個函數題哪都是可見的(包括在其嵌套的函數內),在函數外部不可見。任何不在函數內聲明的變量都是全局變量,在整個JavaScript程序中都是可見的。所以經常定義一個函數來作爲臨時的命名空間,避免變量污染全局空間



閉包

JavaScript也採用詞法作用域,也就是說,函數的執行依賴於變量作用域買這個作用域是在函數定義是決定的,而不是在函數調用時決定的。


當一個函數嵌套了另外一個函數,外部函數將嵌套的函數對象作爲返回值返回的時候往往會導致調用函數時閉包鎖指向的作用域鏈和定義函數時的作用域鏈不一樣,這就是閉包

閉包可以捕捉到局部變量(和參數),並一直保存下來,這樣看起來就像這些變量綁定到了在其中定義它們的外部函數。


每次調用JavaScript函數的時候,都會爲止創建一個新的對象用來保存局部變量,把這個對象添加至作用域鏈中。當函數返回的時候,就從作用域鏈中將這個綁定變量的對象刪除。如果不存在嵌套的函數,也沒有其他引用指向這個綁定會被刪除。但如果函數定義了嵌套函數,並將它作爲返回值返回或存儲在某處的屬性裏,這是就會有一個外部引用指向這個嵌套的函數,他就不會被當成垃圾回收,並且他所知曉的變量綁定對象也不會被當作垃圾回收。


P185


不要將for循環移到定義閉包的函數內


關聯到閉包的作用域鏈都是活動的,嵌套函數不會將作用域內的私有成員複製一份,也不會對所綁定的變量生成靜態快照


每個函數調用都包含一個this值,如果閉包在外部函數裏時無法訪問this,除非外部函數將this轉存爲一個變量

綁定arguments的問題與之類似。arguments並不是一個關鍵字,但在調用每個函數都會自動升, ,由於閉包具有自己的arguments,所以閉包內無法直接訪問外部的arguments


函數屬性、方法和構造函數

每一個函數都包含不同的原型對象。當將函數用作構造函數的時候,新創建的對象會從原型對象上繼承屬性


在ES5點嚴格模式中,call()和apply()的第一個實參會變成this,哪怕傳入的是null或undefined,而在非嚴格模式下,null和undefined會變爲全局對象

傳入apply()的參數數組可以是類數組對象也可以是真實數組


Function()構造函數創建的函數並非使用詞法作用域。


發佈了92 篇原創文章 · 獲贊 30 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章