JavaScript的arguments、caller和callee

來源:互聯網
上載者:User

轉載keakon 原文 http://www.keakon.net/2009/04/30/JavaScript%E7%9A%84arguments%E3%80%81caller%E5%92%8Ccallee

寫的不錯,收藏了。

首先是arguments。

它是在函數調用時,自動在該函數內部產生的一個名為arguments的隱藏對象。該對象類似數組,可以用[]操作符擷取函數調用的時傳遞的實參:

function write(str) {document.write((str == undefined ? '' : str) + '<br />');}function testArg() {write('實參個數:' + arguments.length);for (var i = 0; i < arguments.length; ++i) {write(arguments[i]);}write();}testArg(123);testArg('hi');testArg('keakon', 'loli');

結果:

實參個數:1
123

實參個數:1
hi

實參個數:2
keakon
loli

很顯然,我們可以用這個對象來實現可變參數與預設參數。

此外還能指定arguments所屬的函數:

function write(str) {document.write((str == undefined ? '' : str) + '<br />');}function aCallee() {write('aCallee的實參個數:' + arguments.length);write('aCaller的實參個數:' + aCaller.arguments.length);}function aCaller() {aCallee(789);}aCaller(123, 456);

結果:

aCallee的實參個數:1
aCaller的實參個數:2

也就是說,如果arguments前未跟函數名,則取當前函數的arguments,否則取指定的函數。

此外,雖然arguments的行為像數組,但從技術上而言,它並不是一個數組對象:

(function () {alert(arguments instanceof Array); // falsealert(typeof(arguments)); // object})();

另外,只有函數被調用時,arguments對象才會建立,未調用時其值為null:

alert((function() {}).arguments);//或者alert(new Function().arguments);

接著來看caller。
在一個函數調用另一個函數時,被調用函數會自動產生一個caller屬性,指向調用它的函數對象。如果該函數當前未被調用,或並非被其他函數調用,則caller為null。

function write(anObject) {document.write('<pre>' + (anObject == null ? 'null' : anObject.toString()) + '</pre>');}function testCaller() {var caller = testCaller.caller;write(caller);}function aCaller() {testCaller();}testCaller();aCaller();

結果:

null
function aCaller() {
    testCaller();
}

實際上function對象是可以直接write的,我調用toString()方法是為了表示擷取的不是字串,而是函數本身。不過由於JavaScript的特殊性,我們可以直接輸出函數代碼,不需要什麼反組譯碼的過程。

最後來看callee。
當函數被調用時,它的arguments.callee對象就會指向自身,也就是一個對自己的引用。
由於arguments在函數被調用時才有效,因此arguments.callee在函數未調用時是不存在的(即null.callee),且解引用它會產生異常。
此外,arguments.callee.length可以擷取形參的個數。不過與arguments不同的是,arguments.callee無法獲得形參的值(你應該擷取實參的值)。
而且由於arguments可以指定函數名,所以arguments.callee自然也可以:

function write(str) {document.write((str == undefined ? '' : str) + '<br />');}function aCallee(arg) {write('aCallee的形參個數:' + arguments.callee.length);write('aCaller的形參個數:' + aCaller.arguments.callee.length); // 等效於arguments.callee.caller.arguments.callee.length或arguments.callee.caller.length}function aCaller(arg1, arg2) {aCallee();}aCaller();

結果:

aCallee的形參個數:1
aCaller的形參個數:2

或許有人會問callee有什麼用,這就不得不提this對象了。

由於JavaScript的函數實際上是個對象,在對象內部使用this理論上是會指向對象自身的。但JavaScript的this並非如此,在不同的上下文中,它指向的對象是不同的。
這裡有篇《[圖解] 你不知道的 JavaScript - “this”》很好地闡述了this對象。

簡單來說,函數有個範圍(即它在哪個對象裡建立的),一般來說,該範圍或對象即為this對象。(可以用apply和call方法更改範圍;此外,用new操作符建立對象時,this是指向這個對象自身的。)
當我們在全域範圍(window對象裡)定義函數,並調用它時,它的this對象即為window對象。
如果為對象obj定義了一個f方法,然後用obj.f()來調用,那麼f方法裡的this對象即為obj。
也就是說,想在f方法裡拿到f方法自身的引用,你需要用obj.f或this.f。但這並非一個通用的辦法,假若是g方法的話,你就得改成obj.g或this.g。而如果是匿名函數的話,甚至無法用這種方式擷取。
所以callee的出現就解除了方法與方法名的耦合,使得擷取方法自身變得更為通用。同時,它也為遞迴調用提供了更好的可讀性,並讓引用匿名函數對象成為了可能:

function fibonacci(num) {if (num < 3) {return 1;} else {return arguments.callee(num - 1) + arguments.callee(num - 2); // 如果用fibonacci(num - 1) + fibonacci(num - 2),在更換函數名後,對fibonacci的調用就會出錯}}var f = fibonacci;fibonacci = null;alert(f(10));alert((function (num){if (num < 3) {return 1;} else {return arguments.callee(num - 1) + arguments.callee(num - 2);}})(10));

可以看到,由於使用了arguments.callee,我們可以更清楚地知道這是一個調用自身的遞迴調用。
甚至可以將這個函數對象傳遞給其他函數,即使該函數對象的函數名已經不再引用原函數了,仍可以用新函數名來實現對自身的調用。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.