前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >js 数组去除重复数据-5 个提升你 JS 编码水平的实例

js 数组去除重复数据-5 个提升你 JS 编码水平的实例

作者头像
囍楽云
发布2022-12-26 15:43:43
1.6K0
发布2022-12-26 15:43:43
举报
文章被收录于专栏:囍楽云博客

•作者:陈大鱼头•github:1

  虽然 2020 的今天,各种前端框架、工具林立,而这些框架跟工具也帮我们提前解决了不少麻烦的问题,但是工具始终是工具,扎实的基本功才是最核心的,现在一起来通过几个实际的代码片段来提高我们原生 JS 的编码水平。

  判断数据类型

  首先来提问一个:typeof是否能正确判断类型?

  答案是:不可以,因为由于历史原因,在判断原始类型时,typeof null会等于object。而且对于对象来说,除了函数,都会转换成object。例子如下:

代码语言:javascript
复制
typeof 1; // 'number'`typeof "1"; // 'string'typeof null; //    typeof []; // 'object'typeof {}; // 'object'`typeof window.alert; // 'function'

  再来提问一个,是否能正确判断类型?

  答案是:还是不可以,虽然是通过原型链来判断的,但是对于对象来说,Array也会被转换成Object,而且也不能区分基本类型string和。例如:

代码语言:javascript
复制
function Func() {}`const func = new Func();console.log(func instanceof Func); // true    const obj = {};const arr = [];obj instanceof Object; // truearr instanceof Object; // truearr instanceof Array; // true    const str = "abc";const str2 = new String("abc");str instanceof String; // falsestr2 instanceof String; // true`

  所以该怎么办呢?

  这时候我们可以使用:Object...call()

  所以为什么?

  因为每个对象都有一个()方法,当要将对象表示为文本值或以预期字符串的方式引用对象时,会自动调用该方法。默认情况下,从Object派生的每个对象都会继承()方法。如果此方法未在自定义对象中被覆盖,则()返回Object type,其中type是对象类型。所以就有以下例子:

代码语言:javascript
复制
Object.prototype.toString.call(new Date()); // [object Date]`Object.prototype.toString.call("1"); // [object String]Object.prototype.toString.call(1); // [object Numer]Object.prototype.toString.call(undefined); // [object Undefined]`Object.prototype.toString.call(null); // [object Null]

  所以综合上述知识点,我们可以封装出以下通用类型判断方法:

代码语言:javascript
复制
var type = function(data) {`      var toString = Object.prototype.toString;      var dataType = data instanceof Element                          ? 'element' // 为了统一DOM节点类型输出                          : toString                              .call(data)                              .replace(/\[object\s(.+)\]/, ''$1')                              .toLowerCase()      return dataType}`

  使用方法如下:

代码语言:javascript
复制
type("a"); // string`type(1); // numbertype(window); // windowtype(document.querySelector("h1")); // element`

  通用的数组/类数组对象封装

  如果我们使用 ES5/ES6+的数组 API,很容易就能够对数组进行各类的循环操作,但是如果我们要循环一个类数组对象呢?

  例如。直接循环是会报错的:

代码语言:javascript
复制
document.querySelectorAll("div").map(e => e); // Uncaught TypeError: document.querySelectorAll(...).map is not a function

  当然我们可以用扩展运算符:

代码语言:javascript
复制
[...document.querySelectorAll("div")].map(e => e);

  那如果我们不用扩展运算符呢?

  那么我们就可以利用call的特性,将里的元素一个一个的插入到数组中,例子如下:

代码语言:javascript
复制
var listMap = function(array, type, fn) {`  return !fn ? array : Array.prototype[type]"call";`};

  使用方法如下:

代码语言:javascript
复制
var divs = document.querySelectorAll("div");`listMap(divs, "forEach", function(e) {  console.log(e);});`

  获取 dom 元素节点的偏移量

  如果有用过jQuery的童鞋,就一定不会忘记$('').offset()这个 api 的强大功能,这个 api 可以轻易获取元素的偏移量,那么如果我们不用jQuery该怎么实现呢?

  我们先来看看例子:

代码语言:javascript
复制
var getOffset = function(el) {`  var scrollTop =    el.getBoundingClientRect().top +    document.body.scrollTop +    document.documentElement.scrollTop;  var scrollLeft =    el.getBoundingClientRect().left +    document.body.scrollLeft +    document.documentElement.scrollLeft;  return {    top: scrollTop,    left: scrollLeft  };};`

  首先我们先来看t()这个方法。

  t()方法返回元素的大小及其相对于视口的位置。返回值是一个对象,是与该元素相关的 CSS 边框集合 。

  然后就是.body.跟..这两个是一个功能,只不过在不同的浏览器下会有一个始终为 0js 数组去除重复数据,所以做了以上的兼容性处理。所以当我们做拖拽功能的时候,就可以依赖上以上属性。

  使用方法如下:

代码语言:javascript
复制
var el = document.querySelector(".moveBox");`getOffset(el); // {top: xxx, left: xxx}`

  我们可以看上面的摇杆效果,这里就是利用了offset()去做位置判断。具体实现代码可以看:

  Fade 特效

代码语言:javascript
复制
// Fade in`var fadeIn = function (el) {    el.style.opacity = 0    var last = +new Date()    var tick = function() {        el.style.opacity = +el.style.opacity + (new Date() - last) / 400        last = +new Date()        if (+el.style.opacity < 1) {            requestAnimationFrame(tick))        }    }    tick()}// Fade outvar fadeOut = function (el) {    el.style.opacity = 1    var last = +new Date()    var tick = function() {        el.style.opacity = +el.style.opacity - (new Date() - last) / 400        last = +new Date()        if (+el.style.opacity > 0) {            requestAnimationFrame(tick)        }    }    tick()}`

  上述是淡入淡出效果的具体实现,这里是利用e对通过递归的方式进行修改。

  其实这里需要提一个概念,就是时间分片。

  这是一个非常重要的概念,例如React的Fiber核心实现就是时间分片。它会将一个长任务切分成一个含有若干小任务的任务队列,然后一个接着一个的执行。

  e就是这样一个 API,它可以根据系统来决定回调函数的执行时机,其实也就是在下一次重绘之前更新动画帧,因为有这样的机制,所以能防止丢帧。

  利用队列的概念进行数据操作

  队列(queue),是先进先出(FIFO, First-In-First-Out)的线性表。在具体应用中通常用链表或者数组来实现。队列只允许在后端(称为 rear)进行插入操作,在前端(称为 front)进行删除操作。

  虽然很多人觉得了解数据结构对前端作用不大,但是如果我们懂一些基础的概念,是否在编码时能够更加扩散我们的思维呢?我们看下面两个例子:

  获取节点在该父节点下的坐标。

  如果我们要操作原生 DOM,那么是绕不开获取节点在该父节点的下标的这个功能的,那么我们该如何实现呢?

  当然就是利用我们的循环啦,对子元素集合进行遍历js 数组去除重复数据,直到确定下标为止,代码如下:

代码语言:javascript
复制
var index = function(el) {`  if (!el) {    return -1;  }  var i = 0;  while ((el = el.previousElementSibling)) {    i++;  }  return i;};`

  清空子节点

  如果我们要清空某个 DOM 节点的子节点,我们有以下的方法:

代码语言:javascript
复制
var empty = function(el) {`  while (el.firstChild) {    el.removeChild(el.firstChild);  }`};

  上面只是提供一个思路,其实el. = ''会更简洁。

  利用 reduce 进行数据优化数组去重

  没错,又是一个老生常谈的问题,数组去重,但是我们这次去除的不仅仅是单个的数据,而是拥有某个相同键值的对象集合。例如下面的例子,我们有以下的数据:

  牛逼的 reduce数据去重

  首先我们来看看一个老生常谈的问题,我们假设有这样的一个对象:

代码语言:javascript
复制
const data = [`  {    name: "Kris",    age: "24"  },  {    name: "Andy",    age: "25"  },  {    name: "Kitty",    age: "25"  },  {    name: "Andy",    age: "25"  },  {    name: "Kitty",    age: "25"  },  {    name: "Andy",    age: "25"  },  {    name: "Kitty",    age: "25"  }];`

  现在我们要去重里面name重复的对象,这时候我们可以利用reduce,例子如下:

代码语言:javascript
复制
const dataReducer = (prev, cur, idx) => {`  let obj = {};  const { name } = cur;  obj[name] = cur;  return {    ...prev,    ...obj  };};const reducedData = data.reduce(dataReducer, {});`let newData = Object.values(reducedData);

  批量生成对象元素

  在鱼头的实际业务中,有一个操作是需要对类似以下的对象进行操作的:

代码语言:javascript
复制
{`    a1: 'data',    a2: 'data',    ...,    an: 'data'}`

  像我这么懒的鱼,肯定不会一个个手写,所以就有了以下方法

代码语言:javascript
复制
const createList = (item, idx) => {`  let obj = {};  obj[`a${idx}`] = "data";  return obj;};const listReducer = (acc, cur) => (!acc ? { ...cur } : { ...cur, ...acc });`const obj = Array.from(new Array(20), createList).reduce(listReducer);

本文共 1391 个字数,平均阅读时长 ≈ 4分钟

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档