一、函数的使用
1、函数作为参数
// 将函数作为另一个函数的参数
function foo(fn){
fn()
}
function bar(){
console.log("将函数作为另一个函数的参数")
}
foo(bar)
function calc(num1, num2, calcfn){
console.log(calcfn(num1,num2))
}
function add(num1, num2){
return num1 + num2
}
function sub(num1, num2){
return num1 - num2
}
let num1 = 10, num2 = 20;
calc(num1,num2,sub)
2、函数作为返回值
1)简单的案例
function foo(){
function bar(){
console.log("函数作为返回值")
}
// 返回bar函数
return bar
}
const fn = foo()
fn() // 这个其实是在指执行bar函数
2)参数相加
/**
* 按理说,下面的fun执行结束,count就会销毁,但是因为闭包的原因,导致他不会进行销毁
*/
function fun(count){
return function add(num){
return count + num
}
}
const fn2 = fun(3)
console.log(fn2(9)) // 12
console.log(fn2(11)) // 14
3、函数和方法的具体区别
函数:独立的function,称之为是一个函数
方法:当我们的一个函数属于某一个对象时,我们称这个函数为这个对象的方法
4、数组中的函数
1)filter的使用
let arr = [11,22,55,44,66]
/**
* filter() 过滤的使用(他是方法)
* 他的返回值是个布尔值,同时也会返回一个 数组
* filter()的参数是个函数,该函数的参数如下:
* item 每个元素
* index 每个元素的下标
* Array 传入的整个数组
*/
const newArr = arr.filter(function (item, index, Array) {
if(item % 2 === 0){
return true
}
})
console.log(newArr) // [ 22, 44, 66 ]
2)map 映射
let arr = [11,22,55,44,66]
/**
* map 映射
* 他有返回值
*/
let newArr = arr.map(function (item, index){
return item + 1
})
console.log(newArr)
3)forEach 迭代
let arr = [11,22,55,44,66]
arr.forEach(function (item, index){
console.log(item,index)
})
4)find 查找元素
let arr = [11,22,55,44,66]
/**
* find 查找
* 他的返回值时布尔值
* 他会返回一个元素
*/
let newArr = arr.find(function (item){
if (item === 22) return item
})
console.log(newArr)
5)findIndex 查找对象所对的索引值
let arr = [ 11, 22, 55, 44, 66 ]
const findIndex = arr.findIndex(function (item){
if (item === 22) return true
})
console.log(findIndex)
6)reduce 累加
let arr = [ 11, 22, 55, 44, 66 ]
/**
* reduce 累加
* 他的参数是个函数 和 初始值
* 该函数的参数 如下:
* preValue 上一次的返回值
* item 当前值
* 初始值 可以按照需求传入,默认不穿的时候为0
*/
let newArr4 = arr.reduce(function (preValue, item){
// 返回一个10,就代表他的第二次来的时候preValue就为十了
// return 10
return preValue + item
}, 0)
console.log(newArr4) // 198
二、JS闭包
1、闭包的初步认识
1)闭包是指有权访问另一个函数作用域中的变量的函数(也就是能够读取其他函数内部变量的函数)
2)作用:函数内部的变量,只能在当前函数内部使用,而你要在函数外部使用函数内部的变量,这个时候,你就要使用闭包来完成
文档:developer.mozilla.org/zh-CN/docs/…
function makeFunc() {
let name = "你好"
function displayName() {
console.log(name)
}
return displayName
}
/**
* 在makeFunc()执行完之后,按理说 它里面的内容是要销毁的(也就是 name 本来是要销毁的)
* 但是在执行displayName这个函数的时候,你依然可以访问到本来要销毁的变量时,依然可以访问到
* 在下面调用(displayName)的时候,依然可以访问到makeFunc函数里面的变量,这个时候,他就形成了闭包
* 闭包的组成:函数 + 可以访问的自由变量
*/
let myFunc = makeFunc()
//myFunc就是displayName这个函数
myFunc();
2、闭包的定义
① 闭包跟函数最大的区别在于,当捕捉闭包的时候,它的 自由变量 会在补充时被确定,这样即使脱离了捕捉时的上下文,它也能照常运行;
② 闭包在实现上是一个结构体(整体),它存储了一个函数和一个关联的环境(相当于一个符号查找表);
③ 闭包是由两个东西组成,一个可以用来返回的函数,一个外层的自由变量,只有能在返回的这个函数里,访问到外层的这个自由变量,这两个东西结合在一起,就组成了严格意义上的闭包;
function foo(){
let name = "哈哈哈"
let age = 18
function bar (){
console.log(name)
console.log(age)
}
return bar
}
/**
* 1、按理说上面的bar函数是要销毁的,但是在执行的时候,你给了他一个return
* 2、这个时候,你在下面定义全局变量的时候,就把他赋值给fn了,这个时候,他就不会被销毁了
*/
let fn = foo()
fn()
三、闭包的内存泄露
1、闭包内存泄露的描述
注:因为有一个父级作用域一直指着,所以才不会销毁,也就是在一个函数里你return了一个函数,但是你在外面又去调用外层函数的时候,这个时候,他会把return里面的那个函数进行赋值,从而导致内存不会进行销毁
2、解决内存泄露的方式
把定义的函数,置为null
3、闭包内存泄露案例
function fun(){
/**
* 传入一个长度
* 后面使用fill作为填充,填充的内容时1
* 一个整数是4个字节
* 填充1的个数是 1024 * 1024
* @type {any[]}
*/
let arr1 = new Array(1024 * 1034).fill(1)
return function (){
console.log(arr1.length)
}
}
// const arrFn = fun()
// arrFn()
let arr2 = []
for (let i = 0; i < 100; i++){
/**
* 必须要有一个引用接收,负责他会销毁
* 这里面有100 函数对象
*/
arr2.push(fun())
}
// 下面是销毁(但是他不是立马销毁的)
arr2 = null
4、闭包中自由变量是怎么销毁的
根据ECMA规范得到,在闭包的父级作用域的自由变量都是保存的,但是js引擎,会帮我们进行优化处理,在定义后的代码里面,如果后面没有指向的话,他在执行的时候,就会帮我们把这个未使用的自由变量销毁掉。
// 请把他引入html中运行
function fun(){
let name = "哈哈哈",
age = 22
function bar() {
debugger
console.log(name)
}
return bar
}
const fn = fun()
fn()
四、闭包的使用
1、闭包实现柯里化
具体的参考阮一峰的函数式编程入门教程:www.ruanyifeng.com/blog/2017/0…
// 柯里化之前
function add(x, y) {
return x + y;
}
console.log(add(1, 2));
// add(1, 2) // 3
// 柯里化之后
function addX(y) {
return function (x) {
return x + y;
};
}
console.log(addX(2)(1));
addX(2)(1) // 3