Function.caller
返回調(diào)用指定函數(shù)的函數(shù)尤蛮。
如果一個(gè)函數(shù)f是在全局作用域內(nèi)被調(diào)用的,則f.caller為null,相反,如果一個(gè)函數(shù)是在另外一個(gè)函數(shù)作用域內(nèi)被調(diào)用的,則f.caller指向調(diào)用它的那個(gè)函數(shù)棺棵。
該屬性的常用形式arguments.callee.caller
替代了被廢棄的 arguments.caller
什么? 被廢棄了拌夏?
是的,廢棄的 arguments.caller
屬性原先用在函數(shù)執(zhí)行的時(shí)候調(diào)用自身。本屬性已被移除且不再有用。
arguments.caller
已經(jīng)不可使用了做鹰,但是你還可以使用 Function.caller
,但是該特性也是非標(biāo)準(zhǔn)的鼎姐,盡量不要在生產(chǎn)環(huán)境中使用它钾麸!
下例演示了arguments.caller屬性的作用
function whoCalled() {
if (arguments.caller == null)
console.log('該函數(shù)在全局作用域內(nèi)被調(diào)用.');
else
console.log(arguments.caller + '調(diào)用了我!');
}
下例用來得出一個(gè)函數(shù)是被誰(shuí)調(diào)用的
function myFunc() {
if (myFunc.caller == null) {
return ("該函數(shù)在全局作用域內(nèi)被調(diào)用!");
} else
return ("調(diào)用我的是函數(shù)是" + myFunc.caller);
}
arguments.callee
不用雞凍更振,這個(gè)東西也好不到哪兒去,
callee 是 arguments 對(duì)象的一個(gè)屬性饭尝。它可以用于引用該函數(shù)的函數(shù)體內(nèi)當(dāng)前正在執(zhí)行的函數(shù)肯腕。這在函數(shù)的名稱是未知時(shí)很有用,例如在沒有名稱的函數(shù)表達(dá)式 (也稱為“匿名函數(shù)”)內(nèi)钥平。
但是实撒,看這個(gè)警告:
在嚴(yán)格模式下,第5版 ECMAScript (ES5) 禁止使用 arguments.callee()涉瘾。
當(dāng)一個(gè)函數(shù)必須調(diào)用自身的時(shí)候, 避免使用 arguments.callee(), 通過要么給函數(shù)表達(dá)式一個(gè)名字,要么使用一個(gè)函數(shù)聲明知态。
為什么 arguments.callee 從ES5嚴(yán)格模式中刪除了?
早期版本的 JavaScript不允許使用命名函數(shù)表達(dá)式立叛,出于這樣的原因, 你不能創(chuàng)建一個(gè)遞歸函數(shù)表達(dá)式负敏。
例如,下邊這個(gè)語(yǔ)法就是行的通的:
function factorial (n) {
return !(n > 1) ? 1 : factorial(n - 1) * n;
}
[1,2,3,4,5].map(factorial);
但是:
[1,2,3,4,5].map(function (n) {
return !(n > 1) ? 1 : /* 這里寫什么? */ (n - 1) * n;
});
這個(gè)不行秘蛇。為了解決這個(gè)問題其做, arguments.callee 添加進(jìn)來了。然后你可以這么做
[1,2,3,4,5].map(function (n) {
return !(n > 1) ? 1 : arguments.callee(n - 1) * n;
});
然而赁还,這實(shí)際上是一個(gè)非常糟糕的解決方案妖泄,因?yàn)檫@ (以及其它的 arguments, callee, 和 caller 問題) 使得在通常的情況(你可以通過調(diào)試一些個(gè)別例子去實(shí)現(xiàn)它,但即使最好的代碼是最理想的艘策,你也沒必要去檢查調(diào)試它)不可能實(shí)現(xiàn)內(nèi)聯(lián)和尾遞歸浮庐。另外一個(gè)主要原因是遞歸調(diào)用會(huì)獲取到一個(gè)不同的 this 值,例如:
var global = this;
var sillyFunction = function (recursed) {
if (!recursed) { return arguments.callee(true); }
if (this !== global) {
alert("This is: " + this);
} else {
alert("This is the global");
}
}
sillyFunction();
ECMAScript 3(es3?哈哈) 通過允許命名函數(shù)表達(dá)式解決這些問題柬焕。例如:
[1,2,3,4,5].map(function factorial (n) {
return !(n > 1) ? 1 : factorial(n-1)*n;
});
這有很多好處:
- 該函數(shù)可以像代碼內(nèi)部的任何其他函數(shù)一樣被調(diào)用
- 它不會(huì)在外部作用域中創(chuàng)建一個(gè)變量 (除了 IE 8 及以下)
- 它具有比訪問arguments對(duì)象更好的性能
另外一個(gè)被廢棄的特性是 arguments.callee.caller审残,具體點(diǎn)說則是 Function.caller。為什么? 額斑举,在任何一個(gè)時(shí)間點(diǎn)搅轿,你能在堆棧中找到任何函數(shù)的最深層的調(diào)用者,也正如我在上面提到的富玷,在調(diào)用堆棧有一個(gè)單一重大影響:不可能做大量的優(yōu)化璧坟,或者有更多更多的困難。比如赎懦,如果你不能保證一個(gè)函數(shù) f 不會(huì)調(diào)用一個(gè)未知函數(shù)雀鹃,它就絕不可能是內(nèi)聯(lián)函數(shù) f±剑基本上這意味著內(nèi)聯(lián)代碼中積累了大量防衛(wèi)代碼:
function f (a, b, c, d, e) { return a ? b * c : d * e; }
如果 JavaScript 解釋器不能保證所有提供的參數(shù)數(shù)量在被調(diào)用的時(shí)候都存在黎茎,那么它需要在行內(nèi)代碼插入檢查,或者不能內(nèi)聯(lián)這個(gè)函數(shù)〉被冢現(xiàn)在在這個(gè)特殊例子里一個(gè)智能的解釋器應(yīng)該能重排檢查而更優(yōu)傅瞻,并檢查任何將不用到的值踢代。然而在許多的情況里那是不可能的,也因此它不能夠內(nèi)聯(lián)嗅骄。
番外:
在匿名遞歸函數(shù)中使用 arguments.callee
遞歸函數(shù)必須能夠引用它本身胳挎。很典型的,函數(shù)通過自己的名字調(diào)用自己溺森。然而慕爬,匿名函數(shù) (通過 函數(shù)表達(dá)式 或者 函數(shù)構(gòu)造器 創(chuàng)建) 沒有名稱。因此如果沒有可訪問的變量指向該函數(shù)屏积,唯一能引用它的方式就是通過 arguments.callee澡罚。
下面的例子定義了一個(gè)函數(shù),按流程肾请,定義并返回了一個(gè)階乘函數(shù)。該例并不是很實(shí)用更胖,并且?guī)缀醵寄軌蛴妹瘮?shù)表達(dá)式實(shí)現(xiàn)同樣結(jié)果的例子
function create() {
return function(n) {
if (n <= 1)
return 1;
return n * arguments.callee(n - 1);
};
}
var result = create()(5); // 返回120 (5 * 4 * 3 * 2 * 1)
沒有替代方案的 arguments.callee
下面的例子是沒有可以替代 arguments.callee的方案的铛铁,因此棄用它時(shí)會(huì)產(chǎn)生一個(gè)BUG (參看 bug 725398):
function createPerson (sIdentity) {
var oPerson = new Function("alert(arguments.callee.identity);");
oPerson.identity = sIdentity;
return oPerson;
}
var joe = createPerson("Joseph");
joe();
利用命名函數(shù)表達(dá)式也可以實(shí)現(xiàn)上述例子的同樣效果
function createPerson (identity) {
function Person() {
console.log(Person.identity);
}
Person.identity = identity;
return Person;
}
var joe = createPerson("Joseph");
joe(); //Joseph
Over.