js匿名函数和闭包总结
js匿名函数和闭包总结
一、总结
一句话总结:匿名函数的最主要作用是创建闭包,闭包就是将函数内部和函数外部连接起来的一座桥梁。内层的函数可以使用外层函数的所有变量,即使外层函数已经执行完毕。闭包可以用来模仿块级作用域等等。
匿名函数 闭包:匿名函数的最主要作用是创建闭包,闭包就是将函数内部和函数外部连接起来的一座桥梁。内层的函数可以使用外层函数的所有变量,即使外层函数已经执行完毕。闭包可以用来模仿块级作用域等等
1、js匿名函数基本格式?
匿名函数 赋值 变量
立即执行 匿名函数 ()()
return 匿名函数
没有名字 可以赋值
//情况1.把匿名函数赋值给变量 var fn=function (){ alert('我是匿名函数') } alert(fn) //会将函数表达式输出 fn() //情况2.匿名函数通过表达式自我执行 (function (){ alert('我是匿名函数') } )()
var name='The Window'; var obj={ name:'my obj', get:function(){ return function(){ return this.name; } } } alert(obj.get()()) //这次返回的是全局变量 'The Window' alert(obj.get().call(obj))//这次又返回的是'my obj',因为call()强制改变了this的指向
2、js 中的匿名函数如何自己运行?
(匿名函数)()
因为js中的()可以将函数代码段运行,也可以将变量转化为函数
(function(m,n){ alert(m+n) })(1000,1000)
匿名函数
没有函数名字的函数
- 单独的匿名函数是无法运行和调用的
- 可以把匿名函数赋值给变量
- 通过表达式自我执行,语法:(匿名函数)()
- 匿名函数传递参数,语法:(匿名函数)(参数)
3、js中的匿名函数如何传递参数?
()() 括号 传参
匿名就是()()的格式
//匿名函数传递参数 function myfn(m,n){ alert(m+n) } myfn(100,100); (function(m,n){ alert(m+n) })(1000,1000)
4、js变量后的()可以表示哪些意思?
变量 变成 方法
执行 匿名函数
a、将变量变成方法
b、放在匿名函数后面,用来执行匿名函数,这样做的话匿名函数本身也要用括号扩起来,
5、js中的闭包是什么?
函数 嵌套 匿名函数
在一个函数中嵌套了一个匿名函数,匿名函数可以访问这个函数里面的变量
闭包的相关概念
- 闭包的英文单词是closure,是指有权访问另一个函数作用域中变量的函数。
- 在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。内层的函数可以使用外层函数的所有变量,即使外层函数已经执行完毕。
- 这是JavaScript中非常重要的一部分知识,因为使用闭包可以大大减少我们的代码量,使我们的代码看上去更加清晰等等,总之功能十分强大。
注:这些概念了解即可,接下来我们将通过实例来进行了解。
6、匿名函数最大的作用是什么?
创建闭包
闭包的相关概念
- 闭包的英文单词是closure,是指有权访问另一个函数作用域中变量的函数。
- 在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。内层的函数可以使用外层函数的所有变量,即使外层函数已经执行完毕。
- 这是JavaScript中非常重要的一部分知识,因为使用闭包可以大大减少我们的代码量,使我们的代码看上去更加清晰等等,总之功能十分强大。
注:这些概念了解即可,接下来我们将通过实例来进行了解。
7、alert(myfn);alert(myfn());alert(myfn()())得到的结果分别是什么?
整个函数表达式 匿名函数表达式 执行结果
function myfn(){ return function (){ return('**********') } } //alert(myfn) //输出整个函数表达式 //alert(myfn()) //输出匿名函数表达式 //调用方式1 alert(myfn()())
8、闭包的用途是什么?
局部变量 常驻内存
闭包的相关知识点
- 常见的方式是在函数内部创建另一个函数
- 闭包的第一个用途:通过闭包可以访问局部变量
- 闭包的第二个用途:可以让局部变量的值始终保持在内存中
- 优点:可以把局部变量驻留在内存中,可以避免使用全局变量;
全局变量在复杂程序中会造成许多麻烦(比如命名冲突,垃圾回收等),所以推荐使用私有的,封装的局部变量。而闭包可以实现这一点。
- 缺点:由于闭包里作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存;所以过度使用闭包会导致性能下降;
- 优点:可以把局部变量驻留在内存中,可以避免使用全局变量;
- 循环函数中的匿名函数和闭包问题
9、add()()的方式为何无法让闭包实现函数局部变量的累加?
初始化
// add()();add()();add()();//这种调用方式会出错,因为每次调用 num都会初始化一次;
var fn=add() ;fn();fn();fn();//只在这里初始化一次,后边调用的时候执行的是里边的匿名函数
function add(){ var num= 100; // 这里改为局部变量; return function(){ num++; alert(num); } }; // add()();add()();add()();//这种调用方式会出错,因为每次调用 num都会初始化一次; var fn=add()//只在这里初始化一次,后边调用的时候执行的是里边的匿名函数 fn();fn();fn(); fn=null //应及时解除引用,否则会占用更多存
10、如何解决闭包将局部变量注入内存的缺点?
赋值 null
fn=null //应及时解除引用,否则会占用更多存
闭包的相关知识点
- 常见的方式是在函数内部创建另一个函数
- 闭包的第一个用途:通过闭包可以访问局部变量
- 闭包的第二个用途:可以让局部变量的值始终保持在内存中
- 优点:可以把局部变量驻留在内存中,可以避免使用全局变量;
全局变量在复杂程序中会造成许多麻烦(比如命名冲突,垃圾回收等),所以推荐使用私有的,封装的局部变量。而闭包可以实现这一点。
- 缺点:由于闭包里作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存;所以过度使用闭包会导致性能下降;
- 优点:可以把局部变量驻留在内存中,可以避免使用全局变量;
- 循环函数中的匿名函数和闭包问题
function add(){ var num= 100; // 这里改为局部变量; return function(){ num++; alert(num); } }; // add()();add()();add()();//这种调用方式会出错,因为每次调用 num都会初始化一次; var fn=add()//只在这里初始化一次,后边调用的时候执行的是里边的匿名函数 fn();fn();fn(); fn=null //应及时解除引用,否则会占用更多存
11、js如何让循环中的匿名函数和闭包接收到的i正确?
匿名函数 立即执行
匿名函数 内 匿名函数 传参数
函数内部的匿名函数立即执行
//让匿名函数立即执行来赋值 function fun(){ var arr=[]; for(var i=0; i<5; i++){ arr[i]=(function(){ return '元素'+i; })() } return arr } var Bb=fun() alert(Bb.length) alert(Bb) // for(var i=0; i<5; i++){ // alert(Bb[i]) // }
匿名函数内部加一层匿名函数(加传参):常见结构
1 //通过闭包让局部变量驻留在内存中 2 function fun(){ 3 var arr=[]; 4 for(var i=0; i<5; i++){ 5 arr[i]=function(n){ 6 return function(){ 7 return '元素'+n; 8 } 9 }(i) 10 } 11 return arr 12 } 13 var Bb=fun() 14 //alert(Bb.length) 15 // alert(Bb[0]()) 16 for(var i=0; i<5; i++){ 17 //alert(Bb[i]) 18 alert(Bb[i]()) 19 } 20 //这次成功的输出了 ‘元素0 元素1 元素2 元素3 元素4 ’,而不再都是[元素5] 21 /* 22 1.这里的匿名函数有一个参数 n,也就是最终将返回的结果数值; 23 2.在调用每个匿名函数时传入变量i 24 3.变量i的当前值会赋值给n, 25 4.匿名函数内部创建并返回了一个访问n的闭包 26 5.如此数组arr中的每个函数中都有了自己的n变量的一个副本(闭包可以将局部变量贮存在内存中) 27 28 */
闭包的相关知识点
- 常见的方式是在函数内部创建另一个函数
- 闭包的第一个用途:通过闭包可以访问局部变量
- 闭包的第二个用途:可以让局部变量的值始终保持在内存中
- 优点:可以把局部变量驻留在内存中,可以避免使用全局变量;
全局变量在复杂程序中会造成许多麻烦(比如命名冲突,垃圾回收等),所以推荐使用私有的,封装的局部变量。而闭包可以实现这一点。
- 缺点:由于闭包里作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存;所以过度使用闭包会导致性能下降;
- 优点:可以把局部变量驻留在内存中,可以避免使用全局变量;
- 循环函数中的匿名函数和闭包问题
12、js匿名函数中的this指代的是谁?
window
闭包中的this问题
- 之前的课程中讲过this是在运行时基于函数的执行环境来绑定的
- 全局函数中的this是window,而当函数作为某个对象的方法调用时,this就是指的那个对象......
- 匿名函数的执行环境具有全局性,this通常是指向window的。
- 可以使用对象冒充强制改变this的指向
- 将this赋值给一个变量,闭包访问这个变量
var name='The Window'; var obj={ name:'my obj', get:function(){ return function(){ return this.name; } } } alert(obj.get()()) //这次返回的是全局变量 'The Window'
13、如何让匿名函数中的this指向当前对象?
对象冒充 闭包访问
匿名函数的执行环境具有全局性,this通常是指向window的。
- 可以使用对象冒充强制改变this的指向
- 将this赋值给一个变量,闭包访问这个变量
对象冒充
var name='The Window'; var obj={ name:'my obj', get:function(){ return function(){ return this.name; } } } alert(obj.get()()) //这次返回的是全局变量 'The Window' alert(obj.get().call(obj))//这次又返回的是'my obj',因为call()强制改变了this的指向
闭包访问
var name='The Window'; var obj={ name:'my obj', get:function(){ //这里的this指的是对象,这里为obj var self=this return function(){ //闭包里的this指的是window return self.name; } } } alert(obj.get()())
14、如何模仿块级作用域?
立即执行 匿名函数
用匿名函数
将需要放进块级作用域的东西放进一个立即执行的匿名函数里面
js没有块级作用域代码
function myfun() { for(var i=0;i<5;i++){ } //i不会因为离开了for块就失效; var i; //重新声明后i还是5, alert(i) //此时的i=5 }
模仿块级作用域
<script> //模仿块级作用域 function myfun() { (function(){ for(var i=0;i<5;i++){ alert(i) } })() // 这里定义并立即调用了一个匿名函数; alert(i) //此时的i已结不存在 会报错:'i is not defined' } myfun() </script>
块级作用域又叫私有作用域,但是JS没有块级作用域的概念;这意味着在块语句(比如for语句)中定义的变量,不会因为离开了for块就失效。
- 使用了块级作用域后,匿名函数中定义的任何变量,都会在执行结束时被销毁;
- 一般来说,我们都应该尽可能少向全局作用域中添加变量和函数;过多的全局变量和函数很容易导致命名冲突
- 使用块级作用域,每个开发者既可以使用自己的变量,又不必担心搞乱全局作用域;
- 在全局作用域中使用块级作用域可以减少闭包占用的内存问题.
15、js对象如何创建私有变量和类似其它语言的get、set方法(特权方法)?
私有变量 对象 private 权限
特权方法 this 属性 匿名方法
this方式定义的变量方法外部可以访问
var方式定义的吧变量方法外部无法访问
私有变量也就是例如java对象的private权限的变量
var name='张三'; // 私有变量;
特权方法其实是this的一个属性,这个属性指向一个匿名方法,因为是当前对象的属性,所以外部可以访问
this.getname=function(){ // 对外公共的特权方法;
return name;
}
私有变量
JavaScript没用私有属性的概念;所有的属性都是公用的;
私有变量的概念:在任何函数中定义的变量,都是私有变量,因为不能在函数外部访问这些变量;
- 私有变量:包括函数的参数/局部变量和在函数内部定义的其他函数;
- 特权方法:内部创建一个闭包,闭包可以访问私有变量;因此创建用于访问私有变量的公用方法,称作特权方法
- 可以通过构造方法传参来访问私有变量
这种方法的缺点是会为每一个实例创建一组新的方法,不能实现共享。
function People(){ var name='张三'; // 私有变量; var age='30'; function say(){ // 私有函数; return '我是......'; } this.getname=function(){ // 对外公共的特权方法; return name; } this.getsay=function(){ return say() } } var p=new People() alert(p.getname()) alert(p.getsay())
16、对象创建的时候,用this声明的属性和用var定义的局部变量创建的对象是不同的?
this 属性 访问 变量 public 权限
var 函数变量 无法访问 变量 private 权限
使用this方式的话是对象的属性,外部可以轻松访问变量,相对于java中给变量设置权限public
使用var方式的话,是函数变量(局部变量,私有变量),函数外部无法访问,相对于java中非变量设置private权限,使用特权方法可以访问,特权方法相对于get/set方法,
使用this方式
function People(){ this.name='张三'; this.age='30'; this.say=function (){ return '我是'+this.name+'......'; } }
使用var方式
function People(){ var name='张三'; // 私有变量; var age='30'; function say(){ // 私有函数; return '我是......'; } this.getname=function(){ // 对外公共的特权方法; return name; } this.getsay=function(){ return say() } }
17、js对象如何创建静态变量(静态私有变量)?
立即执行的闭包 原型 特权方法 块级作用域
静态私有变量
通过块级作用域(私有作用域)中定义私有变量或函数,创建对外公共的特权方法;
- 首先创建私有作用域
- 定义私有变量或函数
- 定义构造函数和特权方法
- 这种方式创建的私有变量因为使用原型而实现共享。
- 同时由于共享,实例也就没有自己的私有变量。
1 (function(){ 2 var name='张三'; 3 User=function(){} //构造函数 4 User.prototype.getName=function(){ 5 return name 6 } 7 User.prototype.setName=function(value){ 8 name=value 9 }; 10 })() 11 12 var VIP1=new User() //因为Uer()是私有函数,所以外部无法访问。 13 //alert(VIP1.getName()) 14 VIP1.setName('李四') 15 //alert(VIP1.getName()) 16 17 var VIP2=new User() 18 alert(VIP2.getName())
18、js对象为何会用到静态私有变量(静态方法)?
共享
共享肯定是要通过原型的
私有变量方式的缺点是会为每一个实例创建一组新的方法,不能实现共享。
19、下面代码是匿名函数么?
函数 名字
get:function(){
return this.name;
}
}
这只是json写法的对象中的函数而已,就是讲函数赋值给一个变量,这个函数是有名字的,所以不叫闭包
get:function(){
return function(){
return this.name;
}
}
这个才是闭包
这个不是
var obj={ name:'my obj', get:function(){ return this.name; } } alert(obj.get()) //返回 'my obj'
这个是
var name='The Window'; var obj={ name:'my obj', get:function(){ return function(){ return this.name; } } } alert(obj.get()()) //这次返回的是全局变量 'The Window' alert(obj.get().call(obj))//这次又返回的是'my obj',因为call()强制改变了this的指向
二、内容在总结中