以前幾節中圍繞着函數梳理了 this、原型鏈、做用域鏈、閉包等內容,這一節梳理一下函數自己的一些特色。javascript
javascript 中函數是一等公民。 而且函數也是對象,由於它們能夠像任何其餘對象同樣具備屬性和方法。它們與其餘對象的區別在於函數能夠被調用。簡而言之,它們是 Function 對象。java
一個函數是能夠經過外部代碼調用的一個「子程序」,函數內部包含了執行語句或表達式。每一個自定義函數都是Function
的實例,並繼承Function
的全部屬性和方法,而Function
是語言自己提供的編程接口。編程
function foo (name) { console.log(`hello, ${name}`) } var res = foo(`小明`) // 'hello, 小明' console.log(res)
調用函數時,傳遞給函數的值被稱爲函數的實參(值傳遞),對應位置的函數參數名叫做形參。若是實參是一個包含原始值(數字,字符串,布爾值)的變量,則就算函數在內部改變了對應形參的值,返回後,該實參變量的值也不會改變。若是實參是一個對象引用,則對應形參會和該實參指向同一個對象。假如函數在內部改變了對應形參的值,返回後,實參指向的對象的值也會改.(歡迎查看參數傳值一節)設計模式
若是函數內部沒有經過return
返回一個值,則函數會默認返回undefined
.數組
函數在實際開發中承擔着代碼分塊、 功能封裝等任務。瀏覽器
// 函數申明 foo() let a = 1 function foo () { console.log(a) } // 函數表達式 (函數變量) let too = function (){ console.log('hello') } too() // Function 構造函數實例化一個函數 let bar = new Function('console.log("hello")') bar() // 箭頭函數 let fns = () => { console.log('hello') } fns()
須要注意的是函數申明
和函數表達式
在變量提高時的區別,強烈建議閱讀變量對象
相關內容: 詳情。同時函數申明的函數名稱
不能被改變,而函數表達式賦值給的變量能夠被從新賦值。
函數的各類定義方式就不深刻,這裏須要注意的是經過使用構造函數(new Function()
)方式建立的函數,在每次調用的時候都會解析一次。因此不推薦使用 Function 構造函數建立函數, 由於它須要的函數體做爲字符串可能會阻止一些 JS 引擎優化,也會引發瀏覽器資源回收等問題。閉包
arguments
同this
同樣是函數提供給函數內部使用的一個屬性(ES6中箭頭函數沒有)。經過arguments
咱們能夠獲取調用函數時傳遞進來的實參, 該屬性是一個類數組對象,咱們能夠像數組同樣進行數值的讀取。函數式編程
function foo () { let a = arguments[0] // 獲取函數的第一個參數 let b = arguments[1] // 獲取函數的第二個餐宿 // 遍歷函數的全部參數 for (let arg in arguments) { console.log(`${arg}: ${arguments[arg]}`) } arguments[2] = 1122 } foo(1, 2, 3)
雖然arguments
是相似於數組,可是除了經過索引獲取元素和length
屬性外,不能使用push
、pop
等方法。若是確實要進行修改操做,能夠將其轉換爲真正的數組:函數
let args = Array.prototype.slice.call(arguments) let args = [].slice.call(arguments) // ES2015 let args = Array.from(arguments)
把函數定義和函數執行結合到一塊兒就是當即執行函數,也叫自執行函數。優化
在官方術語中叫作 IIFE( 當即調用函數表達式),是在定義時就會當即執行的函數。被稱爲自執行匿名函數
的設計模式,主要包括兩部分:
()
裏的一個匿名函數,這個匿名函數擁有獨立的詞法做用域。這不只避免了外界訪問此 IIFE 中的變量,並且又不會污染全局做用域。借用這個特性咱們能夠對局部功能進行封裝,只暴露不多的方法給外部環境,這也是不少第三方庫的封裝方法之一。()
建立了一個當即執行函數表達式,JavaScript 引擎到此將直接執行函數。當函數變成當即執行的函數表達式
時,表達式中的變量不能從外部訪問:
(function () { var a = 1 })() console.log(a) // a is not defined
將 IIFE 分配給一個變量,不是存儲 IIFE 自己,而是存儲 IIFE 執行後返回的結果:
let bar = (function (){ let a = 123 return a })() console.log(bar) // 123
別外自執行函數還有一些其餘使用方式:
// 下面2個括弧()都會當即執行 (function(){ /* code */ }()); (function(){ /* code */ })(); // 因爲括弧()和JS的&&,異或,逗號等操做符是在函數表達式和函數聲明上消除歧義的 // 因此一旦解析器知道其中一個已是表達式了,其它的也都默認爲表達式了 var i = function(){ /* code */ }(); true && function(){ /* code */ }(); 0, function(){ /* code */ }(); // 若是你不在乎返回值,或者不怕難以閱讀 // 你甚至能夠在function前面加一元操做符號 !function(){ /* code */ }(); ~function(){ /* code */ }(); -function(){ /* code */ }(); +function(){ /* code */ }(); // 上面這種使用一元表達式這種方式實際上是不太常見的 // 並且有時候確定在一些場景下存在一些弊端,由於一元表達式會有一個不爲undefined的返回值 // 要想返回值爲undefined,那麼最保險的就是使用void關鍵字 void function(){/* code */}();
()
左邊必須是一個函數表達式,而不是一個函數申明。自執行函數也會用在模塊封裝的場景下。
函數和函數聲明時的詞法做用域造成閉包。咱們可使用閉包來保存變量、封裝不須要暴露的私有屬性和方法。
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } var foo = checkscope(); foo(); // "local scope"
完全理解閉包的最好途徑就是理解閉包的由來,閉包的產生和做用域鏈有密切關係。在做用域鏈一節已經梳理過閉包的原理。
未完成