文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

JavaScript怎么监测数组的变化

2023-06-20 16:30

关注

这篇文章主要讲解了“JavaScript怎么监测数组的变化”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JavaScript怎么监测数组的变化”吧!

前言

之前介绍defineProperty的时候说到,其只能监测对象的变化,并不能监测数组的变化。

本文致力于说清楚怎么实现监测数组的变化。

核心思路:找到改变原数组的方法,然后对这些方法进行劫持处理。

上面这句话,是重中之重,务必读三遍,记住了,再往下走。

改变原数组,常用到的方法有push pop shift unshift reverse sort splice。

换言之,这些方法是改变数组的入口。

在数组实例和数组原型之间,加一个新的原型

直接修改Array.prototype,是极其危险的。

换一个思路,复制已有的数组原型,然后修改其中的方法,但这里因为原型上的方法不可枚举,自然也就没法复制。

于是再换一个思路,在数组和数组的原型之间,插入一个原型,形成原型链,数组 => 新原型 => 数组的原型,可以在新原型上增加同名的方法就可以了。

先借助伪代码理解:

// 伪代码let arr = [];arr.__proto__ = newPrototype;newPrototype.__proto__ = Array.prototype;// 然后可以在新原型上添加同名的方法就可以了newPrototype.push = xxx;

转换成真实的代码如下,核心使用了Object.create,

// Object.create返回一个新对象,而来新对象的__proto__就是传进去的参数。let newPrototype = Object.create(Array.prototype);// 然后可以在新原型上添加同名的方法就可以了newPrototype.push = xxx;// 需要监测的数组,绑定新的原型就可以了let arr = [];arr.__proto__ = newPrototype;

以 push 为例,劫持 push

就是在新原型上重新写一个 push 的方法,里面执行老的 push,但除此之外还可以做点别的事。

let newPrototype = Object.create(Array.prototype);// 在新原型上添加同名pushnewPrototype.push = function(...args) {  // 语义化this  let curArr = this;  console.log("使用了push");  // 最后还是会执行原始的push  return Array.prototype.push.call(curArr, ...args);};// 需要监测的数组,绑定新的原型就可以了let arr = [];arr.__proto__ = newPrototype;// 执行push的时候,就会打印了arr.push(1);

然后其他的方法也是类似的,试着写写其他的方法

劫持其他的方法

将其他方法也一起写了,因为逻辑一样,直接遍历即可。

let newPrototype = Object.create(Array.prototype);let methods = ["push", "pop", "shift", "unshift", "reverse", "sort", "splice"];methods.forEach(method => {  newPrototype[method] = function(...args) {    console.log(`使用了${method}`);    return Array.prototype[method].call(this, ...args);  };});// 需要监测的数组,绑定新的原型就可以了let arr = [];arr.__proto__ = newPrototype;// 执行的时候,就会打印了arr.push(1);arr.pop();

数组里有数组项的话,也是需要监测的

这里数组里可能也是有数组的,需要遍历数组的每项,如果是数组的话依然需要指向新的原型。

嗯,对,用到递归了。

let newPrototype = Object.create(Array.prototype);let methods = ["push", "pop", "shift", "unshift", "reverse", "sort", "splice"];methods.forEach(method => {  newPrototype[method] = function(...args) {    console.log(`使用了${method}`);    return Array.prototype[method].call(this, ...args);  };});function observeArr(arr) {  // 既是条件限制,也是递归的终止条件  if (!Array.isArray(arr)) {    return;  }  // 整个数组指向新的原型  arr.__proto__ = newPrototype;  // 数组的每项,如果是数组,也指向新的原型。  arr.forEach(observeArr);}// 需要监测的数组,绑定新的原型就可以了let arr = [[1, 2, 3]];observeArr(arr);// 执行的时候,就会打印了arr[0].push(1);arr[1].pop();

数组新添加的项,如果是数组也需要指向新的原型

能添加元素的方法:push unshift splice。

找到新加的元素,然后是数组的也同样指向新的原型

let newPrototype = Object.create(Array.prototype);let methods = ["push", "pop", "shift", "unshift", "reverse", "sort", "splice"];methods.forEach(method => {  newPrototype[method] = function(...args) {    console.log(`使用了${method}`);    let inserted;    switch (method) {      case "push":      case "unshift":        inserted = args;        break;      case "splice":        inserted = args.slice(2);        break;      default:        break;    }    inserted && observeArr(inserted);    return Array.prototype[method].call(this, ...args);  };});function observeArr(arr) {  // 即是条件限制,也是递归的终止条件  if (!Array.isArray(arr)) {    return;  }  // 整个数组指向新的原型  arr.__proto__ = newPrototype;  // 数组的每项,如果是数组,也指向新的原型。  arr.forEach(observeArr);}// 这里可以导出去,方便别的文件使用export default observeArr;// 需要监测的数组,绑定新的原型就可以了let arr = [];observeArr(arr);let addItem = [1, 2, 3];arr.push(addItem);// 执行的时候,就会打印了addItem.push(1);addItem.pop();

综合使用 defineProperty 监测对象和数组

现在已经有了监测对象的方法,也有了监测数组的方法,将两个综合起来,就能监测数组里面的对象,对象里面的数组了。

将监测数组和监测对象的的可以单独写成一个文件,方便之后使用。

这里为了方便直接运行代码,直接放在一块了。

// 生成新的原型let newPrototype = Object.create(Array.prototype);let methods = ["push", "pop", "shift", "unshift", "reverse", "sort", "splice"];// 在新原型上面添加以上方法,实现劫持methods.forEach(method => {  newPrototype[method] = function(...args) {    console.log(`使用了${method}`);    let inserted;    switch (method) {      case "push":      case "unshift":        inserted = args;        break;      case "splice":        inserted = args.slice(2);        break;      default:        break;    }    inserted && observeArr(inserted);    return Array.prototype[method].call(this, ...args);  };});function observeArr(arr) {  // 新加!!!是对象的话,需要用对象  if (Object.prototype.toString.call(arr) === "[object Object]") {    observeObj(arr);    return;  }  if (Array.isArray(arr)) {    // 整个数组指向新的原型    arr.__proto__ = newPrototype;    // 数组的每项,如果是数组,也指向新的原型。    arr.forEach(observeArr);  }  // 不是对象或者数组的,什么都不做}function observeObj(obj) {  // 加上参数限制,必须是对象才有劫持,也是递归的终止条件  if (typeof obj !== "object" || obj == null) {    return;  }  // 新加!!!数组交给数组处理  if (Array.isArray(obj)) {    observeArr(obj);    return;  }  // 是对象的话 才开始递归  for (let key in obj) {    // 直接使用 obj.hasOwnProperty会提示不规范    if (Object.prototype.hasOwnProperty.call(obj, key)) {      observeKey(obj, key);      // 这里劫持该属性的属性值,如果不是对象直接返回,不影响      observeObj(obj[key]);    }  }  return obj;}function observeKey(obj, key) {  let value = obj[key];  Object.defineProperty(obj, key, {    get() {      console.log("读取属性", value);      return value;    },    set(newValue) {      console.log("设置属性", newValue);      value = newValue;    }  });}let data = { a: 1, b: [1, 2, { c: 2 }] };observeObj(data);data.a = 2;data.b.push([2, 3]);let arr = [{ a: "数组里的对象" }, 3, 4];observeArr(arr);arr[0].a = 3;

缺陷

当然数组其实可以不通过方法改变,比如直接删除数组可以直接使用 length 属性,或者直接arr[0]=xxx改变数组。

但只有当使用"push","pop", "shift","unshift","reverse","sort","splice"才能检测到数组变化。

这也是 vue 的缺陷,当然新版的 proxy 将干掉这个缺陷。

所以在使用 vue 的过程中,要尽量使用以上方法操作数组~~~

附注:查看数组的所有属性和方法

在控制台可以输入dir([]),然后能看到数组所有的属性和方法。

具体用法,可以直接到到mdn 上细看,点击侧边栏看对应的方法

JavaScript怎么监测数组的变化

感谢各位的阅读,以上就是“JavaScript怎么监测数组的变化”的内容了,经过本文的学习后,相信大家对JavaScript怎么监测数组的变化这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯