이 글은 Vue3의 WeakMap을 이해하고 Vue 3 반응형 소스 코드에서 WeakMap이 "캐시 영역"으로 사용되는 이유를 소개합니다. 모두에게 도움이 되기를 바랍니다.
【관련 추천: "vue.js Tutorial"】
코드의 Vue 3 반응 원리 부분을 읽다가 반응형 처리를 수행할 때 각 개체 WeakMap에 대해 를 사용하는 것을 보았습니다. code>는 다음 코드를 사용하여 "캐시 영역"을 생성합니다. <code>WeakMap
创建了一个「缓存区」,代码如下:
// 注意下面这句代码! const reactiveMap = new WeakMap(); // 核心进行劫持的方法 处理 get 和 set 的逻辑 const mutableHandlers = { get, set } function reactive(target: object) { return createReactiveObject(target, mutableHandlers, reactiveMap); } /** * @description 创建响应式对象 * @param {Object} target 需要被代理的目标对象 * @param {Function} baseHandlers 针对每种方式对应的不同处理函数 * @param {Object} proxyMap WeakMap 对象 */ function createReactiveObject(target, baseHandlers, proxyMap) { // 检测 target 是不是对象,不是对象直接返回,不进行代理 if (!isObject(target)) { return target } const existsProxy = proxyMap.get(target); // 如果该对象已经被代理过了,则直接返回,不进行重复代理 if (existsProxy) { return existsProxy } // 未被代理过,则创建代理对象 const proxy = new Proxy(target,baseHandlers); // 缓存,避免重复代理,即避免 reactive(reactive(Object)) 的情况出现 proxyMap.set(target,proxy); return proxy }
从上面的代码可以看出,WeakMap
缓存区的作用就是用来防止对象被重复代理。
为什么 Vue 3 使用 WeakMap
来缓存代理对象?为什么不使用其他的方式来进行缓存,比如说 Map
?
什么是 WeakMap
WeakMap
对象是一组键值对的集合,其中的键是 弱引用 的。其键必须是 对象,而值可以是任意的。
语法
new WeakMap([iterable])
Iterable
是一个数组(二元数组)或者其他可迭代的且其元素是键值对的对象。每个键值对会被加到新的 WeakMap
里。
方法
WeakMap
有四个方法:分别是 get
、set
、has
、delete
,下面我们看一下其大致的用法:
const wm1 = new WeakMap(), wm2 = new WeakMap(), wm3 = new WeakMap(); const o1 = {}, o2 = function() {}, o3 = window; wm1.set(o1, 37); wm1.set(o2, "azerty"); wm2.set(o1, o2); // value 可以是任意值,包括一个对象或一个函数 wm2.set(o3, undefined); wm2.set(wm1, wm2); // 键和值可以是任意对象,甚至另外一个 WeakMap 对象 wm1.get(o2); // "azerty" wm2.get(o2); // undefined,wm2 中没有 o2 这个键 wm2.get(o3); // undefined,值就是 undefined wm1.has(o2); // true wm2.has(o2); // false wm2.has(o3); // true (即使值是 undefined) wm3.set(o1, 37); wm3.get(o1); // 37 wm1.has(o1); // true wm1.delete(o1); wm1.has(o1); // false
为什么要用 WeakMap
而不是 Map
在 JavaScript 里,map
API 可以通过四个 API 方法共用两个数组(一个存放键,一个存放值)来实现。这样在给这种 map
设置值时会同时将键和值添加到这两个数组的末尾。从而使得键和值的索引在两个数组中相对应。当从该 map
取值的时候,需要遍历所有的键,然后使用索引从存储值的数组中检索出相应的值。
但这样的实现会有两个很大的缺点,首先赋值和搜索操作都是 O(n)
的时间复杂度(n
是键值对的个数),因为这两个操作都需要遍历整个数组来进行匹配。
另外一个缺点是可能会导致 内存泄漏,因为数组会一直引用着每个键和值。这种引用使得 垃圾回收算法不能回收处理他们,即使没有其他任何引用存在了。
let jser = { name: "dachui" }; let array = [ jser ]; jser = null; // 覆盖引用
上面这段代码,我们把一个对象放入到数组中,那么只要这个数组存在,那么这个对象也就存在,即使没有其他对该对象的引用。
let jser = { name: "dachui" }; let map = new Map(); map.set(jser, ""); jser = null; // 覆盖引用
类似的,如果我们使用对象作为常规 Map
的键,那么当 Map
存在时,该对象也将存在。它会占用内存,并且不会被垃圾回收机制回收。
相比之下,原生的 WeakMap
持有的是每个键对象的 弱引用,这意味着在没有其他引用存在时垃圾回收能正确进行。
正是由于这样的弱引用,WeakMap
的 key
是不可枚举的 (没有方法能给出所有的 key
)。如果 key
是可枚举的话,其列表将会受垃圾回收机制的影响,从而得到不确定的结果。因此,如果你想要这种类型对象的 key
值的列表,你应该使用 Map
。
综上,我们可以得出以下结论:WeakMap 的键所指向的对象,不计入垃圾回收机制。
所以,如果你要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap
。
看到这里大家就应该知道了,Vue 3 之所以使用 WeakMap
来作为缓冲区就是为了能将 不再使用的数据进行正确的垃圾回收。
什么是弱引用
关于「弱引用」,维基百科给出了答案:
在计算机程序设计中,弱引用 与 强引用 相对,是指不能确保其引用的对象不会被垃圾回收器回收的引用。一个对象若只被弱引用所引用,则被认为是不可访问(或弱可访问)的,并因此 可能在任何时刻被回收。
为什么会出现弱引用
那么,为什么会出现弱引用呢?弱引用除了能解决上述问题之外还能解决什么问题呢?要想回答这些问题,我们首先需要了解一下 V8
引擎是如何进行垃圾回收的。
对于 JSer
来说,内存的管理是自动的、无形的,这一切都归功于 V8
引擎在背后默默地帮我们找到不需要使用的内存并进行清理。
那么,当我们不再需要某个东西时会发生什么,V8
const myWeakmap = newWeakMap(); myWeakmap.set( document.getElementById('logo'), { timesClicked: 0 }, ); document.getElementById('logo').addEventListener('click', () => { const logoData = myWeakmap.get(document.getElementById('logo')); logoData.timesClicked++; }, false);위 코드에서 볼 수 있듯이
WeakMap
캐시 영역의 목적은 객체가 반복적으로 프록시되는 것을 방지하는 것입니다. 🎜🎜Vue 3가 프록시 객체를 캐시하기 위해 WeakMap
을 사용하는 이유는 무엇입니까? Map
과 같은 다른 캐시 방법을 사용해 보는 것은 어떨까요? 🎜WeakMap이란 무엇입니까
🎜WeakMap
개체는 키-값 쌍의 모음이며, 여기서 키는 약한 참조입니다. 키는 객체여야 하며 값은 임의적일 수 있습니다. 🎜구문
const testFn = (function () { let data; class Test { constructor(val) { data = val } getData() { return data; } } return Test; })(); let test1 = new testFn(3); let test2 = new testFn(4); console.log(test1.getData()); // 4 console.log(test2.getData()); // 4🎜
Iterable
은 배열(요소 2개 배열) 또는 요소가 키-값 쌍인 기타 반복 가능한 객체입니다. 각 키-값 쌍은 새로운 WeakMap
에 추가됩니다. 🎜메서드
🎜WeakMap
에는 get
, set
, has
>, 네 가지 메서드가 있습니다. delete
, 일반적인 사용법을 살펴보겠습니다. 🎜const testFn = (function () { let data = Symbol('data') class Test { constructor(val) { this[data] = val } getData() { return this[data] } } return Test; })(); let test1 = new testFn(3); let test2 = new testFn(4); console.log(test1.getData()); // 3 console.log(test2.getData()); // 4 console.log(test1[Object.getOwnPropertySymbols(test1)[0]]); // 3 console.log(test2[Object.getOwnPropertySymbols(test2)[0]]); // 4
Map
대신 WeakMap
을 사용하는 이유 🎜JavaScript에서 , map
API는 두 개의 배열(하나는 키용, 다른 하나는 값용)을 공유하는 4개의 API 메소드를 통해 구현될 수 있습니다. 이는 map
에 값을 설정할 때 두 배열의 끝에 키와 값을 모두 추가합니다. 이렇게 하면 키와 값 인덱스가 두 배열에서 일치하게 됩니다. 이 맵
에서 값을 가져올 때 모든 키를 반복한 다음 인덱스를 사용하여 값이 저장된 배열에서 해당 값을 검색해야 합니다. 🎜🎜그러나 이러한 구현에는 두 가지 큰 단점이 있습니다. 첫째, 할당 및 검색 작업에는 O(n)
(n
은 키-값 쌍 번호)의 시간 복잡도가 있습니다. ), 두 작업 모두 일치시키기 위해 전체 배열을 순회해야 하기 때문입니다. 🎜🎜또 다른 단점은 배열이 항상 각 키와 값을 참조하기 때문에 메모리 누수를 일으킬 수 있다는 것입니다. 이러한 참조는 다른 참조가 없더라도 가비지 수집 알고리즘이 해당 참조를 재활용하는 것을 방지합니다. 🎜const testFn = (function () { let data = new WeakMap() class Test { constructor(val) { data.set(this, val) } getData() { return data.get(this) } } return Test; })(); let test1 = new testFn(3); let test2 = new testFn(4); console.log(test1.getData()); // 3 console.log(test2.getData()); // 4🎜위 코드에서는 객체를 배열에 넣은 다음 배열이 존재하는 한 객체에 대한 다른 참조가 없더라도 객체도 존재하게 됩니다. 🎜rrreee🎜마찬가지로 개체를 일반
Map
의 키로 사용하면 Map
이 존재할 때 개체도 존재하게 됩니다. 메모리를 차지하며 가비지 수집 메커니즘에 의해 회수되지 않습니다. 🎜🎜반면, 네이티브 WeakMap
은 각 키 객체에 대한 약한 참조를 보유합니다. 이는 다른 참조가 없을 때 가비지 수집이 올바르게 진행될 수 있음을 의미합니다. 🎜🎜WeakMap
의 key
가 열거 가능하지 않은 것은 바로 이러한 약한 참조 때문입니다(모든 key
를 제공할 수 있는 메서드는 없습니다). key
가 열거 가능한 경우 해당 목록은 가비지 수집 메커니즘의 영향을 받아 정의되지 않은 결과가 발생합니다. 따라서 이러한 유형의 객체에 대한 키
값 목록을 원할 경우 Map
을 사용해야 합니다. 🎜🎜요약하면 다음과 같은 결론을 내릴 수 있습니다. WeakMap의 키가 가리키는 객체는 가비지 수집 메커니즘에서 계산되지 않습니다. 🎜🎜따라서 객체에 데이터를 추가하고 가비지 수집 메커니즘을 방해하고 싶지 않다면 WeakMap
을 사용할 수 있습니다. 🎜🎜이 내용을 보신 분들은 모두 아시겠지만, Vue 3가 WeakMap
을 버퍼로 사용하는 이유는 더 이상 사용되지 않는 데이터를 올바르게 가비지 수집하기 위한 것입니다. 🎜약한 참조란 무엇입니까
🎜 "약한 참조"에 대해 Wikipedia에서는 다음과 같이 대답합니다. 🎜컴퓨터 프로그래밍에서는 약한 참조 및 강한 참조 Strong>relative는 참조하는 개체가 가비지 수집기에 의해 수집되지 않는다는 것을 보장하지 않는 참조를 나타냅니다. 객체가 약한 참조에 의해서만 참조되는 경우 객체는 액세스할 수 없는(또는 약한 액세스) 것으로 간주되므로 언제든지 재활용될 수 있습니다.
약한 참조는 왜 나타나는 걸까요?
🎜그럼 약한 참조는 왜 나타나는 걸까요? 위의 문제를 해결하는 것 외에도 약한 참조로 해결할 수 있는 다른 문제는 무엇입니까? 이러한 질문에 답하려면 먼저V8
엔진이 가비지 수집을 수행하는 방식을 이해해야 합니다. 🎜🎜JSer
의 경우 메모리 관리는 자동으로 눈에 보이지 않습니다. V8
엔진 덕분에 은밀하게 뒤에서 사용되지 않는 메모리를 찾아 정리하도록 도와줍니다. 🎜🎜그렇다면 더 이상 필요하지 않은 것이 있으면 어떻게 되며, V8
엔진은 이를 어떻게 찾아서 정리할까요? 🎜现在各大浏览器通常用采用的垃圾回收有两种方法,一种是「引用计数」,另外一种就是「标记清除」。下面我们来看一下:
标记清除
标记清除被称为 mark-and-sweep
,它是基于 可达性 来判断对象是否存活的,它会定期执行以下「垃圾回收」步骤:
垃圾收集器找到所有的根,并标记(记住)它们。
然后它遍历并标记来自它们的所有引用。所有被遍历到的对象都会被记住,以免将来再次遍历到同一个对象。
……如此操作,直到所有可达的(从根部)引用都被访问到。
没有被标记的对象都会被删除。
我们还可以将这个过程想象成从根溢出一个巨大的油漆桶,它流经所有引用并标记所有可到达的对象,然后移除未标记的。
引用计数
引用计数方式最基本的形态就是让每个被管理的对象与一个引用计数器关联在一起,该计数器记录着该对象当前被引用的次数,每当创建一个新的引用指向该对象时其计数器就加 1,每当指向该对象的引用失效时计数器就减 1。当该计数器的值降到 0 就认为对象死亡。
区别
引用计数与基于「可达性」的标记清除的内存管理方式最大的区别就是,前者只需要 局部的信息,而后者需要 全局的信息。
在引用计数中每个计数器只记录了其对应对象的局部信息 —— 被引用的次数,而没有(也不需要)一份全局的对象图的生死信息。
由于只维护局部信息,所以不需要扫描全局对象图就可以识别并释放死对象。但也因为缺乏全局对象图信息,所以 无法处理循环引用 的状况。
所以,更高级的引用计数实现会引入 弱引用 的概念来打破某些已知的循环引用。
WeakMap 应用
存储 DOM 节点
WeakMap
应用的典型场合就是以 DOM
节点作为键名。下面是一个例子。
const myWeakmap = newWeakMap(); myWeakmap.set( document.getElementById('logo'), { timesClicked: 0 }, ); document.getElementById('logo').addEventListener('click', () => { const logoData = myWeakmap.get(document.getElementById('logo')); logoData.timesClicked++; }, false);
上面代码中,document.getElementById('logo')
是一个 DOM
节点,每当发生 click
事件,就更新一下状态。我们将这个状态作为值放在 WeakMap
里,对应的键就是这个节点对象。一旦这个 DOM
节点删除,该状态就会自动消失,不存在内存泄漏风险。
数据缓存
谜底就在谜面上,文章一开头我们提出的问题就是这里的答案。Vue 3 在实现响应式原理的时候就是使用了 WeakMap
来作为响应式对象的「缓存区」。
关于这一点用法也很简单,当我们需要关联对象和数据,比如在不修改原有对象的情况下储存某些属性或者根据对象储存一些计算的值等,而又不想手动去管理这些内存问题的时候就可以使用 WeakMap
。
部署类中的私有属性
WeakMap
的另一个用处是部署类中的私有属性。
值得一提的是,TypeScript 中已经实现的 private
私有属性原理就是利用 WeakMap
。
私有属性应该是不能被外界访问到,不能被多个实例共享,JavaScript 中约定俗成地使用下划线来标记私有属性和方法,一定程度来说是不靠谱的。
下面我们用三种方法来实现:
- 版本一:闭包
const testFn = (function () { let data; class Test { constructor(val) { data = val } getData() { return data; } } return Test; })(); let test1 = new testFn(3); let test2 = new testFn(4); console.log(test1.getData()); // 4 console.log(test2.getData()); // 4
可以看到最后都输出 4
,多实例共享私有属性了,所以版本一不符合。
- 版本二:Symbol
const testFn = (function () { let data = Symbol('data') class Test { constructor(val) { this[data] = val } getData() { return this[data] } } return Test; })(); let test1 = new testFn(3); let test2 = new testFn(4); console.log(test1.getData()); // 3 console.log(test2.getData()); // 4 console.log(test1[Object.getOwnPropertySymbols(test1)[0]]); // 3 console.log(test2[Object.getOwnPropertySymbols(test2)[0]]); // 4
使用 Symbol
虽然实现了而且正确输出了 3
、4
,但是我们发现可以在外界不通过 getData
方法直接拿到私有属性,所以这种方法也不满足我们的要求。
- 版本三:
WeakMap
const testFn = (function () { let data = new WeakMap() class Test { constructor(val) { data.set(this, val) } getData() { return data.get(this) } } return Test; })(); let test1 = new testFn(3); let test2 = new testFn(4); console.log(test1.getData()); // 3 console.log(test2.getData()); // 4
如上,完美解决~~
更多编程相关知识,请访问:编程入门!!
위 내용은 위크맵이란 무엇인가요? Vue3 반응형 소스 코드에서 캐시 영역으로 사용되는 이유는 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

Vue.js는 개발 효율성과 사용자 경험을 향상시키기 위해 프론트 엔드 기술 스택과 밀접하게 통합되어 있습니다. 1) 건설 도구 : 모듈 식 개발을 달성하기 위해 웹 팩 및 롤업과 통합. 2) 주 관리 : Vuex와 통합하여 복잡한 응용 프로그램 상태를 관리합니다. 3) 라우팅 : vuerouter와 통합하여 단일 페이지 응용 프로그램 라우팅을 실현합니다. 4) CSS 전 처리기 : 스타일 개발 효율성을 향상시키기 위해 SASS를 지원하고 덜 지원합니다.

Netflix는 React의 구성 요소 설계 및 가상 DOM 메커니즘이 복잡한 인터페이스와 빈번한 업데이트를 효율적으로 처리 할 수 있기 때문에 REACT를 선택했습니다. 1) 구성 요소 기반 설계를 통해 Netflix는 인터페이스를 관리 가능한 위젯으로 분류하여 개발 효율성 및 코드 유지 관리를 향상시킬 수 있습니다. 2) 가상 DOM 메커니즘은 DOM 운영을 최소화하여 Netflix 사용자 인터페이스의 부드러움과 고성능을 보장합니다.

Vue.js는 사용하기 쉽고 강력하기 때문에 개발자에게 사랑을받습니다. 1) 반응 형 데이터 바인딩 시스템은 뷰를 자동으로 업데이트합니다. 2) 구성 요소 시스템은 코드의 재사용 성과 유지 관리를 향상시킵니다. 3) 컴퓨팅 속성 및 청취자는 코드의 가독성과 성능을 향상시킵니다. 4) vuedevtools를 사용하고 콘솔 오류를 확인하는 것이 일반적인 디버깅 기술입니다. 5) 성능 최적화에는 주요 속성, 계산 된 속성 및 유지 구성 요소 사용이 포함됩니다. 6) 모범 사례에는 명확한 구성 요소 이름 지정, 단일 파일 구성 요소 사용 및 수명주기 후크의 합리적인 사용이 포함됩니다.

vue.js는 효율적이고 유지 관리 가능한 프론트 엔드 애플리케이션을 구축하는 데 적합한 점진적인 JavaScript 프레임 워크입니다. 주요 기능은 다음과 같습니다. 1. 응답 데이터 바인딩, 2. 구성 요소 개발, 3. 가상 Dom. 이러한 기능을 통해 vue.js는 개발 프로세스를 단순화하고 응용 프로그램 성능과 유지 관리를 향상시켜 현대 웹 개발에서 매우 인기가 있습니다.

vue.js와 반응은 각각 고유 한 장점과 단점이 있으며, 선택은 프로젝트 요구 사항 및 팀 조건에 따라 다릅니다. 1) vue.js는 단순하고 사용하기 쉬운 소규모 프로젝트 및 초보자에게 적합합니다. 2) REACT는 풍부한 생태계와 구성 요소 설계로 인해 대규모 프로젝트 및 복잡한 UI에 적합합니다.

vue.js는 여러 기능을 통해 사용자 경험을 향상시킵니다. 1. 응답 시스템은 실시간 데이터 피드백을 실현합니다. 2. 구성 요소 개발은 코드 재사용 성을 향상시킵니다. 3. Vuerouter는 원활한 내비게이션을 제공합니다. 4. 동적 데이터 바인딩 및 전환 애니메이션은 상호 작용 효과를 향상시킵니다. 5. 오류 처리 메커니즘은 사용자 피드백을 보장합니다. 6. 성능 최적화 및 모범 사례는 응용 프로그램 성능을 향상시킵니다.

웹 개발에서 vue.js의 역할은 개발 프로세스를 단순화하고 효율성을 향상시키는 점진적인 JavaScript 프레임 워크 역할을하는 것입니다. 1) 개발자는 반응 형 데이터 바인딩 및 구성 요소 개발을 통해 비즈니스 로직에 집중할 수 있습니다. 2) vue.js의 작동 원리는 반응 형 시스템 및 가상 DOM에 의존하여 성능을 최적화합니다. 3) 실제 프로젝트에서는 Vuex를 사용하여 글로벌 상태를 관리하고 데이터 대응 성을 최적화하는 것이 일반적입니다.

vue.js는 2014 년 Yuxi가 출시하여 사용자 인터페이스를 구축하기 위해 진보적 인 JavaScript 프레임 워크입니다. 핵심 장점은 다음과 같습니다. 1. 응답 데이터 바인딩, 데이터 변경의 자동 업데이트보기; 2. 구성 요소 개발, UI는 독립적이고 재사용 가능한 구성 요소로 분할 될 수 있습니다.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

에디트플러스 중국어 크랙 버전
작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

Dreamweaver Mac版
시각적 웹 개발 도구

MinGW - Windows용 미니멀리스트 GNU
이 프로젝트는 osdn.net/projects/mingw로 마이그레이션되는 중입니다. 계속해서 그곳에서 우리를 팔로우할 수 있습니다. MinGW: GCC(GNU Compiler Collection)의 기본 Windows 포트로, 기본 Windows 애플리케이션을 구축하기 위한 무료 배포 가능 가져오기 라이브러리 및 헤더 파일로 C99 기능을 지원하는 MSVC 런타임에 대한 확장이 포함되어 있습니다. 모든 MinGW 소프트웨어는 64비트 Windows 플랫폼에서 실행될 수 있습니다.
