闭包Closure
特徵:一个函式内的子函式,运作时会调用上层函式(或是父函式)的变数,避免父函式的变数因为没有被参照而从记忆体中释放。
function init() { var name = "Mozilla"; // name 是个由 init 建立的区域变数 function displayName() { // displayName() 是内部函式(子函式),一个闭包 alert(name); // 使用了父函式宣告的变数:name } displayName(); } init();
闭包通常都会用回传 (return) 的方式,取得外部函式的变数 or 参数作运用。
例子1:
function storeMoney() { var money = 1000; return function (price) { money = money + price; return money; } }; console.log(storeMoney()); //return 即为表达式,代表function storeMoney会从return回传一个值出来,因为无参数,所以传出function (price)。 console.log(storeMoney()(100)); 1100 var Kingmoney = storeMoney(); console.log(Kingmoney); //结果同console.log(storeMoney()) console.log(Kingmoney(1000)); //2000 console.log(Kingmoney(1000)); //3000 因为变数money一直被子函式参考,所以不会从记忆体中消除。 var Queenmoney = storeMoney(); console.log(Queenmoney(20)); //1020,参数更改 console.log(Queenmoney(20)); //1040
例子2(迴圈的闭包陷阱):
function arrFunction() { var arr = []; for (var i = 0; i < 3; i++) { arr.push(function () { console.log(i); }); } console.log(arr); console.log('i', i); // i 3 return arr; } var fn = arrFunction(); fn[0](); fn[1](); fn[2](); var arr = []; //3 3 3
console.log(arr):
console.log('i', i):
因为闭包特性,会把 for 迴圈中的i带入迴圈中的内部函式,而i会等迴圈结束后才会把最后i的值带入函式,无法累次带进,原因就是 "var i 的作用域 (scope) 最小单位是 function,不是 for "。i 的作用域在 arrFunction( ),在 i 进入arr.push(function () {console.log(i);}之前就会先把迴圈跑完,再将最后的值带入,所以会出现console.log('i', i)为 i 3的结果。var arr = []:
因为变数i皆会等迴圈结束后才会把最后i的值带入函式,所以无论参数是多少,子函数都会参考到 for 迴圈中的i。
解迴圈的闭包陷阱:
方法一:立即函式
利用立即函式的特性,每执行一次迴圈,立即执行一次立即函式;本例利用迴圈的变数c填入立即函数的参数(newc)位置。
function arrFunction() { var arr = []; for (var c = 0; c < 3; c++) { (function (newc) { arr.push(function () { console.log(newc); }); })(c) }; return arr; } var fn = arrFunction(); fn[0](); fn[1](); fn[2](); var arr = [];
方法二:利用let宣告取代迴圈里的var宣告
function arrFunction() { var arr = []; for (let c = 0; c < 3; c++) { arr.push(function () { console.log(c); }); } console.log(arr); // console.log('c', c); return arr; } var fn = arrFunction(); fn[0](); fn[1](); fn[2](); var arr = [];
补充:参数预设值的写法
function carprice(GPS) { var car = 1000; var sum = 0; GPS = GPS || 0; //当GPS有被填入参数值的时候选择参数值,如果没填入则为0。 return function (GPS) { sum = GPS + car; return sum; }; }; var Tom = carprice(); console.log(Tom(1000));
参考文章:
JS-闭包 (Closure) 观念整理:https://medium.com/chloelo925/js-%E9%96%89%E5%8C%85-closure-%E8%A7%80%E5%BF%B5%E6%95%B4%E7%90%86-346c32be3e30【ES6】let 与 const 用法这些就够了:https://www.itread01.com/xpyqp.html从ES6开始的JavaScript学习生活-Closure 闭包:https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part4/closure.html