中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JavaScript中變量的作用域和閉包的應用

發布時間:2020-06-24 11:43:41 來源:億速云 閱讀:134 作者:Leah 欄目:web開發

這期內容當中的小編將會給大家帶來有關JavaScript中變量的作用域和閉包的應用,以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

一、變量的作用域

1、變量的作用域指變量有效的范圍,與變量定義的位置密切相關,作用域是從空間的角度來描述變量的,也可以理解為變量的可見性。在某個范圍內變量是可見的,也就是說,變量是可用的。

2、按照作用域的不同,變量可分為全局變量和局部變量。

● 全局變量:在全局環境中聲明的變量

● 局部變量:在函數中聲明的變量

● 當函數在執行時,會創建一個封閉的執行期上下文環境,函數內部聲明的變量僅可在函數內部使用,外部無法訪問,而全局變量則在任何地方都可以使用

3、在函數中使用var關鍵字顯示聲明的變量是局部變量;而沒有用var關鍵字,用直接賦值的方式聲明的變量是全局變量

var m=8;
function f1(){
    var a1=10;
    console.log(m);  //8
}
function f2(){
    var a2=20;
    console.log(m);  //8
}
f1();
f2();

4、依賴變量作用域實現封裝特性

(1)使用ES6提供的let
(2)通過函數來創建作用域:

var myObject=(function(){
    var _name="yian";  //私有變量
    return {
        getName:function(){  //公有方法
            return _name;
        }
    }
})();
console.log(myObject._name);  //undefined
console.log(myObject.getName());  //yian

二、變量的生存周期

1、對于全局變量來說,其生命周期是永久的,除非主動銷毀此全局變量;

2、對于在函數內用var關鍵字聲明的局部變量來說,當退出函數時,這些局部變量即失去它們的價值,它們會隨著函數調用的結束而被銷毀

3、模仿塊級作用域

(1)用作塊級作用域的匿名函數:將函數聲明包含在一對圓括號中,表示它實際上是一個函數表達式,而緊隨其后的另一對圓括號會立即調用這個函數。

(function(){
    //這里是塊級作用域
})();

在匿名函數中定義的任何變量,都會在執行結束時被銷毀

(2)先定義一個函數,然后調用它。定義函數的方式是創建一個匿名函數,并把這個匿名函數賦值給變量;而調用函數的方式是在函數的名稱后添加一對圓括號。

var someFunction=function(){
    //這里是塊級作用域
};
someFunction();

經典問題:

var nodes=document.getElementsByTagName("div");
for(var i= 0,len=nodes.length;i<len;i++){
    nodes[i].onclick=function(){
        console.log(i);  //無論點擊哪個div,最后彈出的結果都是5
    }
}

解釋: div節點的onclick事件是被異步觸發的,當事件被觸發時,for循環早已結束,此時i 已經是5

解決辦法:

法一:使用ES6中的let

法二:在閉包的幫助下,把每次循環的i值都封存起來

var nodes=document.getElementsByTagName("div");
for(var i= 0,len=nodes.length;i<len;i++){
    (function(x){
        nodes[i].onclick=function(){
            console.log(x);
        }
    })(i);
}

4、作用:讀取函數內部的變量,并將這些變量的值始終保存在內存中

(1)封裝變量:閉包可以把一些不需要暴露在全局的變量封裝為“私有變量”,私有變量包括函數的參數、局部變量和在函數內部定義的其他函數。

例:mult函數接收number類型的參數,并返回這些參數的乘積、

初始代碼:

var cache={ };
var mult=function(){
    var args=Array.prototype.join.call(arguments,",");
    if(cache[args]){
        return cache[args];
    }
    var a=1;
    for(var i=0,len=arguments.length;i<len;i++){
        a=a*arguments[i];
    }
    return cache[args]=a;
};
console.log(mult(1,2,3));  //6
console.log(mult(3,4,5));  //60

使用閉包后:

var mult=(function(){
    var cache={ };  //加入緩存機制,避免相同參數的計算
    var calculate=function(){
        var a=1;
        for(var i= 0,len=arguments.length;i<len;i++){
            a=a*arguments[i];
        }
        return a;
    };
    return function(){
        var args=Array.prototype.join.call(arguments,",");
        if(args in cache){
            return cache[args];
        }
        return cache[args]=calculate.apply(null,arguments);
    }
})();

補充:in判斷屬性屬于對象

var mycar = {make: "Honda", model: "Accord", year: 1998};
if ( "make" in mycar ){  //屬性名必須是字符串形式,因為make不是一個變量
    document.write('true');  // 顯示true
}
else{
    document.write('false');
}

(2)延續局部變量的壽命

例:使用report數據上報時會丟失30%左右的數據,原因是img時report中的局部變量,當report函數調用結束后,img局部變量隨即被銷毀

初始代碼:

var report=function(src){
    var image=new Image();
    image.src=src;
};

使用閉包后(把img變量用閉包封裝起來):

var report=(function(){
    var imgs=[ ];
    return function(){
        var image=new Image();
        imgs.push(image);
        image.src=src;
    }
})();

5、閉包和面向對象設計

閉包寫法:

var extent=function(){
    var value=0;
    return {
        call:function(){
            value++;
            console.log(value);
        }
    }
}
var extent=extent();
extent.call();  //1
extent.call();  //2

面向對象寫法一:

var extend={
    value:0,
    call:function(){
        this.value++;
        console.log(this.value);
    }
};
extend.call();  //1
extend.call();  //2

面向對象寫法二:

var Extend=function(){
    this.value=0;
};
Extend.prototype.call=function(){
    this.value++;
    console.log(this.value);
};
var extend=new Extend();
extend.call();  //1
extend.call();  //2

6、閉包與內存管理

● 局部變量變量應該在函數退出時被解除引用,但如果局部變量被封閉在閉包形成的環境中,那么局部變量就會一直生存下去,即它會常駐內存。

● 使用閉包的同時比較容易形成循環引用,如果閉包的作用域鏈中保存著一些DOM節點,這就有可能造成內存泄漏。

● 解決循環引用帶來的內存泄漏問題:把循環引用中的變量設為null。(將變量設置為null以為著切斷變量與它之前引用的值之間的連接,當垃圾收集器下次運行時,就會刪除這些值并回收它們占用的內存)

7、特點:

● 函數嵌套函數;

● 在函數內部可引用外部的參數和變量;

● 參數和變量不會以垃圾回收機制回收。

8、優點:避免全局變量的污染

9、缺點:會常駐內存,增加內存的使用量,使用不當會造成內存泄漏;閉包會攜帶包含它的函數的作用域,因此會比其他函數占用更多的內存

10、創建閉包

寫法一:

function a() {  
	var b=123;  
	function c(){
    	console.log(b+=1);
    }  
    return c;
}
var d=a();
d();

方式二:

function f1(){
    var num=10;  //函數執行完畢,變量仍然存在
    var f2=function(){
        num++;
        console.log(num);  //11
    };
    return f2;
}
var res=f1();
res();

● 解釋:執行f1()后,f1()閉包內部的變量會存在,而閉包內部函數的內部變量不會存在,使得JavaScript的垃圾回收機制不會收回f1()占用的資源,因為f1()中內部函數的執行需要依賴f1()中的變量。

方式三:

function foo(x) {
    var tmp = 3;
    return function f2(y) {
        alert(x + y + (++tmp));  //17
    };
}
var bar = foo(3);  //bar現在是一個閉包
bar(10);

練習題:

function f1(){
   var a=1;
   t=function(){
       a++;
   }
   return function(){
       console.log(a);
   }
}
var b=f1();  //返回值為一個匿名函數
b();  //1
t();
b();  //2

聲明變量,若變量名稱相同,就近原則:

var name="g";
function out(){
    var name="loc";
    function foo(){
        console.log(name);
    }
    foo();
}
out();  //name=loc

補充知識點:

1、JS中有哪些垃圾回收機制?

(1)引用計數:跟蹤記錄每個值被使用的次數。

● 當聲明一個變量并將一個引用類型賦值給該變量時,該值的引用次數加1;

● 若該變量的值變為另一個,則該值引用次數減1;

● 若該值引用次數為0時,說明變量沒有在使用,此值無法訪問;

● 因此,可以將它占用的空間回收,垃圾回收機制會在運行時清理引用次數為0 的值所占用的空間。

● 在低版的IE中會發生內存泄漏,很多時候就是因為它采用引用計數得到方式進行垃圾回收(如果兩個對象之間形成了循環引用,那么這兩個對象都無法被回收)。

(2)標記清除:最常見的垃圾回收方式

● 當變量進入執行環境時,垃圾回收器將其標為“進入環境”,離開時標記為“離開環境”;

● 垃圾回收機制在運行時給存儲在內存中的所有變量加上標記;

● 去掉環境中的變量及被環境中變量所引用的變量(閉包)的標記;

● 完成這些后仍存在的標記就是要刪除的變量。

2、哪些操作會造成內存泄漏?

● 內存泄漏:指不再擁有或需要任何對象(數據)之后,它們仍然存在于內存中。

● 垃圾回收器定期掃描對象,并計算引用了每個對象的其他對象的數量。如果一個對象的引用數量為0(沒有其他對象引用過該對象),或對該對象的唯一引用是循環的,那么該對象占用的內存立即被回收。

● 如果setTimeout的第一個參數使用字符串而非函數,會造成內存泄漏。

● 閉包、控制臺日志、循環(在兩個對象彼此引用且彼此保留時,就會產生一個循環)等會造成內存泄漏。

上述就是小編為大家分享的JavaScript中變量的作用域和閉包的應用了,如果您也有類似的疑惑,不妨礙參照上述分析進行理解。如果想了解更多相關內容,請關注億速云行業資訊。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

胶南市| 登封市| 曲沃县| 永泰县| 武定县| 林芝县| 永登县| 本溪市| 石阡县| 长海县| 大埔区| 玉环县| 隆子县| 运城市| 罗山县| 邢台市| 肇州县| 房山区| 天镇县| 土默特右旗| 堆龙德庆县| 秦皇岛市| 开远市| 永靖县| 沁水县| 盐池县| 兰州市| 玛曲县| 灌阳县| 阜阳市| 台东市| 福贡县| 兴城市| 新龙县| 鹤岗市| 屯昌县| 定远县| 双桥区| 南平市| 南充市| 太保市|