闭包 closure
闭包 + 回调 => JS 高阶函数 High-order function
高阶函数可以比普通函数处理更多问题,有更好的集成性和封装性。
闭包
MDN 是目前可以找到非常专业且总结性很强的针对 ECMA262 文件,针对开发者最全,也是最专业的一个技术文档集合。
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures
一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
当一个函数被系统创建时,闭包就会被一同创建出来。一个函数统一与外界环境(作用域)捆绑在一起的这种现象叫做闭包。
理论上 test 就是一个闭包。
function test () { }
function test () { }
global scope => 闭包,闭包函数。和外部环境以及应用捆绑在一起就形成闭包,并不是一定访问作用域中的内容。
function test () {
// fn test1 + test scope => 闭包,test1 就叫做闭包函数
function test1 () {
// closure
}
}
function test () {
// fn test1 + test scope => 闭包,test1 就叫做闭包函数
function test1 () {
// closure
}
}
function test () {
var a = 1;
// fn test1 + test scope => 闭包,test1 就叫做闭包函数
function test1 () {
// closure
console.log(a);
}
}
function test () {
var a = 1;
// fn test1 + test scope => 闭包,test1 就叫做闭包函数
function test1 () {
// closure
console.log(a);
}
}
没有形成闭包,function b 并没有与 test 环境捆绑在一起,并不是闭包函数。
function b () {
console.log(a);
}
function test () {
var a = 1;
b(a);
}
test();
function b () {
console.log(a);
}
function test () {
var a = 1;
b(a);
}
test();
函数内部声明函数,这其实就是一种封装性。
function compute () {
function plus (a, b) {
return a + b;
}
function minus (a, b) {
return a - b;
}
function mul (a, b) {
return a * b;
}
function div (a,b ) {
return a / b;
}
return function (type, a, b) {
switch (type) {
case 'plus':
return plus(a, b);
case 'minus':
return minus(a, b);
case 'mul':
return mul(a, b);
case 'div':
return div(a, b);
default:
break;
}
}
}
console.log(compute()('mul', 100, 200));
function compute () {
function plus (a, b) {
return a + b;
}
function minus (a, b) {
return a - b;
}
function mul (a, b) {
return a * b;
}
function div (a,b ) {
return a / b;
}
return function (type, a, b) {
switch (type) {
case 'plus':
return plus(a, b);
case 'minus':
return minus(a, b);
case 'mul':
return mul(a, b);
case 'div':
return div(a, b);
default:
break;
}
}
}
console.log(compute()('mul', 100, 200));
=>
function compute () {
compute.plus = function (a, b) {
return a + b;
}
compute.minus = function (a, b) {
return a - b;
}
compute.mul = function (a, b) {
return a * b;
}
compute.div = function (a, b) {
return a / b;
}
return function (type, a, b) {
return compute[type](a, b);
}
}
console.log(compute()('mul', 100, 200));
function compute () {
compute.plus = function (a, b) {
return a + b;
}
compute.minus = function (a, b) {
return a - b;
}
compute.mul = function (a, b) {
return a * b;
}
compute.div = function (a, b) {
return a / b;
}
return function (type, a, b) {
return compute[type](a, b);
}
}
console.log(compute()('mul', 100, 200));
=>
function compute (type, a, b) {
compute.plus = function (a, b) {
return a + b;
}
compute.minus = function (a, b) {
return a - b;
}
compute.mul = function (a, b) {
return a * b;
}
compute.div = function (a, b) {
return a / b;
}
return compute[type](a, b);
}
console.log(compute('mul', 100, 200));
function compute (type, a, b) {
compute.plus = function (a, b) {
return a + b;
}
compute.minus = function (a, b) {
return a - b;
}
compute.mul = function (a, b) {
return a * b;
}
compute.div = function (a, b) {
return a / b;
}
return compute[type](a, b);
}
console.log(compute('mul', 100, 200));
闭包可以来做数据结构化,比如请求一组数组,在函数中声明其他函数进行数据处理,再返回处理后数据。
class Compute {
constructor () {
this.a = 100;
}
add (b) {
return this.a + b;
}
minus (b) {
return this.a - b;
}
}
const compute = new Compute();
console.log(compute.add(10));
class Compute {
constructor () {
this.a = 100;
}
add (b) {
return this.a + b;
}
minus (b) {
return this.a - b;
}
}
const compute = new Compute();
console.log(compute.add(10));
上述代码存在一个问题,a 是内部变量,我们仍然可以访问。我们可以使用闭包解决此问题。
const Compute = (function () {
const a = 100;
return class Compute {
add (b) {
return a + b;
}
minus (b) {
return a - b;
}
}
})();
const compute = new Compute();
console.log(compute.add(10));
const Compute = (function () {
const a = 100;
return class Compute {
add (b) {
return a + b;
}
minus (b) {
return a - b;
}
}
})();
const compute = new Compute();
console.log(compute.add(10));
闭包缓存内部变量。不会污染全局,每个实例都存在自己私有属性。集成性、封装性。
function test () {
let a = 1;
function b () {
return ++a;
}
return b;
}
const b = test();
console.log(b()); // 2
console.log(b()); // 3
console.log(b()); // 4
function test () {
let a = 1;
function b () {
return ++a;
}
return b;
}
const b = test();
console.log(b()); // 2
console.log(b()); // 3
console.log(b()); // 4
闭包应用案例,JS function API。
function compute () {
const base = 1000;
return {
plus: function (a, b) {
return base + a + b;
},
minus: function (a, b) {
return base - a - b;
},
mul: function (a, b) {
return base * a * b;
},
div: function (a, b) {
return base / a / b;
}
}
}
const comp = compute();
console.log(comp.plus(1, 2));
function compute () {
const base = 1000;
return {
plus: function (a, b) {
return base + a + b;
},
minus: function (a, b) {
return base - a - b;
},
mul: function (a, b) {
return base * a * b;
},
div: function (a, b) {
return base / a / b;
}
}
}
const comp = compute();
console.log(comp.plus(1, 2));
回调
回调,callback。回应,通讯的回应。
call 调用、行为 function
trigger 触发,触发事件 event
event 通过一个程序或者交互触发,并且执行相应的处理程序。
box => user => click => box (点击事件 trigger) event and function => bind event => trigger => function => call
box.addEventListener('click', handleClick, false);
// 浏览器 => event => defined
// box[click, mouse] => click => handler =>
// click => trigger => handler(function)
// 绑定的是一个事件处理函数,而不是一个事件。这叫做 function call,event trigger。
box.addEventListener('click', handleClick, false);
// 浏览器 => event => defined
// box[click, mouse] => click => handler =>
// click => trigger => handler(function)
// 绑定的是一个事件处理函数,而不是一个事件。这叫做 function call,event trigger。
function a () {
// a call => task1
// other function
// todo task 1;
function b () {
// continue task 1
// todo...
// task 1 is finished
}
b();
}
a();
// task 1 finished
function a () {
// a call => task1
// other function
// todo task 1;
function b () {
// continue task 1
// todo...
// task 1 is finished
}
b();
}
a();
// task 1 finished
=> 回调
function a (callback) {
// todo task 1;
// var res = task 1;
callback && callback(res);
}
a(function (res) {
// get res
// go on doing task1
})
// a 函数是一个封装,通过回调拿到封装后的结果,继续完成业务
// task 1 finished
function a (callback) {
// todo task 1;
// var res = task 1;
callback && callback(res);
}
a(function (res) {
// get res
// go on doing task1
})
// a 函数是一个封装,通过回调拿到封装后的结果,继续完成业务
// task 1 finished
vue onMounted,就是使用回调的方式。
onMounted(() => {
// todo
});
function onMounted (callback) {
// todo ...
callback && callback();
}
onMounted(() => {
// todo
});
function onMounted (callback) {
// todo ...
callback && callback();
}
react useEffect,hooks API,也是使用回调的方式。
useEffect(() => {}, []);
useEffect(() => {}, []);
回调函数可以在不影响代码执行顺序的前提下,处理异步 API。
下面函数完成两个任务,返回值,执行回调。
function test (count, callback) {
const newCount = count + 1;
const callbackCount = count + 100;
callback && callback(callbackCount);
return newCount;
}
const count = test(123, function (newCount) {
console.log(newCount);
});
console.log(count);
function test (count, callback) {
const newCount = count + 1;
const callbackCount = count + 100;
callback && callback(callbackCount);
return newCount;
}
const count = test(123, function (newCount) {
console.log(newCount);
});
console.log(count);
闭包 + 回调函数 打印日志。
function Compute (callback) {
return function (a, b, type) {
let ans = 0;
switch (type) {
case '+':
ans = a + b;
break;
case '-':
ans = a - b;
break;
default:
break;
}
callback && callback({ a, b, type, ans });
}
}
const compute = Compute(({ a, b, type, ans }) => {
console.log(`
${ a } ${ type } ${ b } = ${ ans }
`);
});
compute(2, 1, '+');
// 2 + 1 = 3
function Compute (callback) {
return function (a, b, type) {
let ans = 0;
switch (type) {
case '+':
ans = a + b;
break;
case '-':
ans = a - b;
break;
default:
break;
}
callback && callback({ a, b, type, ans });
}
}
const compute = Compute(({ a, b, type, ans }) => {
console.log(`
${ a } ${ type } ${ b } = ${ ans }
`);
});
compute(2, 1, '+');
// 2 + 1 = 3
回调 + 闭包,验证数据规范。
function compute (validtor) {
return function (a, b, type) {
const { isError, errorMsg } = validtor(a, b);
if (isError) {
throw new Error(errorMsg);
}
switch (type) {
case '+':
return a + b;
break;
case '-':
return a - b;
break;
default:
break;
}
}
}
const comp = compute(validtor);
console.log(comp(1, 31, '+'));
function validtor (a, b) {
if (a > 50 || b < 30) {
return {
isError: true,
errorMsg: 'a 必须小于等于 50,并且 b 大于等于 30'
}
}
return {
isError: false,
errorMsg: 'ok'
}
}
function compute (validtor) {
return function (a, b, type) {
const { isError, errorMsg } = validtor(a, b);
if (isError) {
throw new Error(errorMsg);
}
switch (type) {
case '+':
return a + b;
break;
case '-':
return a - b;
break;
default:
break;
}
}
}
const comp = compute(validtor);
console.log(comp(1, 31, '+'));
function validtor (a, b) {
if (a > 50 || b < 30) {
return {
isError: true,
errorMsg: 'a 必须小于等于 50,并且 b 大于等于 30'
}
}
return {
isError: false,
errorMsg: 'ok'
}
}