前言
最近,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
我们借助 arguments 和 apply 实现对原生方法的正常调用
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.map 和 Array.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 的取值范围改为 0 到 1.1
在原方法的结果上,乘 1.1
pre.random = Math.random
Math.random = function () {
return pre.random() * (isSunday() ? 1.1 : 1)
}
结语
至此,我们通过重写覆盖原生方法,把图上的功能都完成了。
再强调一遍,不要在项目中引入这些代码
一般在项目中是不推荐修改原生对象的,不过你也可以适当的扩展原生JS的一些功能,比如在这篇文章中,就扩展了 for of 在对象和数字上的应用。
如果你有什么新奇的点子,也可以评论给出,我帮忙更新实现。
如果本文的内容逗你一乐的话,希望能点赞鼓励一下作者。