5个提高你JS编码水平的实例

虽然 2020 的今天,各类前端框架、工具林立,而这些框架跟工具也帮咱们提早解决了很多麻烦的问题,可是工具始终是工具,扎实的基本功才是最核心的,如今一块儿来经过几个实际的代码片断来提升咱们原生 JS 的编码水平。javascript

判断数据类型

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

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

typeof 1 // 'number'
typeof "1" // 'string'
typeof null //

typeof [] // 'object'
typeof {} // 'object'
typeof window.alert // 'function'
复制代码

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

答案是:仍是不能够,虽然instanceof是经过原型链来判断的,可是对于对象来讲,Array也会被转换成Object,并且也不能区分基本类型stringboolean。例如:github

function Func() {}
const func = new Func()
console.log(func instanceof Func) // true

const obj = {}
const arr = []
obj instanceof Object // true
arr instanceof Object // true
arr instanceof Array // true

const str = "abc"
const str2 = new String("abc")
str instanceof String // false
str2 instanceof String // true
复制代码

因此该怎么办呢?后端

这时候咱们可使用:Object.prototype.toString.call()api

因此为何?数组

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

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]
复制代码

因此综合上述知识点,咱们能够封装出如下通用类型判断方法:前端框架

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
};
复制代码

使用方法以下:

type("a") // string
type(1) // number
type(window) // window
type(document.querySelector("h1")) // element
复制代码

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

若是咱们使用 ES5/ES6+的数组 API,很容易就可以对数组进行各种的循环操做,可是若是咱们要循环一个类数组对象呢?

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

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

固然咱们能够用扩展运算符:

[...document.querySelectorAll("div")].map(e => e)
复制代码

那若是咱们不用扩展运算符呢?

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

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

使用方法以下:

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

获取 dom 元素节点的偏移量

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

咱们先来看看例子:

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
      }
}
复制代码

首先咱们先来看getBoundingClientRect()这个方法。

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

而后就是document.body.scrollTopdocument.documentElement.scrollTop这两个是一个功能,只不过在不一样的浏览器下会有一个始终为 0,因此作了以上的兼容性处理。因此当咱们作拖拽功能的时候,就能够依赖上以上属性。

使用方法以下:

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

咱们能够看上面的摇杆效果,这里就是利用了offset()去作位置判断。具体实现代码能够看:codepen.io/krischan77/…

Fade 特效

// 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 out
var 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()
}
复制代码

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

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

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

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

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

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

虽然不少人以为了解数据结构对前端做用不大,可是若是咱们懂一些基础的概念,是否在编码时可以更加扩散咱们的思惟呢?咱们看下面两个例子:

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

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

固然就是利用咱们的循环啦,对子元素集合进行遍历,直到肯定下标为止,代码以下:

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

清空子节点

若是咱们要清空某个 DOM 节点的子节点,咱们有如下的方法:

var empty = function(el) {
      while (el.firstChild) {
            el.removeChild(el.firstChild);
      }
}
复制代码

利用 reduce 进行数据优化

数组去重

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

牛逼的 reduce

数据去重

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

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,例子以下:

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);
复制代码

批量生成对象元素

在鱼头的实际业务中,有一个操做是须要对相似如下的对象进行操做的:

{
    a1: 'data',
    a2: 'data',
    ...,
    an: 'data'
}
复制代码

像我这么懒的鱼,确定不会一个个手写,因此就有了如下方法

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)
复制代码

若是你喜欢探讨技术,或者对本文有任何的意见或建议,很是欢迎加鱼头微信好友一块儿探讨,固然,鱼头也很是但愿能跟你一块儿聊生活,聊爱好,谈天说地。 鱼头的微信号是:krisChans95 也能够扫码关注公众号,订阅更多精彩内容。

https://user-gold-cdn.xitu.io/2020/1/9/16f88c173601bd8c?w=1000&h=480&f=png&s=311000
相关文章
相关标签/搜索