欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript?弱引用強(qiáng)引用底層示例詳解

 更新時(shí)間:2022年10月18日 08:38:04   作者:夏安君  
這篇文章主要為大家介紹了JavaScript?弱引用強(qiáng)引用底層示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

內(nèi)存和性能管理是軟件開發(fā)的重要方面,也是每個(gè)軟件開發(fā)人員都應(yīng)該注意的方面。雖然弱引用很有用,但在 JavaScript 中并不經(jīng)常使用。在 ES6 版本中,JavaScript 引入了 WeakSetWeakMap

1. 弱引用

與強(qiáng)引用不同,弱引用并不阻止被引用的對(duì)象被垃圾收集器回收或收集,即使它是內(nèi)存中對(duì)對(duì)象的唯一引用。

在討論強(qiáng)引用、WeakSetSet、WeakMapMap 之前,讓我們用下面的代碼片段來(lái)演示弱引用:

// 創(chuàng)建 WeakMap 對(duì)象的實(shí)例
let human = new WeakMap();
// 創(chuàng)建一個(gè)對(duì)象,并將其賦值給名為 man 的變量
let man = { name: "xiaan" };
// 調(diào)用 human 的 set 方法,并傳遞兩個(gè)參數(shù)(鍵和值)給它
human.set(man, "done")
console.log(human)

以上代碼的輸出如下:

WeakMap {{…} => 'done'}
man = null;
console.log(human)

當(dāng)我們將 man 變量重新賦值為 null 時(shí),內(nèi)存中對(duì)原始對(duì)象的唯一引用是弱引用,它來(lái)自我們前面創(chuàng)建的 WeakMap。當(dāng) JavaScript 引擎運(yùn)行垃圾收集過(guò)程時(shí),man 對(duì)象將從內(nèi)存和我們分配給它的 WeakMap 中刪除。這是因?yàn)樗且粋€(gè)弱引用,并且它不阻止垃圾收集。接下來(lái)我們談?wù)剰?qiáng)引用。

2. 強(qiáng)引用

JavaScript 中的強(qiáng)引用是防止對(duì)象被垃圾回收的引用。它將對(duì)象保存在內(nèi)存中。

下面的代碼片段說(shuō)明了強(qiáng)引用的概念:

let man = {name: "xiaan"};
let human = [man];
man =  null;
console.log(human);

以上代碼的結(jié)果如下:

// 長(zhǎng)度為 1 的對(duì)象數(shù)組
[{…}]

由于 human 數(shù)組和對(duì)象之間存在強(qiáng)引用。對(duì)象被保留在內(nèi)存中,可以通過(guò)以下代碼訪問(wèn):

console.log(human[0])

這里要注意的重要一點(diǎn)是,弱引用不會(huì)阻止對(duì)象被垃圾回收,而強(qiáng)引用卻會(huì)阻止對(duì)象被垃圾回收。

3. JavaScript 的垃圾收集

與所有編程語(yǔ)言一樣,在編寫 JavaScript 時(shí),內(nèi)存管理是需要考慮的關(guān)鍵因素。與 C 語(yǔ)言不同,JavaScript 是一種高級(jí)編程語(yǔ)言,它在創(chuàng)建對(duì)象時(shí)自動(dòng)分配內(nèi)存,在不再需要對(duì)象時(shí)自動(dòng)清除內(nèi)存。當(dāng)不再使用對(duì)象時(shí)清除內(nèi)存的過(guò)程稱為垃圾收集。在談?wù)?JavaScript 中的垃圾收集時(shí),幾乎不可能不觸及可達(dá)性的概念。

3.1 可達(dá)性

在特定作用域中的所有值或在作用域中使用的所有值都被稱為在該作用域中的“可達(dá)”,并被稱為“可達(dá)值”??稍L問(wèn)的值總是存儲(chǔ)在內(nèi)存中。

在以下情況下,值被認(rèn)為是可達(dá)的:

  • 程序根中的值或從根中引用的值,如全局變量或當(dāng)前執(zhí)行的函數(shù)、它的上下文和回調(diào)。
  • 通過(guò)引用或引用鏈從根中訪問(wèn)的值(例如,全局變量中的對(duì)象引用另一個(gè)對(duì)象,該對(duì)象也引用另一個(gè)對(duì)象——這些都被認(rèn)為是可訪問(wèn)的值)。

下面的代碼片段說(shuō)明了可達(dá)性的概念:

var person = {name: "xiaan"};

這里我們有一個(gè)對(duì)象,它的鍵值對(duì)(name"xiaan")引用全局變量 person。如果我們通過(guò)賦值 null 來(lái)覆蓋 person 的值:

person = null;

那么對(duì)象將被垃圾回收,"xiaan" 值將無(wú)法再次訪問(wèn)。下面是另一個(gè)例子:

var person = {name: "xiaan"};
var programmer = person;

從上面的代碼片段中,我們可以從 person 變量和 programmer 變量訪問(wèn) object 屬性。然而,如果我們將person 設(shè)置為 null

person = null;

那么對(duì)象仍然在內(nèi)存中,因?yàn)樗梢酝ㄟ^(guò) programmer 變量訪問(wèn)。簡(jiǎn)單地說(shuō),這就是垃圾收集的工作方式。

注意:默認(rèn)情況下,JavaScript 對(duì)其引用使用強(qiáng)引用。要在 JavaScript 中實(shí)現(xiàn)弱引用,可以使用 WeakMap、WeakSetWeakRef

4. Set VS WeakSet

set 對(duì)象是一個(gè)只有一次出現(xiàn)的唯一值的集合。像數(shù)組一樣,集合沒(méi)有鍵值對(duì)。我們可以使用for...of.forEach 的數(shù)組方法遍歷。

讓我們用以下片段來(lái)說(shuō)明這一點(diǎn):

let setArray = new Set(["Joseph", "Frank", "John", "Davies"]);
for (let names of setArray){
  console.log(names)
}// Joseph Frank John Davies

我們也可以使用 .forEach 遍歷:

 setArray.forEach((name, nameAgain, setArray) =>{
   console.log(names);
 });

WeakSet 是唯一對(duì)象的集合。正如其名稱一樣,弱集使用弱引用。以下是 WeakSet() 的特性:

  • 它可能只包含對(duì)象。
  • 集合中的對(duì)象可以在其他地方訪問(wèn)。
  • 它不能循環(huán)遍歷。
  • Set() 一樣,WeakSet()add、hasdelete 方法。

下面的代碼說(shuō)明了如何使用 WeakSet() 和一些可用的方法:

const human = new WeakSet();
let person = {name: "xiaan"};
human.add(person);
console.log(human.has(person)); // true
person = null;
console.log(human.has(person)); // false

在第 1 行,我們創(chuàng)建了 WeakSet() 的一個(gè)實(shí)例。在第 3 行,我們創(chuàng)建了對(duì)象并將它分配給變量 person。在第 5 行,我們將 person 添加到 WeakSet() 中。在第 9 行,我們將 person 引用設(shè)為空。第 11 行代碼返回false,因?yàn)?WeakSet() 將被自動(dòng)清除,因此,WeakSet() 不會(huì)阻止垃圾回收。

5. Map VS WeakMap

我們從上面關(guān)于垃圾收集的部分了解到,只要可以訪問(wèn),JavaScript 引擎就會(huì)在內(nèi)存中保留一個(gè)值。讓我們用一些片段來(lái)說(shuō)明這一點(diǎn):

let person = {name: "xiaan"};
// 對(duì)象可以從引用中訪問(wèn)
// 覆蓋引用 person.
person = null;
// 該對(duì)象不能被訪問(wèn)

當(dāng)數(shù)據(jù)結(jié)構(gòu)在內(nèi)存中時(shí),數(shù)據(jù)結(jié)構(gòu)的屬性被認(rèn)為是可訪問(wèn)的,并且它們通常保存在內(nèi)存中。如果將對(duì)象存儲(chǔ)在數(shù)組中,那么只要數(shù)組在內(nèi)存中,即使沒(méi)有其他引用,也仍然可以訪問(wèn)對(duì)象。

let person = {name: "xiaan"};
let arr = [person];
// 覆蓋引用
person = null;
console.log(array[0]) // {name: 'xiaan'}

即使引用被覆蓋,我們?nèi)匀荒軌蛟L問(wèn)這個(gè)對(duì)象因?yàn)閷?duì)象被保存在數(shù)組中。因此,只要數(shù)組仍然在內(nèi)存中,它就保存在內(nèi)存中。因此,它沒(méi)有被垃圾回收。由于我們?cè)谏厦娴睦又惺褂昧藬?shù)組,我們也可以使用 map。當(dāng) map 仍然存在時(shí),存儲(chǔ)在其中的值將不會(huì)被垃圾回收。

let map = new Map();
let person = {name: "xiaan"};
map.set(person, "person");
// 覆蓋引用
person = null;
// 還能訪問(wèn)對(duì)象
console.log(map.keys());

與對(duì)象一樣,map 可以保存鍵—值對(duì),我們可以通過(guò)鍵訪問(wèn)值。但是對(duì)于 map,我們必須使用 .get() 方法來(lái)訪問(wèn)值。

根據(jù) Mozilla Developer Network,Map 對(duì)象保存鍵—值對(duì)并記住鍵的原始插入順序。任何值(包括對(duì)象值和原語(yǔ)值)都可以用作鍵或值。

map 不同,WeakMap 保存弱引用。因此,如果這些值在其他地方?jīng)]有被強(qiáng)引用,它不會(huì)阻止垃圾回收刪除它引用的值。除此之外,WeakMapmap 是相同的。由于弱引用,WeakMap 不可枚舉。

對(duì)于 WeakMap,鍵必須是對(duì)象,值可以是數(shù)字或字符串。

下面的代碼片段說(shuō)明了 WeakMap 的工作原理和其中的方法:

// 創(chuàng)建一個(gè) WeakMap
let weakMap = new WeakMap();
let weakMap2 = new WeakMap();
// 創(chuàng)建一個(gè)對(duì)象
let ob = {};
// 使用 set 方法
weakMap.set(ob, "Done");
// 你可以將值設(shè)置為一個(gè)對(duì)象甚至一個(gè)函數(shù)
weakMap.set(ob, ob)
// 可以設(shè)置為undefined
weakMap.set(ob, undefined);
// WeakMap 也可以是值和鍵
weakMap.set(weakMap2, weakMap)
// 要獲取值,使用 get 方法
weakMap.get(ob) // Done
// 使用 has 方法
weakMap.has(ob) // true
weakMap.delete(ob)
weakMap.has(ob) // false

在沒(méi)有其他引用的 WeakMap 中使用對(duì)象作為鍵的一個(gè)主要副作用是,它們將在垃圾收集期間自動(dòng)從內(nèi)存中刪除。

6. WeakMap 的應(yīng)用

WeakMap 可以用于 web 開發(fā)的兩個(gè)領(lǐng)域:緩存和額外的數(shù)據(jù)存儲(chǔ)。

6.1 緩存

這是一種 web 技術(shù),它涉及到保存(即存儲(chǔ))給定資源的副本,并在請(qǐng)求時(shí)返回它。可以緩存函數(shù)的結(jié)果,以便在調(diào)用函數(shù)時(shí)重用緩存的結(jié)果。

讓我們來(lái)看看實(shí)際情況。

let cachedResult = new WeakMap();
// 存儲(chǔ)結(jié)果的函數(shù)
function keep(obj){
	if(!cachedResult.has(obj){
    let result = obj;
    cachedResult.set(obj, result);
  }
	return cachedResult.get(obj);
}
let obj = {name: "xiaan"};
let resultSaved = keep(obj)
obj = null;
// console.log(cachedResult.size); Possible with map, not with WeakMap

如果我們?cè)谏厦娴拇a中使用 Map() 而不是 WeakMap(),并且對(duì) keep() 函數(shù)有多次調(diào)用,那么它只會(huì)在第一次調(diào)用時(shí)計(jì)算結(jié)果,并在其他時(shí)候從 cachedResult 檢索結(jié)果。副作用是,每當(dāng)對(duì)象不需要時(shí),我們就需要清理 cachedResult。使用 WeakMap(),一旦對(duì)象被垃圾回收,緩存的結(jié)果就會(huì)自動(dòng)從內(nèi)存中刪除。緩存是提高軟件性能的一種很好的方法——它可以節(jié)省數(shù)據(jù)庫(kù)使用、第三方 API 調(diào)用和服務(wù)器對(duì)服務(wù)器請(qǐng)求的成本。通過(guò)緩存,請(qǐng)求結(jié)果的副本被保存在本地。

6.2 額外的數(shù)據(jù)存儲(chǔ)

WeakMap() 的另一個(gè)重要用途是額外的數(shù)據(jù)存儲(chǔ)。想象一下,我們正在建立一個(gè)電子商務(wù)平臺(tái),我們有一個(gè)計(jì)算訪客數(shù)量的程序,我們希望能夠在訪客離開時(shí)減少計(jì)數(shù)。這個(gè)任務(wù)在 Map 中要求很高,但在 WeakMap() 中很容易實(shí)現(xiàn):

let visitorCount = new WeakMap();
function countCustomer(customer){
  let count = visitorCount.get(customer) || 0;
  visitorCount.set(customer, count + 1);
}
let person = {name: "xiaan"};
// 統(tǒng)計(jì)訪問(wèn)人數(shù)
countCustomer(person)
// 訪客離開
person = null;

使用 Map(),我們必須在客戶離開時(shí)清除 visitorCount,否則,它將在內(nèi)存中無(wú)限增長(zhǎng),占用空間。但是使用 WeakMap(),我們不需要清理 visitorCount,一旦一個(gè)人(對(duì)象)變得不可訪問(wèn),它就會(huì)自動(dòng)被垃圾回收。

7. 小結(jié)

在本文中,我們了解了弱引用、強(qiáng)引用和可達(dá)性的概念,并盡可能地將它們與內(nèi)存管理聯(lián)系起來(lái)。

更多關(guān)于JavaScript 弱引用強(qiáng)引用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論