嚴格模式之caller callee..

arguments.callee 從 ES5 嚴格模式中被移除掉的一個主要原因是遞歸調(diào)用會獲取到一個不同的 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();

另外一個被廢棄的特性是 arguments.callee.caller,具體點說則是 Function.caller。為什么? 額,在任何一個時間點,你能在堆棧中找到任何函數(shù)的最深層的調(diào)用者,也正如我在上面提到的,在調(diào)用堆棧有一個單一重大影響:不可能做大量的優(yōu)化,或者有更多更多的困難。比如,如果你不能保證一個函數(shù) f 不會調(diào)用一個未知函數(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)用的時候都存在,那么它需要在行內(nèi)代碼插入檢查,或者不能內(nèi)聯(lián)這個函數(shù)。現(xiàn)在在這個特殊例子里一個智能的解釋器應該能重排檢查而更優(yōu),并檢查任何將不用到的值。然而在許多的情況里那是不可能的,也因此它不能夠內(nèi)聯(lián)。

安全問題:
可以利用這個屬性來得到當前調(diào)用棧的信息:

function getCallStack() {  
    var stack = [];  
    for (var f = getCallStack.caller; f; f = f.caller) {  
        stack.push(f);  
    }  
    return stack;  
}  

對于簡單的調(diào)用關系,上述確實能夠得到調(diào)用棧的信息:

function f1() {  
    return getCallStack();  
}  
function f2() {  
    return f1();  
}  
var trace = f2();  
trace; // [f1, f2]  

但是當一個函數(shù)在調(diào)用棧中出現(xiàn)不止一次時,就會發(fā)生問題了,比如下面的代碼會產(chǎn)生一個死循環(huán):

function f(n) {  
    return n === 0 ? getCallStack() : f(n - 1);  
}  
var trace = f(1); // infinite loop  

原因在于,當發(fā)生遞歸調(diào)用時,函數(shù)自身會被賦值給它的caller屬性。因此getCallStack中的for循環(huán)的終止條件f永遠不會為false:

for (var f = getCallStack.caller; f; f = f.caller) {  
    stack.push(f);  
}  

參考文獻:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/arguments/callee#為什么arguments.calleeES5嚴格模式中移除掉
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode
https://github.com/airbnb/javascript/issues/52

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容