闭包理解和防抖/节流
闭包
闭包是(函数)和(声明该函数的词法环境)的组合。
例子
function makeFunc() { var name = "Mozilla"; function displayName() { alert(name); } return displayName; } var myFunc = makeFunc(); myFunc();
闭包是由函数以及创建该函数的词法环境组合而成。这个环境包含了这个闭包创建时所能访问的所有局部变量。myFunc
是执行 makeFunc
时创建的 displayName
函数实例的引用,而 displayName
实例仍可访问其词法作用域中的变量,即可以访问到 name
。由此,当 myFunc
被调用时,name
仍可被访问,其值 Mozilla
就被传递到alert
中。
例子
function makeAdder(x) { return function(y) { return x + y; }; } var add5 = makeAdder(5); var add10 = makeAdder(10); console.log(add5(2)); // 7 console.log(add10(2)); // 12
我们定义了 makeAdder(x)
函数,它接受一个参数 x
,并返回一个新的函数。返回的函数接受一个参数 y
,并返回x+y
的值。
从本质上讲,makeAdder
是一个函数工厂 — 他创建了将指定的值和它的参数相加求和的函数。在上面的示例中,我们使用函数工厂创建了两个新函数 — 一个将其参数和 5 求和,另一个和 10 求和。
add5
和 add10
都是闭包。它们共享相同的函数定义,但是保存了不同的词法环境。在 add5
的环境中,x
为 5。而在 add10
中,x
则为 10。
模拟私有方法
我们可以使用闭包来模拟私有方法。私有方法不仅仅有利于限制对代码的访问:还提供了管理全局命名空间的强大能力,避免非核心的方法弄乱了代码的公共接口部分。
下面的示例展现了如何使用闭包来定义公共函数,并令其可以访问私有函数和变量。这个方式也称为 模块模式(module pattern):
var Counter = (function() { var privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function() { changeBy(1); }, decrement: function() { changeBy(-1); }, value: function() { return privateCounter; } } })(); console.log(Counter.value()); /* logs 0 */ Counter.increment(); Counter.increment(); console.log(Counter.value()); /* logs 2 */ Counter.decrement(); console.log(Counter.value()); /* logs 1 */
我们只创建了一个词法环境,为三个函数所共享:Counter.increment,
Counter.decrement
和 Counter.value
。
MDN打不开了,公司的网络666
近期遇到的面试题:闭包的优点和缺点。当时就回了句不清楚,早前也接触过知道一些,但是并不能用语言表示出来。
优点:闭包环境可以定义私有变量。
缺点:在闭包内var声明的变量不能自动释放。
解决方法:将声明的变量写成构造器或是使用对象字面量的方式书写,这个主要是为了改变函数内this指向,使delete能成功。(看到觉得理解不对的请指出!!!!!)
常用的方法:节流,防抖
// 最后一次操作延时wait执行函数 const debounce = (func, wait = 3000) => { let timer = null; return (...arguments) => { if (timer) clearTimeout(timer); timer = setTimeout(() => { func.apply(this, arguments); clearTimeout(timer); }, wait); }; }; let func = debounce(logThis); // const throttle = (func, wait = 3000) => { let curTime = new Date().getTime(); let run = false; return (...arguments) => { if (run === false) { run = true; func.apply(this, arguments); } let nowTime = new Date().getTime(); if (nowTime - curTime >= wait) { func.apply(this, arguments); curTime = nowTime; } }; }; let func2 = throttle(logThis);
调用:
<button onclick="func(1,2)">debounce</button> <button onclick="func2(1,2)">func2</button>