1. JS函数是一等公民(非常重要)
-
在js中,函数是非常重要的,并且是一等公民
- 那么意味着函数的使用是非常灵活的。
- 函数可以作为另一个函数的参数,也可以作为另一个函数的返回值
自己编写高阶函数
使用内置的高阶函数
1.1 高阶函数
如果一个函数接收另一个函数作为参数时,或这个函数返回另一个函数作为返回值的函数 称之为高阶函数
1.1.1 函数作为参数使用
function calc(num1,num2,calcFn){
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,add)
calc(m,n,sub)
calc(m,n,mul)
1.1.2 函数作为返回值使用
function makeAdder(count){
return function add(num){
return count+num;
}
}
var add5=makeAdder(5);
var add10=makeAdder(10);
console.log(add5(10));
console.log(add5(50));
console.log(add10(50));
1.2 函数(function)和方法(method)的区别
- 函数:当一个function是独立的,不属于任何对象的方法时,则称这个function是函数
- 方法:如果一个function不是独立的,是属于某个对象,则这个function是方法
1.2.1 函数
function foo(){ //foo是一个函数
}
1.2.2 方法
var obj={
foo:function(){ //这个foo函数是obj的一个方法
}
}
1.3 数组一些方法的使用
var nums=[10,5,11,100,55]
// 函数function:独立的function,称之为是函数
// methods:方法:当某个function属于某个对象时,我们称这个函数是这个对象的方法
/**
* * filter
* nums.filter((item,index,arr)=>boolean) //这个回调函数会回调5次,因为有5个元素
* item:值 index:下标 arr:当前这个数组的引用
*/
// * 过滤 返回一个新的数组
var newNums= nums.filter(function(item){
return item%2===0
})
console.log(newNums);
// * map 映射 [10,5,11,100,55] 返回一个新的数组
var mapNums=nums.map(function(item){
return item*10
})
console.log(mapNums); //[ 100, 50, 110, 1000, 550 ]
// * forEach 遍历 没有返回值
nums.forEach(function(item){
console.log(item);
})
// * find(返回数组中元素) findIndex(返回在数组中的索引)
// let a=nums.find(function(item){
// return item===11
// })
// console.log("a:",a);
var friends=[
{name:"why",age:18},
{name:"wjy",age:20},
{name:"hyz",age:22},
{name:"tt",age:18},
]
var item=friends.find(function(item){
return item.name=='wjy'
})
console.log(item); //{ name: 'wjy', age: 20 }
var index=friends.findIndex(function(item){
return item.name=='hyz'
})
console.log("index:",index); //index: 2
// * reduce 可以对原来的数组进行统计或者累加 nums:[10,5,11,100,55]
// * reduce函数有两个参数,第一个参数是回调函数,第二个参数是回调函数的第一个参数的初始值
// * 回调函数的参数preValue:是上一次回调函返回的值
var total=nums.reduce(function(preValue,item){
return preValue+item
},0)
console.log(total); //181
2. JS中闭包的定义
闭包的定义,分成两个:在计算机科学中和在JavaScript中
-
在计算机科学对闭包的定义:(维基百科)
- 闭包(英语:closure) 又称词法闭包(lexical closure)或函数闭包
- 是支持在头等函数的编程语言中,实现词法绑定的一种技术
- 闭包在实现上是一个结构体,它存储了一个函数和一个关联的环境(相当于一个符号查找表)
- 闭包和函数最大的区别在于,当捕捉闭包的时候,它的自由变量会在捕捉时被确定,这样即使脱离了捕捉时的上下文,它也能照常运行。
-
闭包的概念出现于60年代,最早实现闭包的程序是Scheme,那么我们就可以理解为什么JavaScript中有闭包
- 因为js的大量设计是来源于Scheme的
- 一个函数和对其周围状态(Lexical Enviroment 词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)
- 也就是说,闭包你可以在一个内层函数中访问到其外层函数的作用域
- 在js中,每当创建一个函数,闭包就会在函数创建的同时被创建出来
-
codewhy老师的总结
- 一个普通的函数function,如果它可以访问外层作用域的变量,那么它就是闭包
- 从广义上来讲,javascript函数都是闭包
- 从狭义上来讲,javascript中的一个函数,如果访问了外层作用域的变量,那么它就是一个闭包。
- 一个普通的函数function,如果它可以访问外层作用域的变量,那么它就是闭包
2.1 代码示例解析
function foo(){
var name="foo";
function bar(){
console.log(name);
}
return bar;
}
var fn=foo();
fn()
// 闭包包括两个部分: 函数+可访问的自由变量
- 在这里原本应该销毁的foo的AO对象,但是因为bar函数在引用其name,所以并没有销毁
3. 闭包的内存泄漏
本来该被销毁的对象,却一直没有被销毁,会造成内存泄漏。
- 从根对象开始,能够被访问的对象不会被销毁
function foo(){
var name="foo";
var age=18;
function bar(){
console.log(name);
}
return bar;
}
let fn=foo()
fn()
- 例如上面的AO(0X1002),一直未被销毁,但其实bar()函数只执行了一次,应当被销毁
- 因为根对象可到达bar、bar又可到达foo的AO
- 因为GC采用的是清除标记,只要能从根对象可到达的所有对象不会被销毁,不可到达的对象会被销毁
那怎么解决以上的内存泄漏呢?
其实只要fn调用完后,并设置为null,这样从根对象就不可到达bar了,所以最后bar和foo的AO都被销毁了
4.AO不使用的属性
我们来探究一个问题,就是AO不会被销毁时,是否里面的所有属性都不会被销毁?
下面这段代码中name属于闭包的父级作用域中的变量:
我们知道形成闭包之后age一定不会被销毁,那么name是否被销毁呢?
- 这里我使用了断点,我们可以在浏览器上查看结果: