JavaScript引用類型——「Function類型」的注意要點

Function 類型

每一個函數都是Function 類型的實例。函數名實際上就是一個指向函數對象的指針,不會與某個函數綁定。數組

函數聲明方式建立Function,語法以下:瀏覽器

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

函數表達式定義Function,用var 以下:app

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

第二種方法不要忘記在結尾加上分號函數

另外還可使用Function 構造函數。如:this

var sum = new function("x","y","return x + y");

很是不推薦使用這種形式。lua

如何證實函數名僅僅是指向函數的指針呢:prototype

var sum = function(x,y){
    return x + y;
};
console.log(sum(1,2)); //3

var anotherSum = sum;
console.log(anotherSum(1,1)); //2

sum = null;
console.log(anotherSum(10,10)); //20

沒有重載

同名的後面的函數將會覆蓋前面的函數。如:指針

function add(num){
    return num + 100;
}
function add(num){
    return num + 200;
}
console.log(add(100)); //300

用另一種寫法就能夠很清楚的看出緣由。如:code

var add = function(num){
    return num + 100;
}
add = function(num){
    return num + 200;
}
console.log(add(100)); //300

函數的聲明與函數表達式(以及函數聲明提高)

解析器會率先讀取函數聲明,並使其在執行任何代碼以前可用(可訪問);至於函數表達式,則必須等到解析器執行到它所在的代碼行,纔會真正被解釋執行。如:對象

console.log(sum(1,1)); //2
function sum(x,y){
    return x + y;
}

以上代碼可行,是由於在代碼開始值錢,解析器就已經經過一個名爲函數聲明提高(function declaration hoisting)的過程,讀取並將函數聲明添加到執行環境中去。即便聲明函數的代碼在調用它的代碼後面,JavaScript 引擎也能把函數聲明提高到頂部。

可是,若是把函數聲明改爲函數表達式,則會致使錯誤。如:

console.log(sum(1,1)); 
var sum = function(x,y){
    return x + y;
}; //TypeError: undefined is not a function (evaluating 'sum(1,1)')

失敗的緣由就在於函數位於初始化語句中,而不是一個函數聲明。

也可同時使用函數聲明和函數表達式,但在safari 瀏覽器中會出錯。

做爲值的函數

函數也能夠做爲值來使用,也就是能夠將一個函數做爲另外一個函數的結果返回。如

function callFunction(someFunction,someArgument){
    console.log(someFunction(someArgument));
}
function addNum(x){
    return x + 100;
}
callFunction(addNum,100); //200

要訪問函數的指針而不執行函數的話,必須去掉函數名後面的括號。

function compare(func,x,y){
    var num1 = func(x);
    var num2 = func(y);
    var array = [num1,num2];
    
    var result = array.sort(sortFunc);
    console.log(result.toString());
    document.write(result.join(" then "));
    
    
    function sortFunc(value1,value2){
        if (value1 > value2){
            return 1;
        }else if(value2 > value1){
            return -1;
        }else{
            return 0;
        }
    }
}

function add10(num){
        return num + 10;
    }

compare(add10,10000,999100); //10010 then 999110

函數內部屬性

這裏主要說一下callee屬性、this屬性以及caller屬性。

callee屬性的功能就是消除函數名的耦合問題。如:

function min(num){
    if(num == 1){
        return 1;
    }else if(num < 1){
        return 0;
    }else{
        return min(num - 1);
    }
}

var result = min(100);
document.write(result);

上面這個min()函數若是改了函數名,函數裏面也相應的須要修改函數名。但添加了callee屬性,就不須要修改。如:

function mins(num){
    if(num == 1){
        return 1;
    }else if(num < 1){
        return 0;
    }else{
        return arguments.callee(num - 1);
    }
}

var result = mins(100);
document.write(result);

着就解決了耦合現象所帶來的問題。

this這個特殊對象的做用與Java 和C# 中的this 相似。this引用的是函數據以執行的環境對象——或者this值。如:

window.color = "red";
var obj = {color: "yellow"};

function show(){
    document.write(this.color);
}

show(); //red

obj.show = show;
obj.show(); //yellow

caller用來調用當前函數的函數的引用(源代碼)。如:

function sum(x,y){
    plus(x,y);
}

function plus(x,y){
    console.log(x + y);
    document.write("<pre>" + plus.caller + "</pre>");
}

sum(10,20);

如:

(
    function a(){
        document.write("function a");
        b();
        function b(){
            document.write("function b");
            alert(arguments.callee.caller);
        }
    }
)()

用arguments.callee 可能在某些瀏覽器中會致使錯誤:

function sum(x,y){
    plus(x,y);
}

function plus(x,y){
    console.log(x + y);
    document.write("<pre>" + arguments.callee.caller + "</pre>");
}

sum(10,20);

函數屬性和方法

每一個函數都包含兩個屬性lengthprototype

length屬性中,將返回參數的個數。如:

function colors(color1,color2){
    var array = [color1,color2];
    document.write(array.join("/"))
}
colors("red","blue");
document.write(" " + colors.length + "個參數"); //red/blue 2個參數

又如:

function toStr(){
        var result = "";
        for (var i = 0; i < arguments.length; i ++){
            result += arguments[i].toString() + " ";
        }
        document.write(result);
        document.write(" " + arguments.length + "個參數" + "<br/>");
        document.write(" " + arguments.callee.length + "個函數指望參數");
}
toStr(321,32,43243,432,3213);
    
/*
321 32 43243 432 3213 5個參數
0個函數指望參數    
*/

prototype屬性。這個屬性是保存它們全部實例方法的真正所在。在建立自定義引用類型以及實現繼承時,這個屬性是很是重要的(之後再單獨討論)。另外,在ECMAScript 5 中,這個屬性不可枚舉(不能用for-in)。主要的兩個非繼承方法有apply()call();這兩個方法的用途都是在特定的做用域中調用函數。

apply()方法接收兩個參數:一個是在其中運行函數的做用域,另外一個是參數數組。第二個參數能夠是Array 的實例,也能夠是arguments 對象。如:

function sum(x,y){
    document.write(x.toString() + y.toString());
}

function plus(){
    return sum.apply(this,arguments);
}

plus(321,"fff");

下面這個則是Array 的實例:

function sum(x,y){
    document.write(x.toString() + y.toString());
}

function plus(a,b){
    return sum.apply(this,[a,b]);
}

plus(321,"fff");

call()方法與前者的做用相同,區別在於接收參數的方式不一樣。第二個參數必須一個個列舉出來。以下:

function sum(x,y){
    document.write(x.toString() + y.toString());
}

function plus(a,b){
    return sum.call(this,a,b);
}

plus(321,"fff");

上面例子中plus(a,b) 不能寫成plus();call(this,a,b)不能寫成call(this,arguments)call(this,[a,b])

高能!!!

高能!!!

事實上,apply()call()真正有價值的用途是擴充函數的做用域。如:

var child = {
    name: "Oliver",
    age: 18
}
var adult = {
    name: "Troy",
    age: 24
}

function showName(){
    document.write(this.name);
}

showName.call(adult); //Troy
showName.call(child); //Oliver

使用上面這兩個方法來擴充做用域的最大好處就是對象不須要與方法又任何耦合關係。

ECMAScript 5 還定義了一個方法:bind()。this 的值會被綁定到傳給bind()函數的值。如:

var child = {
    name: "Oliver",
    age: 18
}
var adult = {
    name: "Troy",
    age: 24
}

function showName(){
    document.write(this.name);
}

var newFunc = showName.bind(child);
newFunc(); //Oliver
相關文章
相關標籤/搜索