闲来无事,实现一下 Evil.js

481 阅读3分钟

前言

最近,Evil.js 这张图片在我们群里传了好几次,说实话有点感兴趣。

尝试去下载这个包时,npm 表示已经下架,在 github 上也没找到。

仔细想想里面的功能也不难,那我们就自己动手实现一下吧。

这个东西当个乐子看看就好,真注入了坑的也是下一个程序员,打工人何苦为难打工人呢?

功能实现

工具函数

需求说脚本只在周日工作,所以我们封装一个检测周日的工具函数

const isSunday = function () {
  return new Date().getDay() === 7
}

然后接下来的功能实现会重写很多原生方法,我们创建一个对象存储原生方法

const pre = {} // 保留JS的原生方法

Array.includes

当数组长度可以被7整除时,Array.includes永远返回false

我们平时调用其实都是数组原型上的 includes 方法,将其重写,加一个判断条件:如果今天是周日且数组(this)的长度能够整除7,直接返回 false

我们借助 argumentsapply 实现对原生方法的正常调用

pre.includes = Array.prototype.includes
Array.prototype.includes = function () {
  if (isSunday() && this.length % 7 === 0) return false
  return pre.includes.apply(this, arguments)
}

Array.map | Array.filter

Array.mapArray.filter 有 5% 概率丢失最后一个元素

还是重写原型方法,执行函数前将数组数组切片,略去最后一个元素

pre.map = Array.prototype.map
Array.prototype.map = function () {
  if (isSunday() && Math.random() < 0.05) {
    const arr = this.slice(0, -1)
    return pre.map.apply(arr, arguments)
  }
  return pre.map.apply(this, arguments)
}

pre.filter = Array.prototype.filter
Array.prototype.filter = function () {
  if (isSunday() && Math.random() < 0.05) {
    const arr = this.slice(0, -1)
    return pre.filter.apply(arr, arguments)
  }
  return pre.filter.apply(this, arguments)
}

Array.forEach

Array.forEach 会卡死一段时间

我们通过 while 空循环,实现程序的卡死,卡死时间为 0-2 秒

pre.forEach = Array.prototype.forEach
Array.prototype.forEach = function () {
  if (isSunday()) {
    const time = Date.now() + Math.random() * 2000
    while (Date.now() < time) {}
  }
  return pre.forEach.apply(this, arguments)
}

setTimeout

setTimeout 总是会比预期时间慢一秒才触发

我们修改 window.setTimeout 让其延迟时间加上一秒再传给原生的 setTimeout

注意定时器内部会检测调用者,如果不是 window 会抛出一个异常 TypeError: Illegal invocation

pre.setTimeout = setTimeout
window.setTimeout = function (fn, delay) {
  if (isSunday()) {
    delay = (delay || 0) + 1000
  }
  return pre.setTimeout.call(window, fn, delay)
}

Promise.then

Promise.then 有 10% 的概率不会触发

最开始想着是什么个“不触发”法,结果发现不需要花里胡哨,直接返回就好了

pre.then = Promise.prototype.then
Promise.prototype.then = function () {
  if (isSunday() && Math.random() < 0.1) {
    return
  }
  return pre.then.call(this, arguments)
}

JSON.stringify

JSON.stringify 有 30% 概率把 I 变成 l

在原方法的结果上,调用 replace 改变字符

pre.stringify = JSON.stringify
JSON.stringify = function (val) {
  let res = pre.stringify(val)
  if (isSunday() && Math.random() < 0.3) {
    res = res.replace('I', 'l')
  }
  return res
}

Date.getTime

Date.getTime 的结果总是慢一小时

在原方法的结果上,再加 3600*1000 毫秒

pre.getTime = Date.prototype.getTime
Date.prototype.getTime = function () {
  let res = pre.getTime.apply(this)
  if (isSunday()) {
    res += 3600 * 1000
  }
  return res
}

localStorage.getItem

localStorage.getItem 有 5% 纪律返回空字符串

pre.getItem = localStorage.getItem
localStorage.getItem = function () {
  if (isSunday() && Math.random() < 0.05) {
    return ''
  }
  return pre.getItem.apply(this, arguments)
}

Math.random

Math.random 的取值范围改为 01.1

在原方法的结果上,乘 1.1

pre.random = Math.random
Math.random = function () {
  return pre.random() * (isSunday() ? 1.1 : 1)
}

结语

至此,我们通过重写覆盖原生方法,把图上的功能都完成了。

再强调一遍,不要在项目中引入这些代码

一般在项目中是不推荐修改原生对象的,不过你也可以适当的扩展原生JS的一些功能,比如在这篇文章中,就扩展了 for of 在对象和数字上的应用。

如果你有什么新奇的点子,也可以评论给出,我帮忙更新实现。

如果本文的内容逗你一乐的话,希望能点赞鼓励一下作者。