1. 程式人生 > >js中的apply,call,arguments,callee,caller詳解

js中的apply,call,arguments,callee,caller詳解

參數 學生 由於 傳遞 函數返回值 最小 n) javascrip 形參和實參

apply與call

主要解決一下幾個問題:

1.apply和call的區別在哪裏

2.什麽情況下用apply,什麽情況下用call

3.apply的其他巧妙用法(一般在什麽情況下可以使用apply)

apply:方法能劫持另外一個對象的方法,繼承另外一個對象的屬性.

call傳的參數是一個一個的加進去的,而apply的參數是一個數組或者類數組(arguments)

1.apply示例:

<script type="text/javascript">  
    /*定義一個人類*/  
    function Person(name,age)  
    {  
        
this.name=name; this.age=age; } /*定義一個學生類*/ functionStudent(name,age,grade) { Person.apply(this,arguments); this.grade=grade; } //創建一個學生類 var student=new Student("zhangsan",21,"一年級"); //測試 alert("name:"+student.name+"\n"+"age:"+student.age+"\n"+"grade:"+student.grade);
//大家可以看到測試結果name:zhangsan age:21 grade:一年級 //學生類裏面我沒有給name和age屬性賦值啊,為什麽又存在這兩個屬性的值呢,這個就是apply的神奇之處. </script>

分析: Person.apply(this,arguments);

this:在創建對象在這個時候代表的是student

arguments:是一個類數組,也就是[“zhangsan”,”21”,”一年級”];

也就是通俗一點講就是:用student去執行Person這個類裏面的內容,在Person這個類裏面存在this.name等之類的語句,這樣就將屬性創建到了student對象裏面

2.call示例

在Studen函數裏面可以將apply中修改成如下:

Person.call(this,name,age);

這樣就ok了

3.什麽情況下用apply,什麽情況下用call

在給對象參數的情況下,如果參數的形式是數組的時候,比如apply示例裏面傳遞了參數arguments,這個參數是數組類型,並且在調用 Person的時候參數的列表是對應一致的(也就是Person和Student的參數列表前兩位是一致的) 就可以采用 apply , 如果我的Person的參數列表是這樣的(age,name),而Student的參數列表是(name,age,grade),這樣就可以用call來 實現了,也就是直接指定參數列表對應值的位置(Person.call(this,age,name,grade));

4.apply的一些其他巧妙用法

細心的人可能已經察覺到,在我調用apply方法的時候,第一個參數是對象(this), 第二個參數是一個數組集合, 在調用Person的時候,他需要的不是一個數組,但是為什麽他給我一個數組我仍然可以將數組解析為一個一個的參數,這 個就是apply的一個巧妙的用處,可以將一個數組默認的轉換為一個參數列表([param1,param2,param3] 轉換為 param1,param2,param3) 這個如果讓我們用程序來實現將數組的每一個項,來裝換為參數的列表,可能都得費一會功夫,借助apply的這點特性,所以就有了以下高效率的方法:

a)Math.max 可以實現得到數組中最大的一項

因為Math.max 參數裏面不支持Math.max([param1,param2]) 也就是數組

但是它支持Math.max(param1,param2,param3…),所以可以根據剛才apply的那個特點來解決 var max=Math.max.apply(null,array),這樣輕易的可以得到一個數組中最大的一項(apply會將一個數組裝換為一個參數接一個參數的傳遞給方法)

這塊在調用的時候第一個參數給了一個null,這個是因為沒有對象去調用這個方法,我只需要用這個方法幫我運算,得到返回的結果就行,.所以直接傳遞了一個null過去

b)Math.min 可以實現得到數組中最小的一項

同樣和 max是一個思想 var min=Math.min.apply(null,array);

c)Array.prototype.push 可以實現兩個數組合並

同樣push方法沒有提供push一個數組,但是它提供了push(param1,param,…paramN) 所以同樣也可以通過apply來裝換一下這個數組,即:

vararr1=new Array("1","2","3");    
vararr2=new Array("4","5","6");   
Array.prototype.push.apply(arr1,arr2);  

也可以這樣理解,arr1調用了push方法,參數是通過apply將數組裝換為參數列表的集合.

通常在什麽情況下,可以使用apply類似Math.min等之類的特殊用法:

一般在目標函數只需要n個參數列表,而不接收一個數組的形式([param1[,param2[,…[,paramN]]]]),可以通過apply的方式巧妙地解決這個問題!

arguments

1、在JavaScript中,arguments對象是比較特別的一個對象,實際上是 當前函數的一個內置屬性。arguments非常類似Array,但實際上又不是一個Array實例。可以通過如下代碼得以證實(當然,實際上,在函數 funcArg中,調用arguments是不必要寫成funcArg.arguments,直接寫arguments即可)。

Array.prototype.testArg = "test";
function funcArg() { 
    alert(funcArg.arguments.testArg);
    alert(funcArg.arguments[0]); 
}

alert(new Array().testArg); // result: "test" 8 
funcArg(10); // result: "undefined" "10"

2、arguments對象的長度是由實參個數而不是形參個數決定的。形參是函數內部重新開辟內存空間存儲的變量,但是其與arguments對象 內存空間並不重疊。對於arguments和值都存在的情況下,兩者值是同步的,但是針對其中一個無值的情況下,對於此無值的情形值不會得以同步。如下代 碼可以得以驗證。

function f(a, b, c){
    alert(arguments.length); // result: "2" 
    a = 100;
    alert(arguments[0]); // result: "100"
    arguments[0] = "qqyumidi";
    alert(a); // result: "qqyumidi"
    alert(c); // result: "undefined"
    c = 2012;
    alert(arguments[2]); // result: "undefined"
}

f(1, 2);

3、由JavaScript中函數的聲明和調用特性,可以看出JavaScript中函數是不能重載的。

根據其他語言中重載的依據:"函數返回值不同或形參個數不同",我們可以得出上述結論:

第一:Javascript函數的聲明是沒有返回值類型這一說法的;

第二:JavaScript中形參的個數嚴格意義上來講只是為了方便在函數中的變量操作,實際上實參已經存儲在arguments對象中了。

另外,從JavaScript函數本身深入理解為什麽JavaScript中函數是不能重載的:在JavaScript中,函數其實也是對象,函數 名是關於函數的引用,或者說函數名本身就是變量。對於如下所示的函數聲明與函數表達式,其實含以上是一樣的(在不考慮函數聲明與函數表達式區別的前提 下),非常有利於我們理解JavaScript中函數是不能重載的這一特性。

function f(a){
    return a + 10;
}

function f(a){
    return a - 10;
}

// 在不考慮函數聲明與函數表達式區別的前提下,其等價於如下

var f = function(a){
    return a + 10;
}

var f = function(a){
    return a - 10;
}

4、arguments對象中有一個非常有用的屬性:callee。arguments.callee返回此arguments對象所在的當前函數引用。在使用函數遞歸調用時推薦使用arguments.callee代替函數名本身。

如下:

function count(a){
    if(a==1){
       return 1;
    }
    return a + arguments.callee(--a);
}

var mm = count(10);
alert(mm);

callee與caller

caller :

functionName.caller 返回調用者。

看看下面的函數,大家可以復制到VS中執行下

復制代碼 代碼如下:

function caller() {
    if (caller.caller) {
        alert(caller.caller.toString());
    } else {
        alert("函數直接執行");
    }
}

function handleCaller() {
    caller();
}

handleCaller();
caller();

大家會發現第一個alert會彈出調用caller函數的調用者handleCaller,而第二個alert由於沒有在其他函數體內調用,所以caller為null,就執行了 alert("函數直接執行");

callee:

返回正被執行的 Function 對象,也就是所指定的 Function 對象的正文.

callee是arguments 的一個屬性成員,它表示對函數對象本身的引用,這有利於匿名

函數的遞歸或者保證函數的封裝性。 下面一段代碼先說明callee的用法,實例代碼摘自網上

復制代碼 代碼如下:

function calleeLengthDemo(arg1, arg2) {
    alert(arguments.callee.toString());

    if (arguments.length == arguments.callee.length) {
        window.alert("驗證形參和實參長度正確!");
        return;
    } else {
        alert("實參長度:" + arguments.length);
        alert("形參長度: " + arguments.callee.length);
    }
}

calleeLengthDemo(1);

第一個消息框彈出calleeLengthDemo函數本身,這說明callee就是函數本身對象的引用。callee還有個非常有用的應用就是用來判斷實際參數跟行參是否一致。上面的代碼第一個消息框會彈出實際參數的長度為1,形式參數也就是函數本身的參數長度為2.

應用場景:

callee的應用場景一般用於匿名函數

大家看下下面一段代碼 摘自網絡

復制代碼 代碼如下:

var fn=function(n){
    if(n>0) return n+fn(n-1);
    return 0;
}

alert(fn(10))

函數內部包含了對自身的引用,函數名僅僅是一個變量名,在函數內部調用即相當於調用

一個全局變量,不能很好的體現出是調用自身,這時使用callee會是一個比較好的方法

復制代碼 代碼如下:

var fn=(function(n){
    if(n>0) return n+arguments.callee(n-1);
    return 0;
})(10);

alert(fn)

這樣就讓代碼更加簡練。又防止了全局變量的汙染。

caller的應用場景 主要用於察看函數本身被哪個函數調用。

js中的apply,call,arguments,callee,caller詳解