ES6笔记 16.WeakMap与WeakSet、proxy与reflect

Map 数据类型结构的转换

map 对象转为数组

const map = new Map();
map.set(true, 7).set({ foo: 3 }, ["abc"]);
console.log(map);
console.log([...map]); // [Array(2)[true, 2], Array(2)[{foo: 3}, ["abc"]]

数组转为 Map 对象

const arr = [
  [true, 7],
  [{ foo: 3 }, ["abc"]],
];
const map = new Map(arr);
console.log(map); // Map(2) {true => 7, {…} => Array(1)}

Map 转变为对象

const map = new Map();
map.set(true, 7).set({ foo: 3 }, ["abc"]);
function mapToObj(strMap) {
  const obj = {};
  for (let [key, value] of strMap) {
    obj[key] = value;
  }
  return obj;
}
const res = mapToObj(map);
console.log(res); // {true: 7, [object Object]: Array(1)}

对象转为 Map 对象

const obj = { true: 7, no: false };
function objToMap(obj) {
  const map = new Map();
  for (let key of Object.keys()) {
    map.set(key, obj[key]);
  }
  return map;
}
const res = objToMap(obj);
console.log(res);

Map、Set 与其它数据类型的对比

1. Map 对比 Array

Map 对比 Array,Map 无论从增删改查的方面都要比 Array 的操作简便,而且 Map 的数据结构更加的清晰明了,所以能够使用 Map 的时候尽量使用 Map 数据结构;

// Map
// 声明方式
let map = new Map();
// 增加元素
map.set("foo", "bar");
// 查找元素
let map_exist = map.has("foo");
// 更新元素
map.set("t", 2);
// 删除元素
map.delete("t");

// Array
// 声明方式
let arr = new Array();
// 增加元素
arr.push({ foo: "bar" });
// 查找元素
let arr_exist = arr.find((elem) => elem["foo"]);
// 更新元素
arr.forEach((elem) => (elem["t"] ? elem["t"] * 2 : ""));
// 删除元素
let index = arr.findIndex((elem) => elem["t"]);
arr.splice(index, 1); // 删除元素

2. Set 对比 Array

Set 数据结构类似 Array 数据结构,但是对数据结构具有唯一性的要求,就要使用 Set 进行操作

// Set
// 声明方式
let set = new Set();
// 增加元素
set.add({ foo: "bar" });
// 查找元素
let set_exist = set.has("foo");
// 更新元素
set.add("foo", "goo");
// 删除元素
set.forEach((elem) => (elem["foo"] ? set.delete(elem) : ""));

// Array
// 声明方式
let arr = new Array();
// 增加元素
arr.push({ foo: "bar" });
// 查找元素
let arr_exist = arr.find((elem) => elem["foo"]);
// 更新元素
arr.forEach((elem) => (elem["foo"] ? (elem["foo"] = "goo") : ""));
// 删除元素
let index = arr.findIndex((elem) => elem["foo"]);
arr.splice(index, 1);

3. Map、Set 与 Object 对比

// Map
// 声明方式
let map = new Map();
// 增加元素
map.set("foo", "bar");
// 查找元素
let map_exist = map.has("foo");
// 修改元素
map.set("t", 2);
// 删除元素
map.delete("t");

// Set
// 声明方式
let set = new Set();
// 增加元素
set.add({ foo: "bar" });
// 查找元素
let set_exist = set.has("foo");
// 修改元素
set.add("foo", "goo");
// 删除元素
set.delete("t");

// Object
// 声明方式
let obj = {};
// 增加元素
obj["foo"] = "bar";
// 查找元素
let obj_exist = Object.hasOwnProperty("foo");
// 修改元素
obj["foo"] = 2;
// 删除元素
delete obj["t"];

WeakSet、WeakMap 数据结构

  1. 不存在遍历的方法
  2. 如果使用 WeakMap 和 WeakSet 来存储,它的成员只能是对象
  3. WeakMap 和 WeakSet 是弱映射,weak 表示弱弱的拿着的意思,这些键不属于正式的引用,不会阻止垃圾回收。但是要注意点是,弱映射中值的引用可不是“弱弱的拿着”的。只要键存在,键/值对就会存在映射中,并被当做值的引用,因此就不会被当做垃圾回收。

WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其它对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象是否还存在 WeakSet 结构;
垃圾回收机制根据对象的可达性(reachability)来判断回收,如果 WeakSet 中的对象还能被访问到,垃圾回收机制就不释放这块内存。可能导致内存不释放,内存泄露的问题。WeakSet 里面的对象引用,都不计入垃圾回收机制,所以不存在内存泄漏的问题;因此,WeakSet 适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失;
WeakSet 的成员是不适合引用的,因为它会随时消失。另外,由于 WeakSet 内部有多少个成员,取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不一样的,而垃圾回收机制何时运行是不可预测的,因此 ES6 规定 WeakSet 不可遍历。

WeakMap 它的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此,只要所引用的对象的其它引用都被清除,垃圾回收机制就会释放对该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对就会自动消失,不用手动删除引用;
WeakMap 的专用场合就是,它的键所对应的对象,可能会在将来消失;WeakMap 结构有助于防止内存泄漏,WeakMap 弱引用只是键名,而不是键值。键值依旧正常引用;

例如下面的例子,我们如果移除了 el、e2 节点,事件处理函数 handleClick1、hanldeClick2 是引用值,现在虽然用不上了,但是没有解除引用,所以我们需要手动的解除引用,这样才能够让 JS 的垃圾回收机制,将这两个函数进行回收。

const e1 = document.getElementsByTagName("div")[0],
  e2 = document.getElementsByTagName("div")[1];

e1.addEventListener("click", handleClick1, false);
e2.addEventListener("click", hanldeClick2, false);

function handleClick1() {}
function handleClick2() {}

// 如果移除节点
e1.remove();
e2.remove();

// 手动删除函数引用
handleClick1 = null;
handleClick2 = null;

如果用 WeakMap 进行改善代码呢?因为 WeakMap 的键名是类型,所以我们可以直接在 WeakMap 中建立映射的关系。例如下面的例子:
现在我们就不需要手动删除函数引用了,因为 WeakMap 的键名是弱引用类型,当我们移除节点时,WeakMap 中的键名对象 e1、e2 失去引用,此时会自动被垃圾回收机制回收,而对应的 handleClick1、handleClick2 也会被垃圾回收机制一起回收。

const e1 = document.getElementsByTagName("div")[0],
  e2 = document.getElementsByTagName("div")[1];

const hashMap = new WeakMap();
hashMap.set(e1, handleClick1);
hashMap.set(e2, handleClick2);

e1.addEventListener("click", hashMap.get(e1), false);
e2.addEventListener("click", hashMap.get(e2), false);

function handleClick1() {}
function handleClick2() {}

// 直接移除节点
e1.remove();
e2.remove();

proxy(代理模式)

代理模式:在访问目标对象时,设置目标对象之间设置一层拦截层,当外界访问对象的时候,必须通过拦截层的拦截;从而实现对源对象基本操作的拦截和定义(如属性查找、赋值、枚举、函数调用等)

const p = new Proxy(target,handler);
参数 1 target:要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组、函数、甚至是另一个代理);
参数 2 handler:一个通常以函数作为属性的对象,各种函数分别定义了在执行各种操作时 Proxy 的拦截行为;(捕捉器)

let star = { name: "shaoyahu", age: 18, phone: "12345" };
// let agent = new Proxy(target, handle);
let agent = new Proxy(star, {
  // get 拦截获取操作
  get: function (target, key) {
    if (key === "phone") {
      return "agent: 1383838438";
    }
    if (key === "price") {
      return 120000;
    }
    return target[key];
  },
  // set 拦截赋值操作
  set: function (target, key, value) {
    if (value < 100000) {
      throw new Error("价格太低");
    } else {
      target[key] = value;
      return true;
    }
  },
  // has 拦截 in 操作符 --> 'age' in agent
  // 但是无法拦截 for in 方法
  has: function (target, key) {
    console.log("请联系agent");
    if (key === "customPrice") {
      return target[key]; // 默认返回布尔值,设置没有用
    } else {
      return false;
    }
  },
});
console.log(agent.phone); // agent: 1383838438
console.log(agent.price); // 120000
console.log(agent.name); // shaoyahu
agent.customPrice = 150000;
console.log(agent.customPrice); // 150000
console.log("customPrice" in agent); // 请联系agent true
agent.hobby = "sing";
console.log(agent["hobby"]); // sing

Reflect(反映,映出)

var obj = {
  a: 1,
  b: 2,
  c: 3,
};

console.log(Reflect.get(obj, "a")); // 1
console.log(Reflect.set(obj, "b", 10)); // true
console.log(Reflect.has(obj, "a")); // true

console.log(obj); // {a: 1, b: 10, c: 3}

Reflect 目的意义

将 Object 对象的一个明显属于 JS 语言内部的方法(比如 Object.defineProperty),放到 Reflect 对象上。现阶段,某些方法同时在 Object 和 Relect 对象上部署,未来的新方法将只部署在 Reflect 对象上。也就是说,从 Reflect 对象上可以拿到语言内部的方法。
修改某些 Object 返回的结果,让其变的合理性。比如 Object.defineProperty(obj,propKey,desc)在无法定义属性时,会抛出一个错误,而 Reflect.defineProperty(obj,name,desc)则会返回 false;

// 旧写法
try {
  Object.defineProperty(target, property, attributes);
} catch (e) {}

// 新写法
if (Reflect.defineProperty(target, property, attributes)) {
} else {
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值