闭包
让人迷惑的闭包
JS中函数是一等公民
01_函数作为参数的使用
// function foo(arg) {
// console.log("foo", arg)
// }
// foo(123)
// 1. 将函数作为另外一个函数的参数
// function foo(aaaaa) {
// aaaaa()
// }
// function bar() {
// console.log("bar")
// }
// foo(bar)
// 案例
function calc(num1, num2, calcFn) {
// 切记这里面才是关键 这里面是 calcFn(num1, num2)也就等于mul(num1, num2) 传过来的参数 参数要一致
// 这里其实也是调用函数 这里的函数十分灵活,类似于一个格式接受传过来的函数 注函数的格式和参数需要一致
console.log(calcFn(num1, num2));
}
function add(num1, num2) {
return num1 + num2;
}
function sub(num1, num2) {
return num1 - num2;
}
function mul(num1, num2) {
return num1 * num2;
}
var m = 20;
var n = 30;
calc(m, n, mul);
02_函数作为返回值的使用
// js语法允许函数内部再定义函数
// function foo() {
// function bar() {
// console.log("bar")
// }
// 函数作为返回值不要写小括号() 这样就变成调用 而不是返回
// return bar
// }
// var fn = foo()
// fn()
function makeAdder(count) {
function add(num) {
return count + num;
}
return add;
}
// 通过传入 不同的参数来定制一些函数
var add5 = makeAdder(5);
// 这里就是count 5 + num 6 结果为11
console.log(add5(6));
// 这里就是count 5 + num 10 结果为15
console.log(add5(10));
// 这里就是count 10 可以再定义num + num 10 结果为20
var add10 = makeAdder(10);
console.log(add10(10));
var add100 = makeAdder(100);
// 这里就是count 100 可以再定义num + num 100 结果为200
console.log(add100(100));
// 高阶函数: 把一个函数如果接受另外一个函数作为参数,或者该函数会返回另外一个函数作为返回值的函数, 那么这个函数就称之为是一个高阶函数
03_数组中的函数使用
var nums = [10, 5, 11, 100, 55];
// var newNums = []
// for (var i = 0; i < nums.length; i++) {
// var num = nums[i]
// if (num % 2 === 0) {
// newNums.push(num)
// }
// }
// console.log(newNums)
// 函数和方法的区别:
// 函数function: 独立的function, 那么称之为是一个函数 foo() {}是函数
function foo() {
}
// 方法method: 当我们的一个函数属于某一个对象时, 我们成这个函数是这个对象的方法 };// obj.foo();是方法
var obj = {
foo: function () {
},
};
obj.foo();
// 高阶函数: 把一个函数如果接受另外一个函数作为参数,或者该函数会返回另外一个函数作为返回值的函数, 那么这个函数就称之为是一个高阶函数
// 1.filter: 过滤 是属性nums对象里面的方法 filter:是数组内置的方法
// [10, 5, 11, 100, 55]
// 10 => true => 不会被放到newNums
// 5 => false => 不会被放到newNums
// 11 => false => 不会被放到newNums
// 100 => true => 不会被放到newNums
// 55 => false => 不会被放到newNums
// nums.filter(function(item,index,arr){})这个回调函数会传入三个参数 并且返回结果是布尔值
// item是每一项,数组里面有几个值回调几次,key是下标,arr是整个数组的引用,如果只用item其他不用写
// var newNums = nums.filter(function(item) {
// return item % 2 === 0 // 偶数
// })
// console.log(newNums)
// 2.map: 映射
// [10, 5, 11, 100, 55]
// var newNums2 = nums.map(function(item) {
// return item * 10
// })
// console.log(newNums2)
// 3.forEach: 迭代 forEach函数是没有返回值的
// nums.forEach(function(item) {
// 打印数组中的每一项
// console.log(item)
// })
// 4.find/findIndex
// es6~12
// 找到第一个return返回为true的值
// var item = nums.find(function(item) {
// return item === 11
// })
// console.log(item)
// var friends = [
// {name: "why", age: 18},
// {name: "kobe", age: 40},
// {name: "james", age: 35},
// {name: "curry", age: 30},
// ]
// 找到对应对象的每一项
// var findFriend = friends.find(function(item) {
// return item.name === 'james'
// })
// console.log(findFriend)
// 找到对应对象的索引值.findIndex
var friendIndex = friends.findIndex(function (item) {
return item.name === "james";
});
// console.log(friendIndex)
// 5.reduce:
// nums.reduce
// [10, 5, 11, 100, 55]
// 最基础的累加方法
// var total = 0
// for (var i = 0; i < nums.length; i++) {
// total += nums[i]
// }
// console.log(total)
// 高阶函数的写法 累加
// prevValue: 0, item: 10
// prevValue: 10, item: 5
// prevValue: 15, item: 11
// 函数的参数prevValue是返回上一次的值 item是这次的值
var total = nums.reduce(function (prevValue, item) {
return prevValue + item;
// 这个函数接受两个参数 第一个参数是函数,第二个参数可以传入初始化值,这里设置默认初始化的值为0
}, 0);
// 返回最终的总和结果
console.log(total);
JS中闭包的定义
04_高阶函数执行过程
function foo() {
function bar() {
console.log("bar")
}
return bar
}
var fn = foo()
fn()
05_闭包到底是什么
function foo() {
// AO: 销毁
var name = "foo"
function bar() {
console.log("bar", name)
}
return bar
}
var fn = foo()
fn()
var name = "why"
function demo() {
console.log(name)
}
// 可以访问: test就是闭包
// 有访问到: test就是不是闭包
function test() {
// 1
// 10000
}
06_函数的执行过程的内存
function foo() {
var name = "foo"
var age = 18
}
function test() {
console.log("test")
}
foo()
test()
闭包的内存泄露
07_函数的执行过程的内存
扫描二维码关注公众号,回复:
16866415 查看本文章
function foo() {
var name = "foo"
var age = 18
function bar() {
console.log(name)
console.log(age)
}
return bar
}
var fn = foo()
fn()
fn = null
foo = null
// fn()
// fn()
// fn()
// fn()
// fn()
闭包的内存泄漏测试
01_js闭包内存泄露案例
function createFnArray() {
// var arr = [1, 1, 1, 1, 1, 1, 1, 1,1, 1,1, 1,1 ]
// 一个整数占据四个字节 所以一共占据 1024*1024*4 个字节(byte) 1024字节(byte)=1kb 1024kb =1m
// 这个数组的占据的空间是4M x 100 + 其他的内存 = 400M+
// 1 -> number -> 8byte -> 8M
// js: 10 3.14 -> number -> 8byte ? js引擎
// 8byte => 2的64次方 => 4byte
// 小的数字类型, 在v8中成为Sim, 小数字 2的32次方 只占据4个字节
// 总结 + —2的32次方的数字一般情况下占据8byte 但是通过V8引擎的优化只占据4个字节byte
// new Array(1024 * 1024)创建数组长度为1024 *1024的数组
// fill()对数组的内容进行填充 填充什么值就在参数中写入什么 。
var arr = new Array(1024 * 1024).fill(1); //创建一个长度为1024 *1024的数组,并每个位置都填入数字1
return function () {
console.log(arr.length);
};
}
// 我们在这里调用 createFnArray()函数相当于调用function() { console.log(arr.length)} 这个函数,这个函数引用这上面newArray这个对象,所以不会销毁
// var arrayFn = createFnArray()
// arrayFn = null
// 100 * 100 = 10000 = 10s
var arrayFns = [];
for (var i = 0; i < 100; i++) {
setTimeout(() => {
arrayFns.push(createFnArray());
}, i * 100);
}
// arrayFns = null 定时器 确定上面的代码执行完毕在清空数组 ,销毁并不是一点点销毁回收,而是等到一定的时间根据GC自己的算法我的优化 来回来在进行销毁
setTimeout(() => {
for (var i = 0; i < 10; i++) {
setTimeout(() => {
arrayFns.pop();
}, 100 * i);
}
}, 10000);
02_js闭包引用的自由变量销毁
function foo() {
var name = "why";
var age = 18;
function bar() {
debugger; //用debugger在控制台打印测试--发现被浏览器引擎销毁优化
console.log(name);
// age并没有使用 按照ECMA的规范 整个AO对象中的name和age都不会销毁--应为会被函数调用,但是js引擎V8引擎为了优化性能,后面用不到age,应该销毁age
// console.log(age)
}
return bar;
}
var fn = foo();
fn();