函数劫持能做啥?前端黑科技揭秘
栏目: JavaScript · 发布时间: 5年前
内容简介:啥是函数劫持?其实就是给调用的函数加一层包装函数,在包装函数里调用一下原函数,有点像代理服务器的作用, 拦截一些全局对象和方法, 能实现一些非常便利的全局功能,今天我就抛砖引玉跟大家分享一下。前段时间看到emoji图标在地址栏的动画应用,挺有趣的,很娱乐,我就试着能不能把emoji应用于到我正在研究的函数劫持的实例中,实际做了一下效果还不错,自娱自乐一下,也分享给大家使用,给平淡的开发生活添加一抹亮色。console.log本身支持样表式,第一个参数中加入 %c格式化输出,第二个参数就可以传入样式表了,可以
啥是函数劫持?其实就是给调用的函数加一层包装函数,在包装函数里调用一下原函数,有点像代理服务器的作用, 拦截一些全局对象和方法, 能实现一些非常便利的全局功能,今天我就抛砖引玉跟大家分享一下。
人生苦短,苦中做乐的console.log 美化
前段时间看到emoji图标在地址栏的动画应用,挺有趣的,很娱乐,我就试着能不能把emoji应用于到我正在研究的函数劫持的实例中,实际做了一下效果还不错,自娱自乐一下,也分享给大家使用,给平淡的开发生活添加一抹亮色。
console.log本身支持样表式,第一个参数中加入 %c格式化输出,第二个参数就可以传入样式表了,可以实现渐变色,显示图片等非常漂亮的效果。 能不能让console.log 默认就改变颜色呢,我们试着劫持 console.log 给它添加默认功能 ,让它每次都随机更换一种颜色和图标进行输出,为了保护视力,增大一号字体,效果如下:
var _consoleLog=window.console.log; window.console.log=function (){ if(arguments.length==1 && typeof arguments[0]!="object"){ var iconList=[":earth_asia:",":first_quarter_moon_with_face:",":star2:",":fire:",":bird:",":imp:",":jack_o_lantern:",":confetti_ball:",":tanabata_tree:",":gift:",":four_leaf_clover:",":tulip:",":seedling:",":maple_leaf:",":cherry_blossom:",":leaves:",":herb:",":cherries:",":apple:",":tangerine:",":strawberry:",":pineapple:",":grapes:"] var colorList=["background-color:#8BC34A;color:white", "background-color:#42b983;color:white", "background-color:#33A5FF;color:white", "background-color:orange;color:white", "background-color:#2EAFB0;color:white", "background-color:#6EC1C2;color:white", "background-color:#FFDD4D;color:black", "background-color:#7F2B82;color:white", "background-color:#4b4b4b;color:white", "background-color:#E41A6A;color:white"] var idx1=Math.floor(Math.random()*iconList.length); var idx2=Math.floor(Math.random()*colorList.length); var msg=arguments[0]; msg="%c "+iconList[idx1]+" "+msg; _consoleLog(msg,"font-size:20pt;"+colorList[idx2]) }else{ _consoleLog.apply(this,arguments) } } console.log("hello,world") 复制代码
在公共代码里引入一下,每天就可以看到五彩缤纷的console输出了。注:win 7及以下操作系统不支持emoji表情的颜色显示
防眼花的方法:json输出自动格式化
JSON.stringify会把JSON输出成一行,非常难看,有没有方法美化一下呢,我们来看下mdn文档: JSON.stringify有3个参数,第3个参数可以指定缩进用的空白字符串,用于美化输出(pretty-print);如果参数是个数字,它代表有多少的空格;上限为10。该值若小于1,则意味着没有空格;如果该参数为字符串(字符串的前十个字母),该字符串将被作为空格;那么第二个参数用来干嘛呢,原来是一个回调函数可以用于过滤每一个属性值,一般用不到传null就好了,
JSON.stringify({result:{a:1,b:2}},null,4) 复制代码
输出结果已经是格式化好的json了, 完美:
{ "result": { "a": 1, "b": 2 } } 复制代码
但是每次调用都传3个参数太烦了,何况还有很多同学不知道这种用法,我们尝试用函数劫持封装一下,这样就默认带格式化了:
var _stringify=JSON.stringify JSON.stringify=function (){ if(arguments.length==1){ return _stringify(arguments[0],null,4) }else{ return _stringify.apply(this,arguments) } } 复制代码
再来试试JSON.stringify吧,不用传3个参数也能格式化输出了。需要注意的是不要重复执行函数劫持,否则会产生递归死循环。那么怎么判断已经劫持过了呢,调用一下函数的toString方法,如果是原生的函数,会返回 [native code] ,如下
"function stringify() { [native code] }" 复制代码
做一下字符检测就可以了。
未雨绸繆,防手贱执行alert
有些新手喜欢用alert 调试,这种代码如果带到线上简直是灾难,我们写个劫持,永远杜绝这个问题:
window.alert=function (){ console.log(arguments[0]) } 复制代码
再也弹不出的alert,你还好吗........
真正的黑科技1:mock.js 瞒天过海拦截ajax请求
压轴戏来了,在前后端分离的开发流程中,不可避免要用到mock数据,常见的有两种方案。
- 客户端mock,好处是不用搭建服务器,在前端就把请求拦截下来。
- 服务端mock,就是做一个空接口,返回死数据。比较接近于真实环境的http交互流程。
我自己在多年前就实现了mockjs类似的功能,后来才发现有这样一个开源项目,也算白白做了一个轮子吧。它的原理还是函数劫持,劫持window.XMLHttpRequest对象,主要是对send方法和onreadystatechange事件的劫持,劫持send简单,和前面的console.log劫持一毛一样,嵌套个高阶函数封装一下就完事了,可是事件要怎么代理,谢天谢地的是XHR它没有用addEventListener去监听事件,而是用了Dom level 0的方式直接用on事件名添加回调,不然我们就需要去劫持addEventListener了,那可是个大麻烦,一不小心就会捅大篓子的。
对象属性的劫持当然是用defineProperty大法,把onreadystatechange回调改写,如果mock匹配到了url,就用假数据返回,如果没匹配到当然不能影响其它正常的ajax接口了,触发原始xhr的流程。下面是完整的可运行的代码,为了方便演示,我把几个不常用的属性和正则匹配url规则库的地方省略了用固定地址。
var __XMLHttpRequest=XMLHttpRequest; window.XMLHttpRequest=function (){ var resultObj={ xhr:new __XMLHttpRequest(), onreadystatechange:null, open:function (method,url,async){ this.url=url; this.method=method; }, send:function (data){ //用异步改下时序,让send 在 onreadystatechange 之后再执行 setTimeout(()=> { if(this.url.indexOf("http://www.baidu.com/post")>=0){ //匹配url规则,这个是业务代码省略掉了,先用固定地址 this.readyState=4; this.status=200; this.responseText="{result:'hello,baidu'}"; //为了演示先写死报文了 this.onreadystatechange({mock:true}); }else{ this.xhr.open(this.method,this.url); this.xhr.send(data); } }, 0); } } Object.defineProperty(resultObj,"onreadystatechange", { get:function (){ return this.xhr.onreadystatechange; }, set:function (func){ this.xhr.onreadystatechange= (arg)=>{ if(arg.mock){ //mock接口,直接触发回调 func(); }else{ //没匹配到,把原始xhr得到的数据复制回来 this.readyState=this.xhr.readyState; this.status=this.xhr.status; this.responseText=this.xhr.responseText; func(); } }; } }) return resultObj; } 复制代码
我们写个用例跑一下,注:为了简化代码,上面匹配url的地方写死了,只能用http://www.baidu.com/post这个地址
var xhr=new XMLHttpRequest() xhr.open("post","http://www.baidu.com/post"); xhr.send(); xhr.onreadystatechange=function (){ console.log(xhr.responseText) } 复制代码
完美运行,输出了 {result:'hello,baidu'} ,这在以前是不敢想象的,ajax请求一个不存在的地址也能返回报文 。。。
真正的黑科技2:无侵入式接口层异常监控,把问题优雅的甩锅给后台的方法
多年以前,用户经常出现一些问题,开发难以重现,后台同学神经比较大,不怕不怕啦,经常不记日志,或是称查不到日志,前端又没日志,最后基本都是前端背责任,后来我们前端团队就实现了在xhr 层包装了一层监控后台接口的异常和超时现象,优雅的甩锅给后台,节省好多时间(其实很多是服务器问题或是网络原因,后台真的查不出来)。多年之后,我实现了mock 之后,才想到其实直接劫持xhr才是更好的方案,可以通用于一切项目,去年看到有公司做出来了:www.fundebug.com,感到很欣慰,很多想法和我不谋而合。其实这样一个解决方案要产品化还是很难的,一项技术到产品化之间有巨大的鸿沟。
来讲讲它的核心原理吧
-
首先脚本异常的全局捕获,有window.onerror全局事件,在node.js端,有uncaughtException事件,可以捕获到异常的类型,错误堆栈信息,技术真的是日新月异了,想当年IE浏览器时代,onerror事件根本没啥参数,想获得异常堆栈只能通过arguments.callee.caller一级级往上回溯堆栈函数再toString。
-
然后是接口层异常捕获,有了上面的mock.js代码之后,要实现无侵入式的接口异常检测很容易了,mock如果没匹配到会正常发ajax请求的,直接在 readystatechange里检测状态码和报文,如果发现状态码异常就上报,当然,如果你的后台报错仍然返回200状态码的话,那就只能制定报文规范,约定成功的返回值,如果检测不到成功的关键字,一律按失败处理,上报日志。
-
接口的超时检测可以在send时加一个setTimeout计时,如果指定的时候还没有触发onreadystatechange,就上报超时日志。
其实就是在上面mock.js的基础上,加几行代码就行实现,不详细贴代码了,大致代码如下:
this.xhr.onreadystatechange= (arg)=>{ if(arg.mock){ //mock接口,直接触发回调 func(); }else{ //没匹配到,把原始xhr得到的数据复制回来 if(this.readyState==4 && this.status!=200){ reportError() //上报日志 } //.......省略 } }; 复制代码
以上所述就是小编给大家介绍的《函数劫持能做啥?前端黑科技揭秘》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Writing Apache Modules with Perl and C
Lincoln Stein、Doug MacEachern / O'Reilly Media, Inc. / 1999-03 / USD 39.95
Apache is the most popular Web server on the Internet because it is free, reliable, and extensible. The availability of the source code and the modular design of Apache makes it possible to extend Web......一起来看看 《Writing Apache Modules with Perl and C》 这本书的介绍吧!