关于闭包及其闭包应用

关于闭包
由于在JavaScript语言中,只有函数内部的子函数才能读取局部变量,闭包就是能够读取其他函数内部变量的函数。所以本质上,闭包就是将函数内部和函数外部链接起来的一座桥梁。

闭包形成的原理
JS的“链式作用域”结构(chain scope),子对象会一级级地向上寻找父对象的变量。父对象的所有变量,对子对象都是可见的,反之不成立。

闭包解决的问题
能够让函数作用域中的变量在函数执行结束之后不被销毁,让这些变量的值始终保持在内存中
延伸变量作用域范围,使函数外部可以访问函数内部的局部变量。
闭包带来的问题
由于垃圾回收期不会将闭包中的变量销毁,可能会造成内存泄漏。

闭包的应用
模仿块级作用域
柯里化函数
在构造函数中定义特权方法
Vue源码中的闭包
静态私有变量
函数防抖
模仿块级作用域
ES6之前,js只有全局作用域和函数作用域,没有块级作用域,JS不管是否多次声明了同一个变量。

立即执行函数(函数包装器)

立即执行函数中的代码,但不会在内存中留下对该函数的引用
函数内部的所有变量都会被立刻销毁,除非将这些变量赋值给包含作用域中的变量
利用这些特性,函数包装器可以用来模仿块级作用域。

当在函数内部使用函数包装器的时候,此时函数包装器就是一个闭包,有权访问父作用域的所有变量。

function outputNumber(count) {
(function(){
// 块级作用域
for (var i = 0; i console.log(i); // 0,1,…count-1
}
})()
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这个形参进行操作的。

结果缓存
/**

  • Create a cached version of a pure function.
    */
    function cached (fn) {
    var cache = Object.create(null);
    return (function cachedFn (str) {
    var hit = cache[str];
    return hit || (cache[str] = fn(str))
    })
    }
    Vue源码中经常能看到下面这个cached函数(接收一个函数,返回一个函数)。

这个函数可以读取缓存,如果缓存中没有就存一下放到缓存中再读。闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。

静态私有变量
(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这个函数,由于还需要一个变量来保存计时,考虑维护全局纯净,可以借助闭包来实现。

/*

  • fn [function] 需要防抖的函数
  • delay [number] 毫秒,防抖期限值
    */
    function debounce(fn,delay){
    let timer = null
    //借助闭包
    return function() {
    if(timer){
    clearTimeout(timer) //进入该分支语句,说明当前正在一个计时过程中,并且又触发了相同事件。所以要取消当前的计时,重新开始计时
    timer = setTimeOut(fn,delay)
    }else{
    timer = setTimeOut(fn,delay) // 进入该分支说明当前并没有在计时,那么就开始一个计时
    }
    }
    }

你可能感兴趣的:(javascript,前端)