DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> JavaScript基礎知識 >> Effective JavaScript 筆記 當心命名函數表達式笨拙的作用域
Effective JavaScript 筆記 當心命名函數表達式笨拙的作用域
編輯:JavaScript基礎知識     

js函數會根據上下文改變其含義。

function double(x){return x*2;}

這是一個函數聲明,也可以是一個命名函數表達式(named function expression),取決於它出現的地方。

聲明一個函數,並綁定一個當前作用域的變量。

同一段函數代碼也可以作為一個表達式。

var f=function double(x){return x*2;}

根據ECMAScript規范,該函數綁定到變量f,而不是變量double。這裡給函數表達式命名並不是必要的的,可以直接使用匿名的函數表達式。

var f=function(x){return x*2;}

匿名和命名函數表達式的官方區別在於後者會綁定到與其函數名相同的變量上,該變量將作為函數內的一個局部變量。

可以用來寫遞歸函數表達式

var f=function find(tree,key){

         if(!tree){

                return null;

          }

         if(tree.key === key){

               return tree.value;

       }

      return find(tree.left,key)||find(tree.right,value);

}

注意:變量find的作用域只在其自身函數中。不像函數聲明,命名函數表達式不能通過其內部的函數名在外部被引用。

find(myTree,”foo”);//error:find is not defined

使用命名函數表達式來進行遞歸似乎沒有必要,因為使用外部作用域的函數名也可以達到同樣的效果

var f=function(tree,key){

if(!tree){

return null;

}

if(tree.key === key){

return tree.value;

}

return f(tree.left,key)||f(tree.right,value);

}

function find(tree,key){

if(!tree){

return null;

}

if(tree.key === key){

return tree.value;

}

return find(tree.left,key)||find(tree.right,value);

}

var f=find;

命名函數的真正的用處是進行調試

大多數現代的JS環境都提供對Error對旬的棧跟蹤功能。在棧跟蹤中,函數表達式的名稱通常作為其入口作用。用於檢查棧的設備調試器對命名函數表達式有類似的使用。

命名函數表達式是作用域和兼容性問題的來源。ES規范的錯誤,在ES3中就已經存在,JS引擎被要求將命名函數表達式的作用域表示為一個對象,像有問題的with結構。該作用域對象只含有單個屬性,該屬性將函數名和函數自身綁定起來。該作用域對象也繼承了Object.prototype的屬性。這意味著僅僅是給函數表達式命名也會將Object.prototype中的所有屬性引用到作用域中。結果出問題如:

var constructor=function(){return null;};

var f=function f(){

     return constructor();

};

f();//{}(in ES3 environments)

ES5修正了這個問題。運行如下:

image

但有些JS環境仍然使用過時的對象作用域,有些環境更不符合標准,對匿名函數表達式使用對象作為作用域。

在系統中避免對象污染函數表達式作用域的最好方式是避免任何時候在Object.prototype中添加屬性,以及避免使用任何與標准Object.prototype屬性同名的局部變量

在流行的JS引擎中的另一個缺陷是對命名函數表達式的聲明進行提升。

var f=function g(){return 17;}

g();//17(在不標准的環境中)

注意這是不符合標准的行為。

有一些JS環境甚至把f和g這兩個函數作為不同的對象,從而導致不必要的內存分配。這種行為的一個合理的解決辦法是創建一個與函數表達式同名的局部變量並賦值為null。

var f=function g(){return 17;}

var g=null;

即使在沒有錯誤地提升函數表達式聲明的環境中,使用var重聲明變量能確保仍然會綁定變量g。設置變量g為null能確保重復的函數被垃圾回收。

合理的結論

  • 命名函數表達式由於會導致很多問題,所以並不值得使用。
  • 一個不太嚴肅的回應是開發階段使用命名函數表達式用作調試,在發布前通過構建工具去把所有函數表達式轉化為匿名的。
  • 要明確自己要發布的平台的JS環境。

提示

  • 在Error對象和調試器中使用命名函數表達式改進棧跟蹤
  • 在ES3和有問題的JS環境中,函數表達式作用域會被Object.prototype污染
  • 錯誤百出的JS環境中會提升命名函數表達式聲明,並導致命名函數表達式的重復存儲
  • 考慮避免使用命名函數表達式或在發布前刪除函數命名
  • 確保JS代碼到正確實現ES5環境中,就不用再擔心這些問題了

js函數會根據上下文改變其含義。

function double(x){return x*2;}

這是一個函數聲明,也可以是一個命名函數表達式(named function expression),取決於它出現的地方。

聲明一個函數,並綁定一個當前作用域的變量。

同一段函數代碼也可以作為一個表達式。

var f=function double(x){return x*2;}

根據ECMAScript規范,該函數綁定到變量f,而不是變量double。這裡給函數表達式命名並不是必要的的,可以直接使用匿名的函數表達式。

var f=function(x){return x*2;}

匿名和命名函數表達式的官方區別在於後者會綁定到與其函數名相同的變量上,該變量將作為函數內的一個局部變量。

可以用來寫遞歸函數表達式

var f=function find(tree,key){

         if(!tree){

                return null;

          }

         if(tree.key === key){

               return tree.value;

       }

      return find(tree.left,key)||find(tree.right,value);

}

注意:變量find的作用域只在其自身函數中。不像函數聲明,命名函數表達式不能通過其內部的函數名在外部被引用。

find(myTree,”foo”);//error:find is not defined

使用命名函數表達式來進行遞歸似乎沒有必要,因為使用外部作用域的函數名也可以達到同樣的效果

var f=function(tree,key){

if(!tree){

return null;

}

if(tree.key === key){

return tree.value;

}

return f(tree.left,key)||f(tree.right,value);

}

function find(tree,key){

if(!tree){

return null;

}

if(tree.key === key){

return tree.value;

}

return find(tree.left,key)||find(tree.right,value);

}

var f=find;

命名函數的真正的用處是進行調試

大多數現代的JS環境都提供對Error對旬的棧跟蹤功能。在棧跟蹤中,函數表達式的名稱通常作為其入口作用。用於檢查棧的設備調試器對命名函數表達式有類似的使用。

命名函數表達式是作用域和兼容性問題的來源。ES規范的錯誤,在ES3中就已經存在,JS引擎被要求將命名函數表達式的作用域表示為一個對象,像有問題的with結構。該作用域對象只含有單個屬性,該屬性將函數名和函數自身綁定起來。該作用域對象也繼承了Object.prototype的屬性。這意味著僅僅是給函數表達式命名也會將Object.prototype中的所有屬性引用到作用域中。結果出問題如:

var constructor=function(){return null;};

var f=function f(){

     return constructor();

};

f();//{}(in ES3 environments)

ES5修正了這個問題。運行如下:

image

但有些JS環境仍然使用過時的對象作用域,有些環境更不符合標准,對匿名函數表達式使用對象作為作用域。

在系統中避免對象污染函數表達式作用域的最好方式是避免任何時候在Object.prototype中添加屬性,以及避免使用任何與標准Object.prototype屬性同名的局部變量

在流行的JS引擎中的另一個缺陷是對命名函數表達式的聲明進行提升。

var f=function g(){return 17;}

g();//17(在不標准的環境中)

注意這是不符合標准的行為。

有一些JS環境甚至把f和g這兩個函數作為不同的對象,從而導致不必要的內存分配。這種行為的一個合理的解決辦法是創建一個與函數表達式同名的局部變量並賦值為null。

var f=function g(){return 17;}

var g=null;

即使在沒有錯誤地提升函數表達式聲明的環境中,使用var重聲明變量能確保仍然會綁定變量g。設置變量g為null能確保重復的函數被垃圾回收。

合理的結論

  • 命名函數表達式由於會導致很多問題,所以並不值得使用。
  • 一個不太嚴肅的回應是開發階段使用命名函數表達式用作調試,在發布前通過構建工具去把所有函數表達式轉化為匿名的。
  • 要明確自己要發布的平台的JS環境。

提示

  • 在Error對象和調試器中使用命名函數表達式改進棧跟蹤
  • 在ES3和有問題的JS環境中,函數表達式作用域會被Object.prototype污染
  • 錯誤百出的JS環境中會提升命名函數表達式聲明,並導致命名函數表達式的重復存儲
  • 考慮避免使用命名函數表達式或在發布前刪除函數命名
  • 確保JS代碼到正確實現ES5環境中,就不用再擔心這些問題了
XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved