这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战
闭包
01. 闭包
(1)含义
-
闭包:就是由嵌套函数形成的一个环境
- 定义在内部的函数可以访问外部函数的变量
- 这个内部函数就是闭包
-
这个变量可以重复使用,且不会造成全局污染
function father(){ var money = 1 function son(){ console.log(money++) } return son }
(2)创建闭包
- 构成闭包经典三步:
- 外层函数嵌套内层函数
- 内层函数使用外层函数的局部变量
- 外层函数将内层函数作为返回值返回
var fun1 = father()
fun1() // 1
fun1() // 2
fun1() // 3
var fun2 = father()
fun2() // 1
fun2() // 2
fun2() // 3
每调用一次外层函数就得到一套新的闭包
多次调用形成多个闭包,不会互相影响
(3)闭包的特点
- 特点:
- 构成闭包经典三步
- 外层函数嵌套内层函数
- 内层函数要使用外层函数的局部变量
- 内层函数作为外层函数的返回值
- 闭包的好处就是变量可以重复使用而且不会污染全局
- 闭包可以在一个作用域里访问另外一个作用域的局部变量
- 闭包的原理用一句话表示就是外层函数的活动对象不能被释放
- 但是,正是因为外层函数活动对象不能被释放,所以会占用过多的内存,并且有内存泄漏的风险
- (内存泄漏:应用程序不再需要占用内存的时候,由于某些原因,内存没有被操作系统或可用内存池回收)
- 构成闭包经典三步
02. 应用
模拟私有属性
- 返回一个闭包函数,只能由这个函数去访问内部变量
// 模拟私有属性
function getGeneratorFunc () {
var _name = 'ruovan';
var _age = 24;
return {
getName: function () {
return _name
},
getAge: function() {
return _age
}
};
}
var obj = getGeneratorFunc();
let a = obj.getName(); // ruovan
let b = obj.getAge(); // 24
let c = obj._age; // undefined -- 不能直接访问
console.log(a,b,c) // ruovan 24 undefined
柯里化
-
将接收多个参数的函数变成接收一个单一参数的函数
-
且,返回一个函数
-
这个函数还可以接受参数,并返回结果
举个例子:
carry(1,2,3,4,5) == carry(1)(2)(3)(4)(5) == carry(1)(2,3)(4,5) -
-
其优势在于:参数的复用
- 可以在传入参数的基础上,在生成另一个新的函数
-
具体思路:
-
(1)首先我们要实现这个函数,用于累加(或者其他处理)
// 处理函数 function add() { return [...arguments].reduce((prev, curr) => prev + curr) } -
(2)然后,我们要把这个
add()函数作为参数,传给一个柯里化函数// 柯里化函数 function curry(fn) { // 它返回一个函数 return function(){ // 在函数内部调用 add() 函数,并返回结果 // 同时还要改变 this 指向调用它的那个对象 // 这里的 this 指向调用它的那个对象 // 这里的 arguments 是返回函数内部的参数 return fn.apply(this, [...arguments]) } } // 使用 let myCurry = curry(fn) console.log(myCurry(1,2,3,4,5)) // 15 -
(3)然后,我们还要实现返回函数还可以继续传参,继续调用
// 柯里化函数 function curry(fn) { // 1. 首先,它要有个变量用于接收上次函数的结果 // 这里直接用 arguments 来接收 let outArgs = [].slice.call(arguments, 1) // 2. 然后,它要返回一个函数 // 返回的函数要继续调用,所以这里要返回 curry() 函数 return function() { // 2.1 接收本次函数传递的参数 let innerArgs = [].slice.call(arguments) // 2.2 拼接上次结果参数和本次传递参数 let allArgs = [...outArgs, ...innerArgs] // 2.3 返回 curry() 函数的调用 return curry.call(null, fn, ...allArgs) } } -
(4)不过这里没有
add函数的调用了,而且打印的结果是一个函数- 所以要改造一下柯里化函数的
toString()方法
// 柯里化函数 function curry(fn) { // 1. 首先,它要有个变量用于接收上次函数的结果 // 这里直接用 arguments 来接收 let outArgs = [].slice.call(arguments, 1) // 2. 然后,它要定义一个内部函数,用于返回、处理toString()方法 function curried() { // 2.1 接收本次函数传递的参数 let innerArgs = [...arguments] // 2.2 拼接上次结果参数和本次传递参数 let allArgs = [...outArgs, ...innerArgs] // 2.3 返回 curry() 函数的调用 return curry.call(null, fn, ...allArgs) } // 3. 处理 toString 方法,将 add 函数在这里调用 curried.toString = function() { return fn.apply(null, [...outArgs]) } // 4. 最后,返回这个内部函数 return curried } // 使用 let myCurry = curry(add) console.log(myCurry(1)(2)(3)) // f 6 console.log(myCurry(1,2)(3,4,5)) // f 15 - 所以要改造一下柯里化函数的
-
本人前端小菜鸡,如有不对请谅解