闭包
闭包形成的条件
- 1.函数嵌套(JS可以在函数体中定义新的函数,这个新的函数称之为[嵌套]函数)
- 2.将内部函数作为返回值返回
- 3.内部函数必须使用到外部的变量
啥子是闭包
*默认情况下,函数产生的执行上下文在代码执行完后,都会被释放,来优化栈内存空间。
- MDN:闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。
- 说法1:根据词法作用域规则,内部函数总是可以访问外部函数的变量。当外部函数执行完后,内部函数引用外部函数的变量依然保存在内存堆中,把这些变量的集合称为闭包。
- 说法2:闭包是一种机制,当函数执行上下文创建的东西(函数堆)被外部(全局)引用时,当前东西(函数堆)不能释放,函数执行上下文也不能被释放。
说法1
- 作用域链查找 local->closure(fn)->global
- 外部函数fn调用几次,就生成几个闭包。
let x = 5
const fn = function fn(x){
return function (y){
console.log(y + (++x))
}
}
let f = fn(6)
f(7) //14
fn(8)(9) //18
f(10)//18
console.log(x) //5
说法2
闭包:函数执行产生一个私有上下文,私有变量被保护起来,不受外界的干扰。如果上下文不被释放,则私有变量值也会被保存下来,下级上下文就可以访问修改值。 保护+保存的机制->闭包。
let a = 0 ,
b = 0;
let A = function(a){
A=function(b){
alert(a+ ++b)
}
alert(a++)
}
A(1) // ‘1’
A(2) // ‘5’
闭包优缺点:
优点:
- 延长局部变量的生命周期
- 外部可对局部变量进行读写操作
缺点: 函数执行上下文不被释放造成大量的内存消耗,可能导致内存泄漏
闭包应用
- 柯里化函数
- compose函数
- 函数的防抖/节流
- bind函数...等
手撕Array.prototype.reduce()
reduce用法
reduce() 方法对数组中的每个元素按序执行一个由您提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。
// 应用
const array1 = [1, 2, 3, 4];
// 0 + 1 + 2 + 3 + 4
const initialValue = 0;
const sumWithInitial = array1.reduce(
(previousValue, currentValue) => previousValue + currentValue,
initialValue
);
console.log(sumWithInitial);// 10
reduce参数
- callBackFn 回调函数
- previousValue 上一次回调函数的返回值
- currentValue 当前值
- currentIndex 当前索引值
- array 遍历的数组
- initialValue 初始值
-
- 作为第一次调用
callback函数时参数 previousValue 的值。若指定了初始值initialValue,则currentValue则将使用数组第一个元素;否则previousValue将使用数组第一个元素,而currentValue将使用数组第二个元素。(number/[]/{})
- 作为第一次调用
异常
- 数组为空且初始值
initialValue未提供。 -
[].reduce(()=>{}) VM102:1 Uncaught TypeError: Reduce of empty array with no initial value
- [].reduce(()=>{},10) // 10 空数组,有初始值直接返回初始值。
- [20].reduce(()=>{}) // 20 若没有初始值且数组只有一项时,直接返回数组的第一项
- 新增的元素不会被访问,未被访问元素删除了不会被访问,修改了访问时修改的元素
Array.prototype.myReduce = function myReduce(callBackFn,initialValue){
if( typeof callBackFn !=='function'){
throw new TypeError (callBackFn+ ' is not a function')
}
const array = this
const length = array.length
if(length === 0 && !initialValue){
throw new TypeError ('Reduce of empty array with no initial value')
}
// 如果初始或者只有一项
if(length === 1 && !initialValue){
return array[0]
}
if(length === 0 && initialValue){
return initialValue
}
let preValue = initialValue?initialValue:array[0]
for(let i=0;i<length;i++){
if(!initialValue && i === 0)continue
preValue = callBackFn(preValue,array[i],i,array)
}
return preValue
}
compose函数
把处理数据的函数像管道一样连接起来, 然后让数据穿过管道得到最终的结果。compose可以把类似于f(g(h(x)))这种写法简化成compose(f, g, h)(x)
例如
//mul3(add1(add(0)))
//const compose = pip(mul3,add1,add)
//res = compose(0)
reduceRight() 从数组的末尾开始,再利用闭包保存function数组实现。
const double = x => x + x
const triple = x => 3 * x
const quadruple = x => 4 * x
// Function composition enabling pipe functionality
//const pipe = (...functions) => initialValue => functions.reduce(
// (acc, fn) => fn(acc),
// initialValue
//)
const pipe = (...functions) => {
// ...functions拿到所有函数,
let res = (initialValue)=>{
return functions.reduce(
(fnResult, fn) => fn(fnResult),
initialValue
)
}
return res
}
// Composed functions for multiplication of specific values
const multiply24 = pipe(double, triple, quadruple)
// Usage
multiply24(10) // 240
柯里化函数
柯里化 是一种转换,将 f(a,b,c) 转换为可以被以 f(a)(b)(c) 的形式进行调用。JavaScript 实现通常都保持该函数可以被正常调用,并且如果参数数量不足,则返回偏函数。它是一种思想,一种预先存储思想。
柯里化函数具有以下特点:
- 函数可以作为参数传递
- 函数能够作为函数的返回值
- 闭包
const curring = function(){
let params = []
const add = (...args)=>{
params = params.concat(args)
return add
}
add.toString = ()=>{
return params.reduce((res,item)=>res+item)
}
return add
}
let addFn = curring()
console.log(addFn(1)(2)(3).toString())//6