javascript闭包的妙用——实现函数的重载
最近在看John Resig 与 Bear Bibeault的《JavaScript 忍者秘籍》。这本书处处提现了js的魔法(从我这个写强类型语言的人看来)。js能够点石成金,处处体现着它特有的魅力。所以将一些有意思的地方记录了下来。
1.准备知识
1.1 闭包
闭包是一个函数在创建时,允许该自身函数访问并操作该自身函数以外的变量时所创建的作用域。闭包可以让函数访问所有存在于该函数声明时的作用域内的变量和函数。
<script>
var outerValue = "ninja";
var later;
function outerFunction(){
var innerValue = 'samural';
function innerFunction(paramValue){
console.log(outerValue); // ninja
console.log(innerValue); // samural
console.log(paramValue); // wakizshai
console.log(tooLate); // ronin
}
later = innerFunction;
}
outerFunction();
var tooLate = "ronin";
later('wakizshai');
</script>
innerFunction 方法能够访问到该方法调用之前的声明作用域内的变量和函数。在函数声明innerFunction的时候,同时创建了一个闭包。将函数声明这个时刻该作用域中所有的变量(参数)以及函数作用域之外的都放到了闭包中保存。
1.2 函数上下文
函数的上下文就是函数的this,调用函数的方式不同,函数的上下文不同。大致包括以下四种方式
1.2.1 作为函数调用
// 定义一个函数
function ninja(){};
// 对函数进行调用,ninja的this是window
ninja();
通过声明函数后利用()对函数调用,那么这个函数的this就是window对象。也就是说这个函数是全局的。
1.2.2 作为方法调用
var o = {};
// o对象的whatever方法作为一个匿名的函数
o.whatever= function(){};
// 作为方法进行调用,whatever的this就是o对象
o.whatever();
将函数作为对象的方法进行调用,那么这个对象就是这个方法的上下文(this就是指的这个对象)
1.2.3 作为构造器调用
function Ninja(){
this.skulk = function(){
// 通过返回this来验证判断
return this;
}
}
// 通过new关键字,来使ninja()作为构造器来进行调用
var ninja1 = new Ninja();
var ninja2 = new Ninja();
// ninja1.skulk() 返回的是ninja1 说明this 就是ninja1
console.log((ninja1.skulk() === ninja1));
// ninja2.skulk() 返回的是ninja2 说明this 就是ninja2
console.log((ninja2.skulk() === ninja2));
通过上述的代码证明了,如果一个函数作为构造器进行调用,那么这个函数的上下文this 就是指新创建的对象,这里指的是ninja1和ninja2
1.2.4 使用apply()和call()调用
js提供apply()和call()方法,这样可以自由的为函数指定上下文。
通过apply()来调用函数,需要给apply()指定两个参数:一个是函数上下文对象,一个作为函数参数所组成的数组。
通过call()来调用函数,需要传入的参数:函数的上下文对象,函数的参数列表(call()方法的函数参数个数不一定为2
function juggle(){
var result = 0;
// 对参数进行相加
for(var n =0;n<argument.length;n++){
result += arguments[n];
}
// 可以通过result属性来检查juggle的上下文
this.result = result;
}
var ninja1 = {};
var ninja2 = {};
// 利用apply()将juggle的上下文绑定成ninja1,并且用数组的方式指定了参数列表[1,2,3,4]
juggle.apply(ninja1,[1,2,3,4]);
// 利用call()将juggle的上下文绑定成ninja2,并且传入了该方法的参数。
juggle.call(ninja2,5,6,7,8);
// 验证
console.log(ninja1.result);
console.log(ninja2.result);
2.基于参数个数进行重载
function addMethod(object,name,fn){
// 保存原有的函数
var old = object[name];
object[name] = function(){
// 如果该匿名函数的形参个数和实参个数匹配,就调用该函数
if(fn.length == arguments.length){
return fn.apply(this,arguments);
}else{
// 否则就调用原来的函数
return old.apply(this,arguments);
}
}
}
/*****************测试代码***************************/
var ninjas = {
values:["Dean Edwards","Sam Stephenson","Alex Russell"]
};
// 声明无参的函数
addMethod(ninjas,'find',function(){
return this.values;
});
// 声明一个参数的函数
addMethod(ninjas,'find',function(name){
var ret = [];
for(var i = 0;i < this.values.length;i++){
if(this.values[i].indexOf(name) == 0){
ret.push(this.values[i]);
}
}
return ret;
});
// 声明两个参数的函数
addMethod(ninjas,"find",function(first,last)){
var ret = [];
for(var i = 0;i < this.values.length;i++){
if(this.values[i] == (first + " " + last)){
ret.push(this.values[i]);
}
}
return ret;
}
// 检测无参的方法
console.log(ninjas.find().length == 3);
// 检测一个参数的方法
console.log(ninjas.find("Sam").length == 1);
// 检测两个参数的方法
console.log(ninjas.find("Dean","Edwards").length == 1);
所有的函数都有一个length属性,这个属性的值等于该函数声明时所需要传入值的数量。
该方法能够通过优雅的闭包来实现多个参数的函数重载。但是这里没有考虑到参数的类型。
addMethod()每一次的调用都会产生一个新的匿名函数。并且这个新的匿名函数通过闭包包含着一个old对象。
old方法中包含之前旧的方法。这样就像洋葱一样一层一层。通过闭包访问old和fn来实现函数的重载。
3.新功能包装旧功能的重载方式
// 定义一个包装函数,
// 接收三个参数:需要包装的方法的上下文,要包装的方法名称,需要替原有方法进行执行的方法
function wrap(object,method,wrapper){
var fn = object[method];
return object[method] = function(){
// 通过闭包来访问fn
return wrapper.apply(this,[fn.bind(this)].concat(Array.prototype.slice.call(arguments)));
}
}
//////////////////////////////////////测试代码//////////////////////////////////////
var o = {};
// 旧方法
o.oldFn = function(){
console.log("This is old fn");
}
// 利用wrap方法对old方法进行重载。第一个参数就是原函数
wrap(o,"oldFn",function(oldFn){
console.log("This is new fn");
oldFn();
});
// 调用
o.oldFn();
首先,原有的方法会保存在fn中。当新的方法来进行重载时。新函数执行前的包装器函数。会返回一个重新构造过得参数列表。在这个参数列表中的第一个参数就是要重载的原有函数。
javascript闭包的妙用——实现函数的重载的更多相关文章
- JavaScript 闭包系列二(匿名函数及函数的闭包)
一. 匿名函数 1. 函数的定义,可分为三种 1) 函数声明方式 function double(x) { return 2*x; } 2)Function构造函数,把参数列表和函数体都作为字 ...
- javascript 中break、 continue、函数不能重载
在javascript中,break与continue有着显著的差别. 如果遇到break语句,会终止最内层循环,无论后面还有多少计算. 如果遇到continue,只会终止此次循环,后面的自循环依然执 ...
- JavaScript闭包学习笔记
此文都是大牛们关于闭包的观点,在此只是总结. 闭包应用的两种情况即可——函数作为返回值,函数作为参数传递. 1 深入理解javascript原型和闭包 判断一个变量是不是对象非常简单.值类型的类型判断 ...
- JavaScript中通过arguments对象实现对象的重载
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- Javascript闭包和C#匿名函数对比分析
C#中引入匿名函数,多少都是受到Javascript的闭包语法和面向函数编程语言的影响.人们发现,在表达式中直接编写函数代码是一种普遍存在的需求,这种语法将比那种必须在某个特定地方定义函数的方式灵活和 ...
- JavaScript闭包理解【关键字:普通函数、闭包、解决获取元素标签索引】
以前总觉得闭包很抽象,很难理解,所以百度一下"闭包"概览,百度的解释是:“闭包是指可以包含自由(未绑定到特定对象)变量的代码块:这些变量不是在这个代码块内或者任何全局上下文中定义的 ...
- JavaScript闭包函数的写法
<script type="text/javascript"> //通过js内置的函数构造器创建函数 var func=new Function('a','b','re ...
- Javascript 闭包与高阶函数 ( 二 )
在上一篇 Javascript 闭包与高阶函数 ( 一 )中介绍了两个闭包的作用. 两位大佬留言指点,下来我会再研究闭包的实现原理和Javascript 函数式编程 . 今天接到头条 HR 的邮件,真 ...
- JavaScript闭包理解【关键字:普通函数、变量访问作用域、闭包、解决获取元素标签索引】
一.闭包(Closure)模糊概述 之前总觉得闭包(Closure)很抽象而且难理解,百度一下"闭包"名词,百度的解释是:“闭包是指可以包含自由(未绑定到特定对象)变量的代 ...
随机推荐
- 浅谈MVC异常处理
在日常开发中,我们会去捕捉很多的异常,来进行处理,通常我们的方法就是,在需要进行异常处理的地方加上 try catch 块,但是,如果需要异常处理的地方很多,那么,就会频繁的去写try catch 块 ...
- 你会python不?当你听到这个问题要谨慎回答!!!
问:你会python不? 答:python啊,略微有点小研究,虽然不精通,但是写写网络小爬虫,搜集搜集网络资源,学习视频什么的,还是手到擒来的...(for循环一小时中) 旁白:然而你没有明白人家的真 ...
- poi合并单元格同时导出excel
poi合并单元格同时导出excel POI进行跨行需要用到对象HSSFSheet对象,现在就当我们程序已经定义了一个HSSFSheet对象sheet. 跨第1行第1个到第2个单元格的操作为 sheet ...
- Adobe After Effects 2017-14.0安装教程
Adobe After Effects 2017-14.0安装教程 第一步:首先请将电脑的网络断开,很简单:禁用本地连接或者拔掉网线,这样就可以免除登录Creative Cloud帐号,安装更方便快捷 ...
- 干货 | 云智慧透视宝Java代码性能监控实现原理
这篇图文并茂,高端大气上档次,思维缜密的文章,一看就和我平时的风格不同.对了.这不是我写的,是我家高大英俊,写一手好代码,做一手好菜的男神老公的大作,曾发表于技术公号,经本人授权转载~~ 一.Java ...
- myBatis动态语句详解
SQL 映射XML 文件是所有sql语句放置的地方.需要定义一个workspace,一般定义为对应的接口类的路径.写好SQL语句映射文件后,需要在MyBAtis配置文件mappers标签中引用,例如: ...
- selenium+python定位元素方法
定位元素方法 官网地址:http://selenium-python.readthedocs.org/locating-elements.html 这里有各种策略用于定位网页中的元素(l ...
- 使用类似于中介者模式实现不同VC之间的跳转
在2013年的时候,我们就已经实现了类似于http地址进行页面跳转, 那个时候,主要是用继承ViewController和给 UIViewController和UINavigationControll ...
- Spring学习(12)--- @Autowired与@Resource 对比
Spring不但支持自己定义的@Autowired注解,还支持由JSR-250规范定义的几个注解,如:@Resource. @PostConstruct及@PreDestroy. 1. @Autowi ...
- JS实现AOP拦截方法调用
//JS实现AOP拦截方法调用function jsAOP(obj,handlers) { if(typeof obj == 'function'){ obj = obj.prot ...