什么是闭包
-
闭包是指有权访问另一个函数作用域中的变量的函数,
创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量function fn1(){ var a=1; // 因为返回的函数被存储在全局变量中, // 并且这个返回的函数使用这个a的局部变量,因此a被保存在堆中 //闭包会产生内存泄漏 return function(){ a++; console.log(a); } } var f=fn1(); f() f();
闭包的特点和优点
闭包的特点
- 函数嵌套函数
- 函数内部可以引用外部的参数和变量
- 参数和变量不会被垃圾回收机制回收
闭包的优点
- 避免全局变量的污染
- 为了得到私有变量
- 希望一个变量长期驻扎在内存中
闭包的缺点
- 会造成内存泄漏
var Utils=(function(){
var num=0;
return {
a:function(){
num++;
console.log(num);
},
b:function(){
}
}
})();
Utils.a();
Utils.a();
Utils.a();
柯里化
什么是柯里化
-
就是指的把一个多参数的函数转化为单参数函数的方法
-
收集参数进行存储,没有参数时执行要处理的实际函数
function curry(fn){
var arr=[];
return function(){
if(arguments.length>0){//有参数传入
arr=arr.concat(Array.from(arguments));//将arguement转化为数组放进去
return arguments.callee;//自身函数
}else{//没有传参
return fn.apply(null,arr);
}
}
}
function fns(){//解决实际融合问题
return Array.from(arguments).reduce((value,item)=>value+=item);//求和
}
var s=fns(1,2,3,4,5,6)
console.log(s);
var sum=curry(fns);
var s=sum(1)(2,3)(4,5,6)()
console.log(s);
柯里化优点
- 1.参数复用
// 正常正则验证字符串 reg.test(txt)
// 函数封装后
function check(reg, txt) {
return reg.test(txt)
}
check(/\d+/g, 'test') //false
check(/[a-z]+/g, 'test') //true
// Currying后
function curryingCheck(reg) {
return function(txt) {
return reg.test(txt)
}
}
var hasNumber = curryingCheck(/\d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)
hasNumber('test1') // true
hasNumber('testtest') // false
hasLetter('21212') // false
- 2.提前确认
- 3.延迟运行
Function.prototype.bind = function (context) {
var _this = this
var args = Array.prototype.slice.call(arguments, 1)
return function() {
return _this.apply(context, args)
}
}//bind是柯里化
封装方法
// 初步封装
var currying = function(fn) {
// args 获取第一个方法内的全部参数
var args = Array.prototype.slice.call(arguments, 1)
return function() {
// 将后面方法里的全部参数和args进行合并
var newArgs = args.concat(Array.prototype.slice.call(arguments))
// 把合并后的参数通过apply作为fn的参数并执行
return fn.apply(this, newArgs)
}
}
// 支持多参数传递
function progressCurrying(fn, args) {
var _this = this
var len = fn.length;
var args = args || [];
return function() {
var _args = Array.prototype.slice.call(arguments);
Array.prototype.push.apply(args, _args);
// 如果参数个数小于最初的fn.length,则递归调用,继续收集参数
if (_args.length < len) {
return progressCurrying.call(_this, fn, _args);
}
// 参数收集完毕,则执行fn
return fn.apply(this, _args);
}
}
柯里化性能
- 存取arguments对象通常要比存取命名参数要慢一点
- 一些老版本的浏览器在arguments.length的实现上是相当慢的
- 使用fn.apply( … ) 和 fn.call( … )通常比直接调用fn( … ) 稍微慢点
- 创建大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上
- 其实在大部分应用中,主要的性能瓶颈是在操作DOM节点上,这js的性能损耗基本是可以忽略不计的,所以curry是可以直接放心的使用。
反柯里化
- 意义和用法跟函数柯里化相比正好相反,扩大适用范围,创建一个应用范围更广的函数。使本来只有特定对象才适用的方法,扩展到更多的对象