1. 程式人生 > 其它 >JavaScript函式(二)

JavaScript函式(二)

在前面我們已經對函式作了簡單的介紹,比如函式的定義、函式的宣告、函式的呼叫和函式的傳參等。本節將進一步介紹函式的應用,深度理解函式的各種使用。

函式是一個物件,每個函式時Function型別的一個例項,與其他引用型別一樣有屬性和方法。由於函式時物件,因此函式名實際上也是一個指向函式物件的指標,不會與某個函式繫結。函式通常是函式宣告語法定義的。

function sum(x,y){
                    return x+y;
                }

上面使用函式宣告語法定義的。

var sum=function(a,b){
                    return a+b;
                };

上面使用函式表示式聲明瞭一個函式。定義了變數sum,並將其初始化為一個函式。在使用函式表示式定義函式時,沒有必要使用函式名。通過變數sum可以引用函式,另外函式的末尾有個分號,與普通的變數定義一樣。

另外可以使用Function建構函式來定義函式。Function建構函式可以定義任何數量的引數,最後一個函式被看成是函式體。

var sum= new Function("a","b","return a+b");
function sum(a,b){
                    return a+b;
                }
                var anotherSum=sum;
                console.log(sum(1,2));
                sum=null;
                console.log(anotherSum(2,3));

上面定義了函式sum,用於求兩個數之和。定義了變數anotherSum,並將sum賦值給anthorSum。這樣anotherSum與sum指向了同一個指標。最後呼叫的時候,即使將sum=null,依然可以呼叫anotherSum。

var sum=function(a,b){
                    return a+b;
                }
                sum=function(a,b){
                    return a*b;
                }
                console.log(sum(2,4));//輸出8

上面的程式碼可以清楚地看出函式時沒有過載的。後定義的sum將前面的sum覆蓋。

JavaScript解析器在執行環境時,對於函式宣告和函式表示式定義的函式並不是一視同仁的。解析器會先呼叫函式的宣告,在執行其他程式碼之前可用。函式表示式,只有當函式執行到當前行,才會被解釋執行。

console.log(sum(2,3));//輸出5
                function sum(a,b){
                    return a+b;
                }
                console.log(subtract(2,3));//報錯,subtract is not function
                var subtract=function(a,b){
                    return a*b;
                }

上面的程式碼可以看出區別。函式的宣告在呼叫前已經新增到執行環境中,會將函式宣告新增到頂部環境中。而通過函式表示式定義的函式,在宣告變數前,就呼叫會發生錯誤。經過上面的比較,不管是函式宣告,還是函式表示式,我們都要養成先定義,再使用的習慣。

JavaScript函式本身就是變數,所以函式可以作為值來使用。函式不僅可以作為引數來傳遞,而且可以將函式作為另一個函式的結果返回。

function callFunction(func,num){
					return func(num);
				}
				function sum(num){
					return num+10;
				}
				console.log(callFunction(sum,10));

  上面的程式碼就是將函式作為另一個函式的結果返回,輸出20.

函式內部包含一個子函式,並且返回子函式,這就是所謂的函式閉包。閉包的好處就是可以快取資料。

function closeTest(){
                    var n=1;
                    Add=function(){
                        n=n+1;
                    }
                    return function F(){
                        return n+1;
                    }
                }
                var result = closeTest();
                console.log(result());
                Add();
                console.log(result());

上面的程式碼就是一個閉包函式。在閉包函式內部可以使用函式的內部變數。第一個呼叫result,返回2.第二次呼叫result輸出3,因為前面已經呼叫了add方法,將n+1,所以最好輸出的結果是3.

var name = "The Window";
              var object = {
                name : "My Object",
                getNameFunc : function(){
                  return function(){
                    return this.name;
                  };
                }
              };
            var  result=object.getNameFunc();
              console.log(result());//undefined,this的指向是window

通過上面的程式碼可以看清楚,呼叫result方法的時候,this指向了window,this.name為undefined

var name = "The Window";
              var object = {
                name : "My Object",
                getNameFunc : function(){
                  return function(){
                    return this.name;
                  }.bind(this);
                }
              };
            var  result=object.getNameFunc();
              console.log(result());//My Object

這個程式碼我們加了bind方法,bind函式用於改變this的指向。this.name指向的是object物件,所以result有輸出值。bind函式並不會立即執行,當呼叫該函式的時候才會執行,同時bind函式可以給函式繫結引數。

var substract=function(a,b){
                return a*b;
            };
                var sum=function(a,b){
                    var that=this;
                    console.log(that);
                    return a+b;
                }.bind(substract,3,4);
                console.log(sum(1,2));//輸出7

上面的程式碼使用bind函式改變了sum函式的this指向,this指向了substract函式,同時為sum添加了兩個引數3,4.雖然我在呼叫的時候又傳遞了引數1,2,但是我們bind的引數值是在前面的,所有輸出7.arguments[2]=1,arguments[3]=2。傳遞的引數通過arguments依然可以訪問到。

函式還有兩個常用的方法apply和call。apply和call兩個方法的效果是改變函式的指向,並能傳遞引數,但是立即執行。apply第一個引數是物件,用於改變函式的this指向;第二個引數是陣列,是傳遞給函式的值。call的第一個引數與apply一樣,但是call的第二個引數不是陣列,與bind類似,引數是一個一個傳遞的。

function sayHello(str){
                    console.log(str);
                }
                sayHello.bind(this,"bind");
                sayHello.call(this,"call");
                sayHello.apply(this,["apply"]);

上面的程式碼,我們比較了函式的bind、call和apply方法的不同。最後我們看輸出的值為 call apply。但是bind並沒有輸出,所以說bind並不能讓函式立即呼叫。函式呼叫bind方法之後,函式依然是一個函式。

sayHello.bind(this,"bind")();這個程式碼會輸出“bind”

apply方法和call方法還可以在函式內部進行呼叫,並能改變this的指向。

function Super(){
                    this.a="10";
                }
                function Sub(){
                    Super.call(this);
                    this.b="20";
                }
                var sub = new Sub();
                console.log(sub.a);
                console.log(sub.b);

上面的程式碼有點面向物件繼承的意思。通過在sub內部呼叫super的call方法,讓sub也擁有了super的屬性。最後的輸出結果是 10 20。

函式還擁有一些屬性可以使用。如果讓我們寫一個階乘函式,大多數人都會這樣寫:

function factorial(n){
                    if(n<1){
                        return 1;
                    }
                    else{
                        return n*factorial(n-1);
                    }
                }

階乘演算法會用到函式的遞迴,上面的方法將函式名在函式內部再次呼叫,增加了函式的耦合。我們可以使用函式的屬性callee來解耦。callee是函式的屬性,它是一個指標,指向擁有這個函式名的函式。

上面的程式碼我們可以使用arguments.callee替換函式。

function factorial(n){
                    if(n<1){
                        return 1;
                    }
                    else{
                        return n*arguments.callee(n-1);
                    }
                }
                console.log(factorial(10));//3628800

 閉包可以減少全域性變數的使用。

(function() {
                
                    window.oEvent=window.oEvent||{};
                    var addEvent = function() { /*程式碼的實現省略了*/ };

                    function removeEvent() {}

                    oEvent.addEvent = addEvent;
                    oEvent.removeEvent = removeEvent;
                })(window);
                console.log(oEvent.addEvent());

上面的程式碼只定義了一個全域性變數