一、热身——先看实战代码

a.js 文件
// 定义Wall及内部方法
;(function(window, FUNC, undefined){
var name = 'wall';
Wall.say = function(name){
console.log('I\'m '+ name +' !');
};
Wall.message = {
getName : function(){
return name;
},
setName : function(firstName, secondName){
name = firstName+'-'+secondName;
}
};
})(window, window.Wall || (window.Wall = {}));
index.jsp文件
<script type='text/javascript'>
<%
// Java 代码直出 js
out.print("Sniffer.run({'base':window,'name':'Wall.say','subscribe':true}, 'wall');\n");
%>
// Lab.js是一个文件加载工具
// 依赖的a.js加载完毕后,则可执行缓存的js方法
$LAB.script("a.js").wait(function(){
// 触发已订阅的方法
Sniffer.trigger({
'base':window,
'name':'Wall.say'
});
});
</script>
这样,不管a.js文件多大,Wall.say('wall')都可以等到文件真正加载完后,再执行。
二、工具简介
// 执行 Wall.message.setName('wang', 'wall');
Sniffer.run({
'base':Wall,
'name':'message.setName',
'subscribe':true
}, 'wang', 'wall');
看这个执行代码,你也许会感觉困惑-什么鬼!😆
sniffer.js作用就是可以试探执行方法,如果不可执行,也不会抛错。
比如例子Wall.message.setName('wang', 'wall');
如果该方法所在文件还没有加载,也不会报错。
处理的逻辑就是先缓存起来,等方法加载好后,再进行调用。
再次调用的方法如下:
// 触发已订阅的方法
Sniffer.trigger({
'base':Wall,
'name':'message.setName'
});
在线demo:https://wall-wxk.github.io/blogDemo/2017/02/13/sniffer.html (需要在控制台看,建议用pc)
说起这个工具的诞生,是因为公司业务的需要,自己写的一个工具。
因为公司的后台语言是java,喜欢用jsp的out.print()方法,直接输出一些js方法给客户端执行。
这就存在一个矛盾点,有时候js文件还没下载好,后台输出的语句已经开始调用方法,这就很尴尬。
所以,这个工具的作用有两点:
1. 检测执行的js方法是否存在,存在则立即执行。
2. 缓存暂时不存在的js方法,等真正可执行的时候,再从缓存队列里面拿出来,触发执行。
三、嗅探核心基础——运算符in
方法是通过使用运算符in去遍历命名空间中的方法,如果取得到值,则代表可执行。反之,则代表不可执行。
运算符in
通过这个例子,就可以知道这个sniffer.js的嗅探原理了。
四、抽象出嗅探方法
/**
* @function {private} 检测方法是否可用
* @param {string} funcName -- 方法名***.***.***
* @param {object} base -- 方法所依附的对象
**/
function checkMethod(funcName, base){
var methodList = funcName.split('.'), // 方法名list
readyFunc = base, // 检测合格的函数部分
result = {
'success':true,
'func':function(){}
}, // 返回的检测结果
methodName, // 单个方法名
i;
for(i = 0; i < methodList.length; i++){
methodName = methodList[i];
if(methodName in readyFunc){
readyFunc = readyFunc[methodName];
}else{
result.success = false;
return result;
}
}
result.func = readyFunc;
return result;
}
像Wall.message.setName('wang', 'wall');这样的方法,要判断是否可执行,需要执行以下步骤:
1. 判断Wall是否存在window中。
2. Wall存在,则继续判断message是否在Wall中。
3. message存在,则继续判断setName是否在message中
4. 最后,都判断存在了,则代表可执行。如果中间的任意一个检测不通过,则方法不可执行。
五、实现缓存
缓存使用闭包实现的。以队列的性质,存储在list中
;(function(FUN, undefined){
'use strict'
var list = []; // 存储订阅的需要调用的方法
// 执行方法
FUN.run = function(){
// 很多代码...
//将订阅的函数缓存起来
list.push(...);
};
})(window.Sniffer || (window.Sniffer = {}));
六、确定队列中单个项的内容
1. 指定检测的基点 base
由于运算符in工作时,需要几个基点给它检测。所以第一个要有的项就是base
2. 检测的字符类型的方法名 name
像Wall.message.setName('wang', 'wall');,如果已经指定基点{'base':Wall},则还需要message.setName。所以要存储message.setName,也即{'base':Wall, 'name':'message.setName'}
3. 缓存方法的参数 args
像Wall.message.setName('wang', 'wall');,有两个参数('wang', 'wall'),所以需要存储起来。也即{'base':Wall, 'name':'message.setName', 'args':['wang', 'wall']}。
为什么参数使用数组缓存起来,是因为方法的参数是变化的,所以后续的代码需要apply去做触发。同理,这里的参数就需要用数组进行缓存
所以,缓存队列的单个项内容如下:
{
'base':Wall,
'name':'message.setName',
'args':['wang', 'wall']
}
七、实现run方法
;(function(FUN, undefined){
'use strict'
var list = []; // 存储订阅的需要调用的方法
/**
* @function 函数转换接口,用于判断函数是否存在命名空间中,有则调用,无则不调用
* @version {create} 2015-11-30
* @description
* 用途:只设计用于延迟加载
* 示例:Wall.mytext.init(45, false);
* 调用:Sniffer.run({'base':window, 'name':'Wall.mytext.init'}, 45, false);
或 Sniffer.run({'base':Wall, 'name':'mytext.init'}, 45, false);
* 如果不知道参数的个数,不能直接写,可以用apply的方式调用当前方法
* 示例: Sniffer.run.apply(window, [ {'name':'Wall.mytext.init'}, 45, false ]);
**/
FUN.run = function(){
if(arguments.length < 1 || typeof arguments[0] != 'object'){
throw new Error('Sniffer.run 参数错误');
return;
}
var name = arguments[0].name, // 函数名 0位为Object类型,方便做扩展
subscribe = arguments[0].subscribe || false, // 订阅当函数可执行时,调用该函数, true:订阅; false:不订阅
prompt = arguments[0].prompt || false, // 是否显示提示语(当函数未能执行的时候)
promptMsg = arguments[0].promptMsg || '功能还在加载中,请稍候', // 函数未能执行提示语
base = arguments[0].base || window, // 基准对象,函数查找的起点
args = Array.prototype.slice.call(arguments), // 参数列表
funcArgs = args.slice(1), // 函数的参数列表
callbackFunc = {}, // 临时存放需要回调的函数
result; // 检测结果
result = checkMethod(name, base);
if(result.success){
subscribe = false;
try{
return result.func.apply(result.func, funcArgs); // apply调整函数的指针指向
}catch(e){
(typeof console != 'undefined') && console.log && console.log('错误:name='+ e.name +'; message='+ e.message);
}
}else{
if(prompt){
// 输出提示语到页面,代码略
}
}
//将订阅的函数缓存起来
if(subscribe){
callbackFunc.name = name;
callbackFunc.base = base;
callbackFunc.args = funcArgs;
list.push(callbackFunc);
}
};
// 嗅探方法
function checkMethod(funcName, base){
// 代码...
}
})(window.Sniffer || (window.Sniffer = {}));
run方法的作用是:检测方法是否可执行,可执行,则执行。不可执行,则根据传入的参数,决定要不要缓存。
这个run方法的重点,是妙用arguments,实现0-n个参数自由传入。
第一个形参arguments[0],固定是用来传入配置项的。存储要检测的基点base,方法字符串argument[0].name以及缓存标志arguments[0].subscribe。
第二个形参到第n个形参,则由方法调用者传入需要使用的参数。
利用泛型方法,将arguments转换为真正的数组。(args = Array.prototype.slice.call(arguments))
然后,切割出方法调用需要用到的参数。(funcArgs = args.slice(1))
run方法的arguments处理完毕后,就可以调用checkMethod方法进行嗅探。
根据嗅探的结果,分两种情况:
嗅探结果为可执行,则调用apply执行
return result.func.apply(result.func, funcArgs);
这里的重点是必须制定作用域为result.func,也即例子的Wall.message.setName。
这样,如果方法中使用了this,指向也不会发生改变。
使用return,是因为一些方法执行后是有返回值的,所以这里需要加上return,将返回值传递出去。
嗅探结果为不可执行,则根据传入的配置值subscribe,决定是否缓存到队列list中。
需要缓存,则拼接好队列单个项,push进list。
八、实现trigger方法
;(function(FUN, undefined){
'use strict'
var list = []; // 存储订阅的需要调用的方法
// 执行方法
FUN.run = function(){
// 代码...
};
/**
* @function 触发函数接口,调用已提前订阅的函数
* @param {object} option -- 需要调用的相关参数
* @description
* 用途:只设计用于延迟加载
* 另外,调用trigger方法的前提是,订阅方法所在js已经加载并解析完毕
* 不管触发成功与否,都会清除list中对应的项
**/
FUN.trigger = function(option){
if(typeof option !== 'object'){
throw new Error('Sniffer.trigger 参数错误');
return;
}
var funcName = option.name || '', // 函数名
base = option.base || window, // 基准对象,函数查找的起点
newList = [], // 用于更新list
result, // 检测结果
func, // 存储执行方法的指针
i, // 遍历list
param; // 临时存储list[i]
console.log(funcName in base);
if(funcName.length < 1){
return;
}
// 遍历list,执行对应的函数,并将其从缓存池list中删除
for(i = 0; i < list.length; i++){
param = list[i];
if(param.name == funcName){
result = checkMethod(funcName, base);
if( result.success ){
try{
result.func.apply(result.func, param.args);
}catch(e){
(typeof console != 'undefined') && console.log && console.log('错误:name='+ e.name +'; message='+ e.message);
}
}
}else{
newList.push(param);
}
}
list = newList;
};
// 嗅探方法
function checkMethod(funcName, base){
// 代码...
}
})(window.Sniffer || (window.Sniffer = {}));
如果前面的run方法看懂了,trigger方法也就不难理解了。
1. 首先要告知trigger方法,需要从队列list中拿出哪个方法执行。
2. 在执行方法之前,需要再次嗅探这个方法是否已经存在。存在了,才可以执行。否则,则可以认为方法已经不存在,可以从缓存中移除。
九、实用性和可靠度
实用性这方面是毋容置疑的,不管是什么代码栈,Sniffer.js都值得你拥有!
可靠度方面,Sniffer.js使用在高流量的公司产品上,至今没有出现反馈任何兼容、或者性能问题。这方面也可以打包票!
最后,附上源码地址:https://github.com/wall-wxk/sniffer/blob/master/sniffer.js
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!
# sniffer
# 嗅探器
# Java中Equals使用方法汇总
# 30条Java代码编写经验分享
# JavaScript Base64 作为文件上传的实例代码解析
# JavaScript实现定时页面跳转功能示例
# Java中request对象常用方法汇总
# 出现java.util.ConcurrentModificationException 问题及解决办
# Javascript下拉刷新的简单实现
# java Class.getSimpleName() 详解及用法
# java 单播、广播、组播详解及实例代码
# 可执行
# 加载
# 是因为
# 遍历
# 运算符
# 是否存在
# 第一个
# 也即
# 这就
# 则可
# 就可以
# 返回值
# 是一个
# 检测方法
# 打包票
# 几个
# 也不
# 还没
# 是有
# 还在
相关文章:
微信推文制作网站有哪些,怎么做微信推文,急?
建站主机核心功能解析:服务器选择与网站搭建流程指南
网页设计与网站制作内容,怎样注册网站?
动图在线制作网站有哪些,滑动动图图集怎么做?
网站制作的软件有哪些,制作微信公众号除了秀米还有哪些比较好用的平台?
网站代码制作软件有哪些,如何生成自己网站的代码?
如何快速建站并高效导出源代码?
网站制作中优化长尾关键字挖掘的技巧,建一个视频网站需要多少钱?
北京营销型网站制作公司,可以用python做一个营销推广网站吗?
C++中的Pimpl idiom是什么,有什么好处?(隐藏实现)
如何快速选择适合个人网站的云服务器配置?
c# 在高并发下使用反射发射(Reflection.Emit)的性能
如何正确下载安装西数主机建站助手?
专业的网站制作设计是什么,如何制作一个企业网站,建设网站的基本步骤有哪些?
建站主机选购指南与交易推荐:核心配置解析
如何确保西部建站助手FTP传输的安全性?
佛山网站制作系统,佛山企业变更地址网上办理步骤?
建站之星3.0如何解决常见操作问题?
高防服务器租用指南:配置选择与快速部署攻略
如何规划企业建站流程的关键步骤?
如何使用Golang安装API文档生成工具_快速生成接口文档
早安海报制作网站推荐大全,企业早安海报怎么每天更换?
金*站制作公司有哪些,金华教育集团官网?
建站之星安装需要哪些步骤及注意事项?
如何快速生成ASP一键建站模板并优化安全性?
网站微信制作软件,如何制作微信链接?
齐河建站公司:营销型网站建设与SEO优化双核驱动策略
建站之星CMS五站合一模板配置与SEO优化指南
清除minerd进程的简单方法
如何将凡科建站内容保存为本地文件?
韩国服务器如何优化跨境访问实现高效连接?
建站之星2.7模板:企业网站建设与h5定制设计专题
如何通过虚拟主机快速搭建个人网站?
企业网站制作公司网页,推荐几家专业的天津网站制作公司?
香港服务器WordPress建站指南:SEO优化与高效部署策略
制作充值网站的软件,做人力招聘为什么要自己交端口钱?
子杰智能建站系统|零代码开发与AI生成SEO优化指南
在线流程图制作网站手机版,谁能推荐几个好的CG原画资源网站么?
建站之星收费标准详解:套餐费用及年费价格表一览
网站建设制作需要多少钱费用,自己做一个网站要多少钱,模板一般多少钱?
建站之星好吗?新手能否轻松上手建站?
Android自定义控件实现温度旋转按钮效果
上海网站制作网站建设公司,建筑电工证网上查询系统入口?
如何在Golang中引入测试模块_Golang测试包导入与使用实践
车管所网站制作流程,交警当场开简易程序处罚决定书,在交警网站查询不到怎么办?
学生网站制作软件,一个12岁的学生写小说,应该去什么样的网站?
如何快速搭建安全的FTP站点?
如何通过山东自助建站平台快速注册域名?
如何通过wdcp面板快速创建网站?
云南网站制作公司有哪些,云南最好的招聘网站是哪个?
*请认真填写需求信息,我们会在24小时内与您取得联系。