函數及作用域
函數作用域
函數有個隱藏的屬性[[scope]],這個屬性就是作用域,其存儲了運行期上下文的集合。
[[scope]]存儲運行期上下文集合,這些集合呈鏈式關係,就是作用域鏈。
[[scope]]屬性結構
第一個元素 >>> 當前運行函數的 ActivationObject
第二個元素 >>> 當前運行函數父函數的 ActivationObject
第三個元素 >>> 當前運行函數父函數的父函數的 ActivationObject
…………
最後元素 >>> GlobalObject
<script type = "text/javascript">
function a(){
function b(){
function c(){
function d(){
}
d();
}
c();
}
b();
}
function e(){
}
// a 和 e 函數定義時它們的 [[scope]] 屬性中只有一個元素,就是 GlobalObject(全局運行時上下文)
//當要執行 a 函數時,a 函數就創建自己的 ActivatonObject(運行時上下文),存儲在屬性[[scope]]中,並放置到頂端(第1個元素)
//這時 a 函數的[[scope]]屬性的內容是
//第一個元素 ActivationObject(a的運行時上下文)
//第二個元素 GlobalObject(全局的運行時上下文)
a();
//當執行 a 函數時,b 函數運行前,b函數屬性[[scope]]引用了函數 a 的[[scope]]屬性,並且b函數創建自己的 ActivationObject。
//這時 b 函數的[[scope]]屬性的內容是
//第一個元素 ActivationObject(b的運行時上下文)
//第二個元素 ActivationObject(a的運行時上下文)
//第三個元素 GlobalObject(全局的運行時上下文)
//當 b 函數執行時,查找變量的順序是[[scope]]屬性,從上往下查找。直到找到第一個符合的停止。
//當 b 函數執行完後,銷燬自己創建ActivationObject
//這時 b 函數的[[scope]]屬性的內容變爲
//第一個元素 ActivationObject(a的運行時上下文)
//第二個元素 GlobalObject(全局的運行時上下文)
//當執行 b 函數時,c 函數運行前,c函數屬性[[scope]]引用函數 b 的[[scope]]屬性,並且c函數創建自己的 ActivationObject。
//以此類推,直到所有函數執行完畢。
</script>
閉包:
內部函數被返回到外部時,內部函數本身存着父函數的ActivationObject,即使父函數執行完,取消了對 ActivationObject引用,但內部函數依然可以存取父函數變量。這樣就產生閉包。閉包會導致原有作用域鏈不釋放,造成內存泄漏。
閉包的作用:實現公有變量(累加器)、緩存(存儲結構)、實現封裝,屬性私有化、模塊化開發,防止污染全局變量。
<script type = "text/javascript">
function a(){
var num = 0;
function b(){
num ++;
document.write("num = ",num,"<br/>")
}
return b;
}
var demo = a();
demo();//1
demo();//2
demo();//3
</script>
閉包應用:緩存
<script type = "text/javascript">
function eater(){
var food = [];
var obj = {
eat:function(){
if(food.length < 1){
document.write("There is no food!<br/>");
}
else{
document.write("i am eating " + food[food.length - 1],"<br/>");
food.pop();
}
},
push:function(myFood){
food.push(myFood);
}
}
return obj;
}
var eater1 = eater();
eater1.push("apple");
eater1.push("banana");
eater1.eat();
eater1.push("orange");
eater1.eat();
eater1.push("cake");
eater1.eat();
eater1.eat();
eater1.push("banana");
eater1.eat();
eater1.eat();
</script>
立即執行函數(初始化功能的函數)
應用在只執行一次的函數。只有函數表達式才能被執行符號()執行,函數聲明不能被執行。
<script type = "text/javascript">
(function(){document.write("ohohoh!!","<br/>")}());//W3C推薦立即執行函數格式
(function(){document.write("ohohoh!!","<br/>")})();
var x =(function(a,b,c){
return a + b + c;
}(1,2,3))
document.write(x,"<br/>");
var y = function demo(){
document.write("hahaha!!!","<br/>")
}();
//function error(){document.write("ohohoh!!","<br/>");}();函數聲明不能被執行
+function error(){document.write("ohohoh!!","<br/>");}();
</script>
立即執行函數與閉包應用
<script type = "text/javascript">
//立即執行函數,閉包保存循環變量 ii 的使用方式。
function test(){
var arr = [];
for (var i = 0; i < 10; i++) {
(function(ii){
arr[ii] = function(){
document.write("i = " + i + " ii = " + ii + "<br/>")
}
}(i))
}
return arr;
}
var myArr = test();
for(var j = 0;j < myArr.length;j++){
myArr[j]();
}
</script>
" ii = " + ii + "<br/>")
}
}(i))
}
return arr;
}
var myArr = test();
for(var j = 0;j < myArr.length;j++){
myArr[j]();
}
</script>