1. 程式人生 > >Javascript高階程式設計學習筆記(16)—— 引用型別(5) Function型別

Javascript高階程式設計學習筆記(16)—— 引用型別(5) Function型別

JS中許多有趣的地方都和函式脫不了聯絡

那麼是什麼讓JS中的函式這麼有趣呢?

 我們一起來看看吧

 

Function型別

在JS中函式實際上就是物件,每個函式都是Function型別的例項,和JS的其他引用型別都擁有屬性和方法

正是由於這個原因,函式名實際上就是一個指標,指向函式物件,並不會與函式繫結

 

函式的宣告

建立一個函式一般來說有下面幾種方法

// function關鍵字宣告
function sayHello(){
    alert("hello");
}

// 函式表示式
var sayHello = function(){
    alert(
"hello"); } // 建構函式 var sayHello = new Function("alert('hello')");// 接收任意數量引數,最後一個引數為函式體

其中最少使用的就是最後一種,因為這種方式會導致JS解析兩次程式碼

而對於function 和 表示式這兩種方式來說,區別在於(在ES6以下)function宣告會在建立執行環境階段完成,而表示式會在程式碼執行階段建立函式;

簡單地說function宣告的函式會比,函式表示式更早的建立函式(在程式碼執行之前)

PS. 由於函式名僅僅只是指向函式的指標,所以一個函式可以有多個函式名

 

沒有過載

之前的文章中,我提到了JS的函式沒有過載

這一切的根源就是:JS中的函式名只是一個指標,而不是一個函式簽名

所以當我們在JS中宣告同名函式的時候,實際上就是讓已經指向一個函式的指標指向另外一個函式,自然也就不能實現過載了

 

變數提升

變數提升,以及閉包等概念,在許多公司前端面試的時候經常會考到

這裡撇開閉包不談,先來說一下變數提升

變數提升這一概念只存在於ES6以下的ES語法中

前面講JS執行環境的時候我詳細解釋了這一過程

 

什麼是變數提升?

說白了變數提升就是:當JS在執行到宣告變數的語句之前就可以訪問該變量了

對於函式之外的變數,變數提升雖然可以在宣告語句前訪問到該變數,但是由於賦值語句尚未執行,所以只能訪問到 undefined

在上方的例子中我們看到變數a就在宣告語句前被我們訪問了,而變數 b 我們沒有宣告所以js報錯

 

而對於函式來說,函式的變數提升,我們甚至可以在函式宣告前就訪問到函式的函式體

 

而變數提升的原理就是,JS的執行環境的建立,具體過程見下圖:

所以var宣告的變數在,語句執行前就已經被js引擎設定了值,而ES6的let,const在建立執行環境時並不會為這些變數設定值,所以也就不存在變數提升

而由於函式表示式是在程式碼執行階段才會執行,所以函式表示式建立的函式並不能在宣告語句前訪問

我也不知道我講清楚沒,沒懂的小夥伴可以留言,有錯的地方也歡迎大家留言指出

 

函式作為函式返回值

 在JS中由於函式本就是一個物件,自然也可以作為函式的返回值返回

這種情況適用於使用的函式需要根據引數的型別、值等呼叫不同的函式的情況

 

函式內部屬性

函式的內部有兩個特殊的物件:

1.this

2.arguments

其中 this 物件指向的是當前函式的執行環境物件

而arguments用於儲存函式的引數

arguments物件有個callee屬性,指向當前函式本身

通過arguments可以實現更加安全的遞迴呼叫

var test = function(num){
    if(num===1){
        return ; 
    }
    return num * arguments.callee(num-1);
}

這種方式可以避免當函式名改變時導致的遞迴報錯

然而為了安全性,在嚴格環境下無法使用

所以嚴格環境可以使用下面的程式碼來實現該功能

var test = function lhy(num){
    if(num===1){
        return ; 
    }
    return num * lhy(num-1);
}

 

除此而外函式例項上還有一個屬性 caller

用於儲存呼叫當前函式的函式的引用,全域性環境下該值為 null

 

函式屬性和方法

每個函式都包含以下兩個屬性:

1.length  表示函式希望獲得引數的個數

2.prototype 跟原型鏈相關,在此不過多介紹

除了上述方法之外,函式還包括3個非繼承而來的方法

1.applay()

2.call()

3.bind()

 

這三個方法的作用十分類似,都是用於改變函式的 this 指向

其中 applay 和 call 都是改變指向並執行,而 bind 只改變函式指向並不會立即執行(只接收一個引數即要繫結的物件)

而apply和call的第一個引數都是this指向的物件,區別在於:

call方法的的引數必須一一例舉

而apply只需要接收兩個引數,第二引數是引數陣列

所以使用時要視具體情況而定

 

JS中的函式與原型無關的地方就介紹完了,原型相關的內容在後面的文章介紹

明天再見~~