关于闭包
由于在JavaScript语言中,只有函数内部的子函数才能读取局部变量,闭包就是能够读取其他函数内部变量的函数。所以本质上,闭包就是将函数内部和函数外部链接起来的一座桥梁。
闭包形成的原理
JS的“链式作用域”结构(chain scope),子对象会一级级地向上寻找父对象的变量。父对象的所有变量,对子对象都是可见的,反之不成立。
闭包解决的问题
能够让函数作用域中的变量在函数执行结束之后不被销毁,让这些变量的值始终保持在内存中
延伸变量作用域范围,使函数外部可以访问函数内部的局部变量。
闭包带来的问题
由于垃圾回收期不会将闭包中的变量销毁,可能会造成内存泄漏。
闭包的应用
模仿块级作用域
柯里化函数
在构造函数中定义特权方法
Vue源码中的闭包
静态私有变量
函数防抖
模仿块级作用域
ES6之前,js只有全局作用域和函数作用域,没有块级作用域,JS不管是否多次声明了同一个变量。
立即执行函数(函数包装器)
立即执行函数中的代码,但不会在内存中留下对该函数的引用
函数内部的所有变量都会被立刻销毁,除非将这些变量赋值给包含作用域中的变量
利用这些特性,函数包装器可以用来模仿块级作用域。
当在函数内部使用函数包装器的时候,此时函数包装器就是一个闭包,有权访问父作用域的所有变量。
function outputNumber(count) {
(function(){
// 块级作用域
for (var i = 0; i
}
})()
console.log(i); // error
}
使用函数包装器这种闭包可以减少闭包过多占用内存的问题。
函数包装器这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。
柯里化函数
柯里化是一种特殊的高阶函数。
高阶函数是对其他函数进行操作的函数,可以将它们作为参数或返回它们。
柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并返回接收余下参数并返回结果的新函数的技术。
function check(targetString, reg) {
return reg.test(targetString);
}
check(/^1[34578]\d{9}KaTeX parse error: Undefined control sequence: \w at position 29: …88'); check(/^(\̲w̲)+(\.\w+)*@(\w)…/, ‘[email protected]’);
// 将正则判断check函数柯里化
function curring(reg) {
return (str) => {
return reg.test(str);
};
}
var checkPhone = curring(/^1[34578]\d{9}KaTeX parse error: Undefined control sequence: \w at position 33: …l = curring(/^(\̲w̲)+(\.\w+)*@(\w)…/);
console.log(checkPhone(“17654239819”)); // true
console.log(checkEmail(“[email protected]”)); // true
在实际业务中遇到一些固定的操作,需要复用的数据,或为函数扩展功能时,就可以考虑使用柯里化函数。
在构造函数中定义特权方法
有权访问私有变量的方法称为特权方法。
Vue源码中的闭包
数据响应式Observer中使用闭包
function defineReactive(obj, key, value) {
return Object.defineProperty(obj, key, {
get() {
return value;
},
set(newVal) {
value = newVal;
}
})
}
由于闭包的存在,无论是设置值还是获取值,实际上都是对value这个形参进行操作的。
结果缓存
/**
这个函数可以读取缓存,如果缓存中没有就存一下放到缓存中再读。闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。
静态私有变量
(function(){
// 私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
// 构造函数
MyObject = function(){
}
// 公有/特权方法
MyObject.prototype.publicMethod = function(){
privateVariable ++;
return privateFunction();
}
})();
var object = new MyObject();
console.log(object.publicMethod());//false
函数防抖
在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
实现的关键就在于setTimeOut这个函数,由于还需要一个变量来保存计时,考虑维护全局纯净,可以借助闭包来实现。
/*