2、揮舞函數

匿名函數

創建匿名函數的情況一般是將匿名函數保存在一個變量里,或者將其作為一個對象的方法,或者將其作為一個回調。

// 匿名函數直接作為鼠鍵處理程序
window.onload=function (){console.log(1)};

// 匿名函數作為某個對象的一個方法
var a={
    x: function () {
        console.log(1)
    }
};
a.x();

// 匿名函數作為回調函數傳遞給setTimeout()方法
setTimeout(function(){
    console.log(1)
},500);

遞歸

遞歸函數有兩個條件:引用自身,并且有終止條件。遞歸函數不能終止于無限循環。

普通命名函數中的遞歸
// 在函數內部通過調用函數自身,直到條件結束
function a(n){
   return n>1?a(n-1)+1:1; 
}
console.log(a.x(5)); // 5
作為方法中的遞歸

將一個遞歸函數作為一個對象的方法,遞歸函數會變為一個匿名函數賦值給對象的一個屬性。

// 通過引用對象的屬性名稱來調用該匿名函數進行遞歸
var a={
    x:function (n){
        return n>1? a.x(n-1)+1:1;
    }
};
console.log(a.x(5)); // 5

這樣的引用方法存在引用丟失的問題,例如:

// 定義一個新對象b,該對象也引用之前a對象上的匿名遞歸函數。
var b={
    x: a.x
};
//重新定義a對象,去除所有屬性,此時a上的x屬性已經沒有了
a={};
console.log(b.x(5)); // 報錯  在執行遞歸函數時,b中的x方法依然是使用a.x來調用自身,a.x已經不存在,所以報錯

在匿名函數中不再使用顯示的a引用,而是使用函數上下文(this)進行引用可以解決這一問題。

// 當函數作為方法被調用時,函數上下文指的是調用該方法的對象
var a={
    x:function (n){
        return n>1? this.x(n-1)+1:1;
    }
};
// 調用b.x()方法時,其中的this指向b
console.log(b.x(5)); // 5

還存在一個問題,這樣修改代碼后,b對象引用a.x的方法名稱也必須為x,因為遞歸函數是使用this.x調用,屬性名為x被固定死了。給匿名遞歸函數取一個名稱,再通過該名稱調用,可以解決這一問題,這樣的匿名遞歸函數被稱為內聯函數(一個具有名稱的匿名函數)。

// 給匿名函數命名,再通過該名稱進行遞歸調用
var a={
    x:function z(n){
        return n>1? z(n-1)+1:1;
    }
};
// b中原本的x屬性名可以修改為任意名稱,再對a中的x方法進行引用
var b={
    y: a.x
};
a={};
console.log(b.y(5)); // 5

內聯函數還可以在賦值給變量的時候使用

var a= function b(){
    console.log(b);  // 可以訪問到名稱b
};
a();
console.log(b); // 報錯,b在函數外部不可以訪問到,證明內聯函數聲明的名稱的作用域只在函數體內部可訪問

還有一種通過arguments的callee屬性調用函數自身的方法,不過callee在新版的Javascript被去除。現版本的嚴格模式下也禁用了這一屬性。arguments.callee引用的是當前所執行的函數本身。

var a={
    x:function (n){
        return n>1? arguments.callee(n-1)+1:1;
    }
};
var b={
    y: a.x
};
a={};
console.log(b.y(5)); // 5

可變長度的參數列表

使用apply()可以使函數的參數列表長度可變

// Javascript中查找最大或最小值時,參數需要一個一個傳入,無法直接傳入元素數量不定的數組。
var a= Math.max(1,2,3);
console.log(a); // 3

// 使用apply()調用函數,第二個參數作為數組傳入
var a=[1,2,3];
var b=Math.max.apply(Math,a);
console.log(b); // 3

即使只定義了固定數量的形式參數,仍然可以是用arguments參數列表訪問到所有的參數,因為argument參數列表總是指向所有參數的集合。函數本身也具有length屬性,與arguments屬性的length不同在于,函數本身的length表示函數在定義的時候定義了幾個形式參數,而arguments的length屬性表示實際調用函數時傳入了幾個參數。利用這兩個length的差別可以在Javascript中實現函數的重載。

// 定義一個方法,將一系列匿名函數綁定在一個對象的同名屬性下,根據傳入參數數量的不同運行不同的匿名函數
function add(obj, name, fn) {
    var old=obj[name];
    obj[name]=function () {
        if(fn.length===arguments.length){
            return fn.apply(this,arguments);
        }else if(typeof old==="function"){
            return old.apply(this,arguments);
        }
    }
}

var a={};
add(a,"b",function () {
    console.log(0);
});
add(a,"b",function (x) {
    console.log(x);
});
add(a,"b",function (x,y) {
    console.log(x+y);
});

// 傳入的參數數量不同,執行的匿名函數也不同
a.b();  // 0
a.b(1);  // 1
a.b(1,2);  // 3

函數判斷

兼容各個瀏覽器的函數判斷方法:

function isFunction(fn){
    return Object.prototype.toString.call(fn)==="[object Function]"
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容