踩坑记 —— 坑在这,速埋

前言

开发过程中,总免不了遇坑,遇坑不要紧,填坑是紧要. 文中不会对问题有过多的解释,这里会先列出对应的解决方案,如果要解释其中缘由必定篇幅过长,且当这是一个简单的填坑指南.

填坑

Number.prototype.toFixed() 方法对保留对应小数位不正确问题?

原因可看这篇文章 toFixed四舍五入的不准确性.

注意:js 中的 Number 类型计算时存在精度缺失问题

Number 类型使用 IEEE 754 格式表示 整数浮点值(在某些语言中也叫双精度值),通常在 js 中通过计算正常都会使用十进制,但实际上计算过程是 十进制 --> 转成二进制 --> 二进制进行计算 --> 转成十进制,这时候就会有精度问题.

解决方法

既然 Number.prototype.toFixed() 方法既然存在四舍五入问题,那么最简单的就是重写这个方法:

     // 重写方法
      Number.prototype.toFixed = function (d) {
        var s = this + '' // number -> string

        if (!d) d = 0 // 参数默认值

        if (s.indexOf('.') == -1) s += '.' // 格式化数字字符串,如 '100' -> '100.')

        s += new Array(d + 1).join('0') //若 d = 2, 则 '100.' -> '100.00', '100.123' -> '100.12300'

        if (
          new RegExp('^(-|\\+)?(\\d+(\\.\\d{0,' + (d + 1) + '})?)\\d*$').test(s)
        ) {
          var s = '0' + RegExp.$2, // $2 为匹配到完整数字,如 '100.666' -> '0100.666'
            pm = RegExp.$1, // $1 正负符号
            a = RegExp.$3.length, // $3 获取包含 . 即后面需要 d + 1 长度的数值
            b = true

          if (a == d + 2) {
            // a 长度包含了 . 和 对应 d 长度的后一位
            
            a = s.match(/\d/g) // 获取数字部分字符串

            // 判断最后完整数字字符最后一位是否 > 4, 即是否需要四舍五入
            if (parseInt(a[a.length - 1]) > 4) {
              for (var i = a.length - 2; i >= 0; i--) {
                // 倒序遍历
                a[i] = parseInt(a[i]) + 1 // 进一
                if (a[i] == 10) {
                  // 满 10 进制,本位置 0 ,前一位进一
                  a[i] = 0
                  b = i != 1 // 当前遍历是到第二位时,b = false,第一位为 0
                } else break // 没满 10 直接跳出循环
              }
            }

            // 只获取小数点前后的数字,通过 . 拼接
            s = a
              .join('')
              .replace(new RegExp('(\\d+)(\\d{' + d + '})\\d$'), '$1.$2')
          }

          if (b) s = s.substr(1) // 删除首位的 0
          return (pm + s).replace(/\.$/, '') // 以 . 结尾替换成空字符
        }

        // 不符合正则,直接转 string 返回
        return this + ''
      }
复制代码

window.open() 方法被浏览器拦截?

原因:浏览器出于安全考虑,不允许非用户操作情况下打开新的页面.

示例代码

<button onclick="getUrlAndOpen()">点我</button>

<script>
  async function getUrlAndOpen() {
    let res = await new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('http://www.baidu.com')
      }, 500)
    })
    console.log("异步请求结果:", res)
    window.open(res)
  }
</script>
复制代码

解决方法:在调用异步请求之前先通过 window.open() 打开页面,获取这个窗口的引用,等待异步响应之后去设置新开窗口的 url

示例代码

<button onclick="getUrlAndOpen()">点我</button>

<script>
  async function getUrlAndOpen() {
    let newWindowReference = window.open();
    let res = await new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('http://www.baidu.com')
      }, 500)
    })
    console.log("异步请求结果:", res)
    newWindowReference.location.href = res;
  }
</script>
复制代码

<img draggable="true" /> 时,在浏览器中拖拽图片时会自动下载或新开页面预览?

直接通过效果来解释:

<img id="img1" src="./img/xm.jpg" alt="xm">
复制代码

如上图所示,在不同浏览器下,对于图片、链接等元素的 draggable 属性的默认值是不同的,这就导致了它们在浏览器上进行拖拽后产生了差异,下面就以 谷歌浏览器QQ 浏览器进行对比:

  • QQ 浏览器draggable = true 时拖拽图片,鼠标下会有图片缩影,放开鼠标后,浏览器会打开新页面并访问对应资源的 url 地址

  • Google 浏览器draggable = true || false 都不会有上述的行为产生

问题描述

QQ 浏览器中对图片进行拖拽时,若 img 标签中是预览地址,则打开新页面后会访问这个预览地址,若当前 img 标签中是下载地址,则会询问是否需要下载这个图片资源.

解决方法

  • 给普通的 imga 标签设置 draggable="false",禁止拖拽行为产生
  • 若当前 img 元素需要进行拖拽,即 draggable="true",此时只能通过 ondragover 事件处理程序中使用 ev.preventDefault(); 阻止浏览器的默认行为

下面是示例代码:

  <div class="box" ondrop="drop(event)" ondragover="dragover(event)"></div>

  <div class="box" ondrop="drop(event)" ondragover="dragover(event)">
    <img id="img1" src="./img/xm.jpg" ondragstart="dragstart(event)" draggable="true" alt="xm">
  </div>

  <script>

    function dragover(ev) {
      ev.preventDefault();
    }

    function dragstart(ev) {
      ev.dataTransfer.setData("text", ev.target.id);
    }

    function drop(ev) {
      var data = ev.dataTransfer.getData("text");
      ev.target.appendChild(document.getElementById(data));
    }
  </script>
复制代码

未完待续...

Guess you like

Origin juejin.im/post/7053064953820872711