อะไรคือการใช้งานจริงของ ES6 WeakMap?


397

การใช้งานจริงของWeakMapโครงสร้างข้อมูลนำมาใช้ใน ECMAScript 6 อย่างไร

เนื่องจากคีย์ของแผนที่ที่อ่อนแอสร้างการอ้างอิงที่แข็งแกร่งกับค่าที่สอดคล้องกันเพื่อให้มั่นใจว่าค่าที่ถูกแทรกลงในแผนที่ที่อ่อนแอจะไม่หายไปตราบใดที่คีย์ของมันยังมีชีวิตอยู่จึงไม่สามารถใช้สำหรับตารางบันทึกได้ แคชหรือสิ่งอื่นใดที่ปกติแล้วคุณจะใช้การอ้างอิงที่อ่อนแอแผนที่ที่มีค่าต่ำเป็นต้น

ฉันดูเหมือนว่าสิ่งนี้:

weakmap.set(key, value);

... เป็นเพียงวิธีวงเวียนในการพูดแบบนี้:

key.value = value;

กรณีการใช้งานของฉันหายไปอย่างเป็นรูปธรรม



2
และอีกอันหนึ่ง - ilikekillnerds.com/2015/02/what-are-weakmaps-in-es6
James Sumners

35
กรณีการใช้งานจริง: เก็บข้อมูลที่กำหนดเองสำหรับโหนด DOM
เฟลิกซ์คลิง

ทุกกรณีการใช้งานที่คุณกล่าวถึงสำหรับการอ้างอิงที่อ่อนแอนั้นมีความสำคัญเป็นอย่างยิ่ง พวกมันยากมากที่จะเพิ่มในภาษาเนื่องจากพวกเขาแนะนำลัทธิ nondeterminism มาร์คมิลเลอร์และคนอื่น ๆ ทำงานหนักในการอ้างอิงที่อ่อนแอและฉันคิดว่าพวกเขากำลังจะมาในที่สุด ในที่สุด
Benjamin Gruenbaum

2
WeakMaps สามารถใช้เพื่อตรวจหารอยรั่วหน่วยความจำ: stevehanov.ca/blog/?id=148
theWebalyst

คำตอบ:


513

ลึกซึ้ง

WeakMaps จัดเตรียมวิธีการขยายวัตถุจากภายนอกโดยไม่รบกวนการเก็บขยะ เมื่อใดก็ตามที่คุณต้องการที่จะขยายวัตถุ แต่ไม่สามารถทำได้เพราะมันถูกปิดผนึก - หรือจากแหล่งภายนอก - WeakMap สามารถนำไปใช้

WeakMap เป็นแผนที่ (พจนานุกรม) ที่คีย์อ่อนแอ - นั่นคือถ้าการอ้างอิงทั้งหมดไปยังคีย์หายไปและไม่มีการอ้างอิงถึงค่าอีกต่อไป - ค่าสามารถเก็บรวบรวมขยะได้ มาแสดงตัวอย่างนี้ก่อนจากนั้นอธิบายให้ใช้และใช้งานจริงในที่สุด

สมมติว่าฉันใช้ API ที่ให้วัตถุกับฉัน:

var obj = getObjectFromLibrary();

ตอนนี้ฉันมีวิธีการที่ใช้วัตถุ:

function useObj(obj){
   doSomethingWith(obj);
}

ฉันต้องการติดตามจำนวนครั้งที่วิธีการนั้นถูกเรียกด้วยวัตถุบางอย่างและรายงานว่ามันเกิดขึ้นมากกว่า N ครั้ง อย่างไร้เดียงสาใครจะคิดว่าใช้แผนที่:

var map = new Map(); // maps can have object keys
function useObj(obj){
    doSomethingWith(obj);
    var called = map.get(obj) || 0;
    called++; // called one more time
    if(called > 10) report(); // Report called more than 10 times
    map.set(obj, called);
}

ใช้งานได้ แต่มีหน่วยความจำรั่ว - ตอนนี้เราติดตามทุกวัตถุไลบรารีเดียวที่ส่งผ่านไปยังฟังก์ชันที่ป้องกันไม่ให้วัตถุห้องสมุดถูกเก็บรวบรวมขยะ เราสามารถใช้WeakMap:

var map = new WeakMap(); // create a weak map
function useObj(obj){
    doSomethingWith(obj);
    var called = map.get(obj) || 0;
    called++; // called one more time
    if(called > 10) report(); // Report called more than 10 times
    map.set(obj, called);
}

และหน่วยความจำรั่วไหลออกไป

ใช้กรณี

บางกรณีใช้งานที่อาจทำให้หน่วยความจำรั่วและเปิดใช้งานโดยWeakMaps รวมถึง:

  • การเก็บข้อมูลส่วนตัวเกี่ยวกับวัตถุเฉพาะและให้การเข้าถึงแก่ผู้ที่มีการอ้างอิงถึงแผนที่เท่านั้น วิธี ad-hoc เพิ่มเติมมาพร้อมกับข้อเสนอสัญลักษณ์ส่วนตัว แต่นั่นเป็นเวลานานจากนี้
  • การเก็บข้อมูลเกี่ยวกับวัตถุไลบรารีโดยไม่เปลี่ยนหรือก่อให้เกิดค่าใช้จ่าย
  • การเก็บข้อมูลเกี่ยวกับชุดวัตถุขนาดเล็กที่มีวัตถุหลายประเภทที่มีอยู่เพื่อไม่ให้เกิดปัญหากับคลาสที่ซ่อนอยู่เอ็นจิน JS engine ใช้สำหรับวัตถุประเภทเดียวกัน
  • การเก็บข้อมูลเกี่ยวกับวัตถุโฮสต์เช่นโหนด DOM ในเบราว์เซอร์
  • การเพิ่มความสามารถให้กับวัตถุจากภายนอก (เช่นตัวอย่างตัวปล่อยเหตุการณ์ในคำตอบอื่น ๆ )

ลองดูการใช้งานจริง

มันสามารถใช้เพื่อขยายวัตถุจากภายนอก ลองยกตัวอย่างจริง (ดัดแปลงเรียงลำดับของจริง - เพื่อให้เป็นจุด) จากโลกแห่งความจริงของ Node.js

สมมติว่าคุณเป็น Node.js และคุณมีPromiseวัตถุ - ตอนนี้คุณต้องการติดตามสัญญาที่ถูกปฏิเสธทั้งหมดในปัจจุบัน - อย่างไรก็ตามคุณไม่ต้องการให้พวกเขาถูกเก็บขยะในกรณีที่ไม่มีการอ้างอิงถึงพวกเขา

ตอนนี้คุณไม่ต้องการเพิ่มคุณสมบัติให้กับวัตถุดั้งเดิมด้วยเหตุผลที่ชัดเจนดังนั้นคุณจึงติดขัด หากคุณยังคงอ้างอิงถึงสัญญาที่คุณทำให้หน่วยความจำรั่วเนื่องจากไม่มีการรวบรวมขยะสามารถเกิดขึ้นได้ หากคุณไม่เก็บข้อมูลอ้างอิงคุณจะไม่สามารถบันทึกข้อมูลเพิ่มเติมเกี่ยวกับสัญญาแต่ละสัญญาได้ รูปแบบใด ๆ ที่เกี่ยวข้องกับการบันทึก ID ของสัญญาโดยเนื้อแท้หมายความว่าคุณต้องมีการอ้างอิง

ป้อน WeakMaps

WeakMaps หมายความว่ากุญแจนั้นอ่อนแอ ไม่มีวิธีระบุแผนที่ที่อ่อนแอหรือรับค่าทั้งหมด ในแผนที่ที่ไม่รัดกุมคุณสามารถจัดเก็บข้อมูลตามคีย์และเมื่อคีย์ได้รับการรวบรวมขยะให้ทำค่า

ซึ่งหมายความว่าให้สัญญาคุณสามารถเก็บสถานะเกี่ยวกับมัน - และวัตถุนั้นยังสามารถเก็บขยะ ในภายหลังหากคุณได้รับการอ้างอิงไปยังวัตถุคุณสามารถตรวจสอบว่าคุณมีสถานะใด ๆ ที่เกี่ยวข้องกับมันและรายงาน

สิ่งนี้ถูกนำมาใช้เพื่อดำเนินการปฏิเสธการขอจัดการโดย Petka Antonov เช่นนี้ :

process.on('unhandledRejection', function(reason, p) {
    console.log("Unhandled Rejection at: Promise ", p, " reason: ", reason);
    // application specific logging, throwing an error, or other logic here
});

เราเก็บข้อมูลเกี่ยวกับสัญญาไว้ในแผนที่และสามารถทราบได้เมื่อสัญญาที่ถูกปฏิเสธได้รับการจัดการ


8
สวัสดี! คุณช่วยกรุณาบอกฉันว่าส่วนหนึ่งของรหัสตัวอย่างทำให้หน่วยความจำรั่ว?
ltamajs

15
@ ltamajs4 แน่นอนในuseObjตัวอย่างโดยใช้Mapและไม่ใช่WeakMapเราใช้วัตถุที่ผ่านเป็นคีย์แผนที่ วัตถุจะไม่ถูกลบออกจากแผนที่ (เนื่องจากเราไม่รู้ว่าจะต้องทำเมื่อไหร่) ดังนั้นจึงมีการอ้างอิงถึงมันอยู่เสมอและจะไม่มีการเก็บขยะ ในตัวอย่าง WeakMap เร็วที่สุดเท่าที่อ้างอิงอื่น ๆ ทั้งหมดไปยังวัตถุที่หายไป - WeakMapวัตถุที่สามารถหักล้างได้จาก หากคุณยังไม่แน่ใจว่าฉันหมายถึงอะไรโปรดแจ้งให้เราทราบ
Benjamin Gruenbaum

@Benjamin เราจำเป็นต้องแยกแยะระหว่างความต้องการแคชที่มีความสำคัญของหน่วยความจำและความต้องการ data_object tuple อย่าทำให้ข้อกำหนดทั้งสองนี้แยกจากกัน calledตัวอย่างของคุณเขียนได้ดีขึ้นโดยใช้jsfiddle.net/f2efbm7zและไม่แสดงการใช้แผนที่ที่ไม่รัดกุม ในความเป็นจริงมันสามารถเขียนได้ดีกว่าในทั้งหมด6วิธีซึ่งฉันจะแสดงรายการด้านล่าง
Pacerier

โดยพื้นฐานแล้วจุดประสงค์ของแผนที่ที่อ่อนแอนั้นเป็นแคชที่ไวต่อหน่วยความจำ ในขณะที่มันสามารถนำมาใช้ในการขยายวัตถุจากภายนอกที่เป็นสับหมัด undeeded และแน่นอนไม่ได้เป็นวัตถุประสงค์ที่เหมาะสมของมัน
Pacerier

1
หากคุณต้องการให้ลิงก์ระหว่างสัญญาและจำนวนครั้งที่มีการจัดการ / ปฏิเสธให้ใช้สัญลักษณ์1) ; p[key_symbol] = data. หรือ2)การตั้งชื่อที่ไม่ซ้ำกัน p.__key = data. หรือ3)ขอบเขตส่วนตัว (()=>{let data; p.Key = _=>data=_;})(). หรือ4)พร็อกซีที่มี 1 หรือ 2 หรือ 3 หรือ5)แทนที่ / ขยายคลาส Promise ด้วย 1 หรือ 2 หรือ 3 หรือ6) แทนที่ / ขยายคลาส Promise ด้วย tuple ของสมาชิกที่ต้องการ - ไม่ว่าในกรณีใด ๆแผนที่ที่อ่อนแอไม่จำเป็นเว้นแต่คุณจะต้องใช้แคชที่ไวต่อหน่วยความจำ
Pacerier

48

คำตอบนี้ดูเหมือนจะเอนเอียงและใช้ไม่ได้ในสถานการณ์จริง โปรดอ่านอย่างที่เป็นอยู่และไม่ถือว่าเป็นตัวเลือกที่แท้จริงสำหรับสิ่งอื่นนอกเหนือจากการทดลอง

กรณีการใช้งานอาจใช้เป็นพจนานุกรมสำหรับผู้ฟังฉันมีเพื่อนร่วมงานที่ทำสิ่งนั้น มันมีประโยชน์มากเพราะผู้ฟังคนใดมีเป้าหมายโดยตรงกับวิธีการทำสิ่งนี้ listener.onลาก่อน

แต่จากมุมมองที่เป็นนามธรรมมากขึ้นWeakMapโดยเฉพาะอย่างยิ่งมีประสิทธิภาพในการ dematerialize การเข้าถึงสิ่งใดโดยทั่วไปคุณไม่จำเป็นต้องมีเนมสเปซเพื่อแยกสมาชิกเนื่องจากมันบ่งบอกโดยธรรมชาติของโครงสร้างนี้แล้ว ฉันค่อนข้างมั่นใจว่าคุณสามารถทำการปรับปรุงหน่วยความจำที่สำคัญได้โดยการแทนที่คีย์ออบเจ็กต์ซ้ำซ้อนที่น่าอึดอัดใจ (แม้ว่า deconstructing จะทำงานได้ดีสำหรับคุณ)


ก่อนอ่านสิ่งต่อไปคืออะไร

ผมทำตอนนี้ตระหนักถึงฉันเน้นไม่ว่าวิธีที่ดีที่สุดที่จะจัดการปัญหาและเป็นเบนจามิน Gruenbaumชี้ให้เห็น (ตรวจสอบคำตอบของเขาถ้ามันไม่ได้แล้วข้างต้นเหมือง: p) ปัญหานี้อาจจะไม่ได้รับการแก้ไขด้วยปกติMapตั้งแต่ มันจะรั่วไหลออกมาดังนั้นจุดแข็งหลักของWeakMapมันก็คือมันจะไม่เข้าไปยุ่งเกี่ยวกับการเก็บขยะเพราะพวกมันไม่ได้อ้างอิง


นี่คือรหัสที่แท้จริงของเพื่อนร่วมงานของฉัน (ขอบคุณเขาที่แชร์)

แหล่งข้อมูลเต็มรูปแบบที่นี่มันเกี่ยวกับการจัดการผู้ฟังที่ผมพูดถึงข้างต้น (คุณสามารถดูรายละเอียดได้ )

var listenableMap = new WeakMap();


export function getListenable (object) {
    if (!listenableMap.has(object)) {
        listenableMap.set(object, {});
    }

    return listenableMap.get(object);
}


export function getListeners (object, identifier) {
    var listenable = getListenable(object);
    listenable[identifier] = listenable[identifier] || [];

    return listenable[identifier];
}


export function on (object, identifier, listener) {
    var listeners = getListeners(object, identifier);

    listeners.push(listener);
}


export function removeListener (object, identifier, listener) {
    var listeners = getListeners(object, identifier);

    var index = listeners.indexOf(listener);
    if(index !== -1) {
        listeners.splice(index, 1);
    }
}


export function emit (object, identifier, ...args) {
    var listeners = getListeners(object, identifier);

    for (var listener of listeners) {
        listener.apply(object, args);
    }
}

2
ฉันไม่เข้าใจว่าคุณจะใช้สิ่งนี้อย่างไร มันจะทำให้ผู้สังเกตการณ์ยุบรวมถึงเหตุการณ์ที่ถูกผูกไว้กับมันเมื่อไม่มีการอ้างอิงอีกต่อไป ปัญหาที่ฉันมักจะมีคือเมื่อไม่มีการอ้างอิงผู้สังเกตการณ์อีกต่อไป ฉันคิดว่าวิธีแก้ปัญหาที่นี่แก้ปัญหาเพียงครึ่งเดียว ฉันไม่คิดว่าคุณสามารถแก้ปัญหาผู้สังเกตการณ์ด้วย WeakMap ได้เพราะมันไม่สามารถทำได้
jgmjgm

1
ผู้ฟังเหตุการณ์ดับเบิลบัฟเฟอร์อาจเร็วในภาษาอื่น แต่ในกรณีนี้มันเป็นเพียงความลับธรรมดาและช้า นั่นคือสามเซ็นต์ของฉัน
Jack Giffin

@axelduch ว้าวตำนานผู้ฟังที่ฟังได้รับการเร่ขายไปยังชุมชน Javascript เพิ่มขึ้น 40 upvotes! เพื่อให้เข้าใจว่าทำไมคำตอบนี้ไม่ถูกต้องสมบูรณ์โปรดดูความคิดเห็นภายใต้stackoverflow.com/a/156618/632951
Pacerier

1
@Pacerier อัปเดตคำตอบขอบคุณสำหรับข้อเสนอแนะ
axelduch

1
@axelduch ใช่แล้วมีการอ้างอิงจากที่นั่นเช่นกัน
Pacerier

18

WeakMap ทำงานได้ดีสำหรับการห่อหุ้มและซ่อนข้อมูล

WeakMapใช้งานได้กับ ES6 ขึ้นไปเท่านั้น A WeakMapคือชุดของคู่ของคีย์และค่าที่คีย์ต้องเป็นวัตถุ ในตัวอย่างต่อไปนี้เราสร้าง a WeakMapด้วยสองรายการ:

var map = new WeakMap();
var pavloHero = {first: "Pavlo", last: "Hero"};
var gabrielFranco = {first: "Gabriel", last: "Franco"};
map.set(pavloHero, "This is Hero");
map.set(gabrielFranco, "This is Franco");
console.log(map.get(pavloHero));//This is Hero

เราใช้set()วิธีการกำหนดความสัมพันธ์ระหว่างวัตถุและรายการอื่น (สตริงในกรณีของเรา) เราใช้get()วิธีการดึงรายการที่เกี่ยวข้องกับวัตถุ แง่มุมที่น่าสนใจของWeakMaps คือความจริงที่ว่ามันมีการอ้างอิงที่ไม่ดีกับกุญแจในแผนที่ การอ้างอิงที่ไม่รัดกุมหมายความว่าหากวัตถุถูกทำลายตัวรวบรวมขยะจะลบรายการทั้งหมดออกจากWeakMapดังนั้นจึงเป็นการเพิ่มหน่วยความจำ

var TheatreSeats = (function() {
  var priv = new WeakMap();
  var _ = function(instance) {
    return priv.get(instance);
  };

  return (function() {
      function TheatreSeatsConstructor() {
        var privateMembers = {
          seats: []
        };
        priv.set(this, privateMembers);
        this.maxSize = 10;
      }
      TheatreSeatsConstructor.prototype.placePerson = function(person) {
        _(this).seats.push(person);
      };
      TheatreSeatsConstructor.prototype.countOccupiedSeats = function() {
        return _(this).seats.length;
      };
      TheatreSeatsConstructor.prototype.isSoldOut = function() {
        return _(this).seats.length >= this.maxSize;
      };
      TheatreSeatsConstructor.prototype.countFreeSeats = function() {
        return this.maxSize - _(this).seats.length;
      };
      return TheatreSeatsConstructor;
    }());
})()

4
เรื่อง "อ่อนแอแผนที่ทำงานได้ดีสำหรับการห่อหุ้มและซ่อนข้อมูล" เพียงเพราะคุณทำได้ไม่ได้หมายความว่าคุณควรทำ Javascript มีวิธีการเริ่มต้นในการทำ encapsulation และซ่อนข้อมูลก่อนที่จะทำการสร้างแผนที่ ณ ตอนนี้มีตัวอักษร 6 วิธีที่จะทำมัน การใช้จุดอ่อนในการทำ encapsulation เป็นรูปหน้าน่าเกลียด
Pacerier

12

𝗠𝗲𝘁𝗮𝗱𝗮𝘁𝗮

แผนที่ที่อ่อนแอสามารถใช้เพื่อจัดเก็บข้อมูลเมตาเกี่ยวกับองค์ประกอบ DOM โดยไม่รบกวนการเก็บขยะหรือทำให้เพื่อนร่วมงานคลั่งไคล้รหัสของคุณ ตัวอย่างเช่นคุณสามารถใช้เพื่อจัดทำดัชนีองค์ประกอบทั้งหมดในเว็บเพจ

𝗪𝗶𝘁𝗵𝗼𝘂𝘁𝗪𝗲𝗮𝗸𝗠𝗮𝗽𝘀𝗼𝗿𝗪𝗲𝗮𝗸𝗦𝗲𝘁𝘀:

var elements = document.getElementsByTagName('*'),
  i = -1, len = elements.length;

while (++i !== len) {
  // Production code written this poorly makes me want to cry:
  elements[i].lookupindex = i;
  elements[i].elementref = [];
  elements[i].elementref.push( elements[(i * i) % len] );
}

// Then, you can access the lookupindex's
// For those of you new to javascirpt, I hope the comments below help explain 
// how the ternary operator (?:) works like an inline if-statement
document.write(document.body.lookupindex + '<br />' + (
    (document.body.elementref.indexOf(document.currentScript) !== -1)
    ? // if(document.body.elementref.indexOf(document.currentScript) !== -1){
    "true"
    : // } else {
    "false"
  )   // }
);

𝗨𝘀𝗶𝗻𝗴𝗪𝗲𝗮𝗸𝗠𝗮𝗽𝘀𝗮𝗻𝗱𝗪𝗲𝗮𝗸𝗦𝗲𝘁𝘀:

var DOMref = new WeakMap(),
  __DOMref_value = Array,
  __DOMref_lookupindex = 0,
  __DOMref_otherelement = 1,
  elements = document.getElementsByTagName('*'),
  i = -1, len = elements.length, cur;

while (++i !== len) {
  // Production code written this greatly makes me want to 😊:
  cur = DOMref.get(elements[i]);
  if (cur === undefined)
    DOMref.set(elements[i], cur = new __DOMref_value)

  cur[__DOMref_lookupindex] = i;
  cur[__DOMref_otherelement] = new WeakSet();
  cur[__DOMref_otherelement].add( elements[(i * i) % len] );
}

// Then, you can access the lookupindex's
cur = DOMref.get(document.body)
document.write(cur[__DOMref_lookupindex] + '<br />' + (
    cur[__DOMref_otherelement].has(document.currentScript)
    ? // if(cur[__DOMref_otherelement].has(document.currentScript)){
    "true"
    : // } else {
    "false"
  )   // }
);

𝗧𝗵𝗲𝗗𝗶𝗳𝗳𝗲𝗿𝗲𝗻𝗰𝗲

ความแตกต่างอาจดูเล็กน้อยนอกเหนือจากความจริงที่ว่าเวอร์ชันของอ่อนแอแผนที่นั้นยาวกว่าอย่างไรก็ตามมีความแตกต่างที่สำคัญระหว่างรหัสสองชิ้นที่แสดงด้านบน ในตัวอย่างโค้ดแรกโดยไม่มีการแมปอ่อนชิ้นส่วนของรหัสจะอ้างอิงทุก ๆ ทางระหว่างองค์ประกอบ DOM สิ่งนี้ป้องกันไม่ให้องค์ประกอบ DOM ถูกรวบรวมขยะ(i * i) % lenอาจดูเหมือนแปลกที่ไม่มีใครใช้ แต่คิดอีกครั้ง: รหัสการผลิตจำนวนมากมีการอ้างอิง DOM ที่เด้งไปทั่วเอกสาร ตอนนี้สำหรับโค้ดส่วนที่สองเนื่องจากการอ้างอิงทั้งหมดไปยังองค์ประกอบนั้นอ่อนแอเมื่อคุณลบโหนดออกเบราว์เซอร์สามารถตรวจสอบได้ว่าไม่ได้ใช้โหนดนั้น (ไม่สามารถเข้าถึงได้โดยรหัสของคุณ) และ จึงลบออกจากหน่วยความจำ เหตุผลที่คุณควรกังวลเกี่ยวกับการใช้หน่วยความจำและจุดยึดหน่วยความจำ (สิ่งต่าง ๆ เช่นส่วนย่อยของรหัสแรกที่องค์ประกอบที่ไม่ได้ใช้งานจะถูกเก็บไว้ในหน่วยความจำ) เนื่องจากการใช้หน่วยความจำเพิ่มเติม หลีกเลี่ยงความผิดพลาดของเบราว์เซอร์) หมายถึงประสบการณ์การท่องเว็บที่ช้าลงและบางครั้งเบราว์เซอร์อาจเสียหาย

สำหรับ polyfill สำหรับสิ่งเหล่านี้ฉันจะแนะนำห้องสมุดของตัวเอง ( พบได้ที่นี่ @ github ) มันเป็นไลบรารี่ที่มีน้ำหนักเบามากซึ่งจะเติมโพลิฟิลโดยไม่มีกรอบที่ซับซ้อนเกินไปที่คุณอาจพบในโพลีฟิลอื่น ๆ

~ การเข้ารหัสที่มีความสุข!


1
ขอบคุณสำหรับคำอธิบายที่ชัดเจน ตัวอย่างมีค่ามากกว่าคำพูดใด ๆ
newguy

@ lolzery เรื่องนี้เป็นการป้องกันองค์ประกอบ DOM ไม่ให้ถูกรวบรวมขยะ "สิ่งที่คุณต้องมีเพื่อตั้งค่าelementsให้เป็นโมฆะและคุณเสร็จแล้ว: มันจะ GCed & อ้างอิง " การอ้างอิง DOM ที่เด้งไปทั่วเอกสาร " ไม่สำคัญเลย: เมื่อลิงก์หลักelementsหายไปการอ้างอิงแบบวงกลมทั้งหมดจะถูก GCed หากองค์ประกอบของคุณมีการอ้างอิงกับองค์ประกอบที่ไม่ต้องการให้แก้ไขรหัสและตั้งค่าการอ้างอิงเป็นโมฆะเมื่อคุณใช้เสร็จแล้ว มันจะเป็น GCed Weakmaps ไม่จำเป็นต้อง
Pacerier

2
@Pacerier ขอบคุณสำหรับคำติชมที่กระตือรือร้นของคุณอย่างไรก็ตามการตั้งค่าelementsเป็น null จะไม่อนุญาตให้เบราว์เซอร์ GC ตรวจสอบองค์ประกอบในสถานการณ์ตัวอย่างแรก นี่เป็นเพราะคุณตั้งค่าคุณสมบัติที่กำหนดเองในองค์ประกอบจากนั้นองค์ประกอบเหล่านั้นยังคงสามารถรับได้และคุณสมบัติที่กำหนดเองของพวกเขายังสามารถเข้าถึงได้ดังนั้นจึงป้องกันไม่ให้คุณสมบัติใด ๆ ของพวกเขาถูก GC'ed คิดเหมือนห่วงโซ่โลหะ โซลองที่คุณสามารถเข้าถึงลิงก์อย่างน้อยหนึ่งรายการในเชนคุณสามารถกดลิงค์นั้นไว้ในเชนนั้นและป้องกันไม่ให้โซ่ทั้งหมดของไอเท็มตกลงไปในเหว
Jack Giffin

1
รหัสการผลิตด้วย dunder ชื่อ vars ทำให้ฉันอาเจียน
Barbu Barbu

10

ฉันใช้WeakMapสำหรับแคชของการบันทึกความจำที่ไม่ต้องกังวลของฟังก์ชั่นที่ใช้ในวัตถุที่ไม่เปลี่ยนรูปเป็นพารามิเตอร์ของพวกเขา

การบันทึกเป็นวิธีแฟนซีในการพูดว่า "หลังจากคุณคำนวณค่าแคชแล้วดังนั้นคุณจึงไม่ต้องคำนวณอีกครั้ง"

นี่คือตัวอย่าง:

สิ่งที่ควรทราบ:

  • วัตถุ Immutable.js ส่งคืนวัตถุใหม่ (พร้อมตัวชี้ใหม่) เมื่อคุณปรับเปลี่ยนเพื่อใช้เป็นกุญแจใน WeakMap จะรับประกันค่าที่คำนวณได้เช่นเดียวกัน
  • WeakMap นั้นยอดเยี่ยมสำหรับบันทึกช่วยจำเพราะเมื่อวัตถุ (ใช้เป็นกุญแจ) ได้รับการรวบรวมขยะดังนั้นค่าที่คำนวณได้ใน WeakMap

1
นี่คือการใช้จุดอ่อนที่ถูกต้องตราบใดที่แคช memoization มีความไวต่อหน่วยความจำไม่คงอยู่ตลอดอายุการใช้งาน obj / ฟังก์ชัน หาก "memoization cache" นั้นหมายถึงการคงอยู่ตลอดอายุการใช้งานของ obj / ฟังก์ชั่นอ่อนแอmapเป็นตัวเลือกที่ผิด: ใช้ เทคนิคการห่อหุ้มจาวาสคริปต์ 6 ค่าเริ่มต้นแทน
Pacerier

3

ฉันมีกรณี / ตัวอย่างการใช้ WeakMaps ที่ใช้งานง่ายนี้เป็นพื้นฐาน

จัดการคอลเลกชันของผู้ใช้

ผมเริ่มออกด้วยUserวัตถุที่มีคุณสมบัติ ได้แก่fullname, username, age, genderและวิธีการที่เรียกว่าprintที่พิมพ์สรุปการอ่านของมนุษย์ของคุณสมบัติอื่น ๆ

/**
Basic User Object with common properties.
*/
function User(username, fullname, age, gender) {
    this.username = username;
    this.fullname = fullname;
    this.age = age;
    this.gender = gender;
    this.print = () => console.log(`${this.fullname} is a ${age} year old ${gender}`);
}

จากนั้นผมก็เพิ่มแผนที่เรียกว่าเพื่อให้คอลเลกชันของผู้ใช้หลายคนที่มีความสำคัญโดยusersusername

/**
Collection of Users, keyed by username.
*/
var users = new Map();

การเพิ่มคอลเล็กชันยังต้องการฟังก์ชันผู้ช่วยในการเพิ่มรับลบผู้ใช้และแม้แต่ฟังก์ชันเพื่อพิมพ์ผู้ใช้ทั้งหมดเพื่อความสมบูรณ์

/**
Creates an User Object and adds it to the users Collection.
*/
var addUser = (username, fullname, age, gender) => {
    let an_user = new User(username, fullname, age, gender);
    users.set(username, an_user);
}

/**
Returns an User Object associated with the given username in the Collection.
*/
var getUser = (username) => {
    return users.get(username);
}

/**
Deletes an User Object associated with the given username in the Collection.
*/
var deleteUser = (username) => {
    users.delete(username);
}

/**
Prints summary of all the User Objects in the Collection.
*/
var printUsers = () => {
    users.forEach((user) => {
        user.print();
    });
}

เมื่อโค้ดข้างต้นทั้งหมดทำงานอยู่ให้บอกNodeJSเฉพาะusersMap เท่านั้นที่มีการอ้างอิงถึง User Objects ภายในกระบวนการทั้งหมด ไม่มีการอ้างอิงอื่นไปยังวัตถุผู้ใช้แต่ละรายการ

ใช้รหัสนี้เปลือก NodeJS แบบโต้ตอบเช่นเดียวกับตัวอย่างฉันเพิ่มผู้ใช้สี่คนและพิมพ์พวกเขา: การเพิ่มและการพิมพ์ผู้ใช้

เพิ่มข้อมูลเพิ่มเติมให้กับผู้ใช้โดยไม่ต้องแก้ไขรหัสที่มีอยู่

ตอนนี้ต้องมีคุณสมบัติใหม่ที่เชื่อมโยง Social Media Platform (SMP) ของผู้ใช้แต่ละคนต้องถูกติดตามพร้อมกับ User Objects

ที่สำคัญคือที่นี่ยังต้องใช้คุณสมบัตินี้ด้วยการแทรกแซงขั้นต่ำกับรหัสที่มีอยู่

สิ่งนี้เป็นไปได้ด้วย WeakMaps ในลักษณะดังต่อไปนี้

ฉันเพิ่ม WeakMaps สามรายการสำหรับ Twitter, Facebook, LinkedIn

/*
WeakMaps for Social Media Platforms (SMPs).
Could be replaced by a single Map which can grow
dynamically based on different SMP names . . . anyway...
*/
var sm_platform_twitter = new WeakMap();
var sm_platform_facebook = new WeakMap();
var sm_platform_linkedin = new WeakMap();

ฟังก์ชันตัวช่วยgetSMPWeakMapถูกเพิ่มเข้ามาเพื่อส่งคืน WeakMap ที่เชื่อมโยงกับชื่อ SMP ที่กำหนด

/**
Returns the WeakMap for the given SMP.
*/
var getSMPWeakMap = (sm_platform) => {
    if(sm_platform == "Twitter") {
        return sm_platform_twitter;
    }
    else if(sm_platform == "Facebook") {
        return sm_platform_facebook;
    }
    else if(sm_platform == "LinkedIn") {
        return sm_platform_linkedin;
    }
    return undefined;
}

ฟังก์ชั่นเพื่อเพิ่มลิงก์ผู้ใช้ SMP ไปยัง SMP WeakMap ที่กำหนด

/**
Adds a SMP link associated with a given User. The User must be already added to the Collection.
*/
var addUserSocialMediaLink = (username, sm_platform, sm_link) => {
    let user = getUser(username);
    let sm_platform_weakmap = getSMPWeakMap(sm_platform);
    if(user && sm_platform_weakmap) {
        sm_platform_weakmap.set(user, sm_link);
    }
}

ฟังก์ชันเพื่อพิมพ์เฉพาะผู้ใช้ที่มีอยู่ใน SMP ที่กำหนด

/**
Prints the User's fullname and corresponding SMP link of only those Users which are on the given SMP.
*/
var printSMPUsers = (sm_platform) => {
    let sm_platform_weakmap = getSMPWeakMap(sm_platform);
    console.log(`Users of ${sm_platform}:`)
    users.forEach((user)=>{
        if(sm_platform_weakmap.has(user)) {
            console.log(`\t${user.fullname} : ${sm_platform_weakmap.get(user)}`)
        }
    });
}

ตอนนี้คุณสามารถเพิ่มลิงก์ SMP สำหรับผู้ใช้รวมถึงความเป็นไปได้ของผู้ใช้แต่ละรายที่มีลิงก์บน SMP หลายรายการ

... ดำเนินการตามตัวอย่างก่อนหน้านี้ฉันเพิ่มลิงก์ SMP ให้กับผู้ใช้ลิงก์หลายรายการสำหรับผู้ใช้ Bill และ Sarah จากนั้นพิมพ์ลิงก์สำหรับแต่ละ SMP แยกจากกัน: การเพิ่มลิงก์ SMP ให้กับผู้ใช้และแสดง

ตอนนี้บอกว่าผู้ใช้จะถูกลบออกจากแผนที่โดยการเรียกusers deleteUserที่ลบการอ้างอิงไปยังวัตถุผู้ใช้เท่านั้น สิ่งนี้ก็จะลบลิงก์ SMP ออกจาก SMP WeakMaps ใด ๆ / ทั้งหมด (โดยการรวบรวมขยะ) เช่นเดียวกับที่ไม่มีออบเจ็กต์ผู้ใช้ไม่มีวิธีเข้าถึงลิงก์ SMP ใด ๆ

... ดำเนินการตามตัวอย่างฉันลบบิลผู้ใช้แล้วพิมพ์ลิงก์ของ SMP ที่เขาเชื่อมโยงด้วย:

การลบบิลผู้ใช้จากแผนที่จะลบลิงก์ SMP ด้วย

ไม่มีข้อกำหนดของรหัสเพิ่มเติมใด ๆ ในการลบลิงก์ SMP แยกต่างหากและรหัสที่มีอยู่ก่อนที่คุณสมบัตินี้จะไม่ได้รับการแก้ไข

หากมีวิธีอื่นในการเพิ่มคุณสมบัตินี้ด้วย / ไม่มี WeakMaps โปรดแสดงความคิดเห็น


_____nice______
Aleks
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.