菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
194
0

js 闭包

原创
05/13 14:22
阅读数 75833

定义

闭包:有权访问另一个函数作用域中的变量的函数。

解析

相信刚看到这个定义,很多人肯定很迷糊,现在给出示例。

  1. function func1(){
  2. var a = 1;
  3. function func2(){
  4. a++;
  5. console.log(a);
  6. };
  7. return func2;
  8. }
  9. var res = func1();//等于func2
  10. res();//等于func2(),输出2

首先定义了func1,func2两个函数,函数func1嵌套函数func2,然后再func1中定义变量a,再在func2中调用a。 这里的func2就是一个闭包,他有权访问func1函数中的变量。

作用

①可以读取函数内部的变量,且私有。
②变量可以长期驻扎在内存中。
现在我们需要实现一个变量完成计数器的任务,每次调用+1

1.不使用闭包,使用全局变量完成
  1. var count = 1;
  2. function func(){
  3. count++;
  4. console.log(count);
  5. }
  6. func();// 输出2
  7. func();// 输出3
2.不使用闭包,使用局部变量完成
  1. function func(){
  2. var count = 1;
  3. count++;
  4. console.log(count);
  5. }
  6. func();// 输出2
  7. func();// 输出2

很明显,只是使用局部变量无法达成我们要求,因为局部变量有一个性质,当函数执行完毕时局部变量即刻销毁,所以我们调用两次func函数,对于变量count来说每次都是新的。上面的全局变量方法虽然能够实现要求,但是不合符数据私有安全,而且也容易造成数据交错,不利于程序的移植。 这个时候就体现出闭包的作用性了,既可以保证数据私有,又可以长期驻扎内存中不会被销毁。

  1. function func1(){
  2. var count = 0;
  3. function func2(){
  4. count++;
  5. console.log(count);
  6. }
  7. return func2;
  8. }
  9. var obj = func1(); // func1() ==> func2
  10. obj(); // 1
  11. obj(); // 2
  12. obj(); // 3

解惑

虽然闭包的概念很好理解,但是它衍生出来的程序却不一定很好理解,下面的疑问也是博主我在学习的时候遇到的,进行总结一下。

1.函数数组

  1. function func1(){
  2. var arr = new Array();
  3. for(var i = 0;i<3;i++){
  4. arr[i] = function(){
  5. return i;
  6. }
  7. }
  8. return arr;
  9. }
  10. var array = func1();
  11. console.log(array[0]()); //输出3
  12. console.log(array[1]()); //输出3
  13. console.log(array[2]()); //输出3

这段程序的本意也很明了,就是新建一个数组arr,并且将值0,1,2分别赋给a[0],a[1],a[2],但是结果却出乎意料。 这里比较通俗易懂的解释是,func1中的for循环先执行匿名函数并不执行(因为没有调用),当for循环执行完毕后,在我们范围arrayi的时候才会调用arr[i]的匿名函数,而for循环结束以后i的值为3,匿名函数中由于i并没有赋值,所以他会从外部寻找i值,而这个i值此时是3,所以arr[0-2]的值均为3。造成的主要原因就是这里所有的匿名函数都调用的同一个func1中的同一个i。所以,我们可以创建另一个匿名函数强制让闭包行为符合预期

  1. function func1(){
  2. var arr = new Array();
  3. for(var i = 0;i<3;i++){
  4. arr[i] = function(num){
  5. return num;
  6. }(i);
  7. }
  8. return arr;
  9. }
  10. var array = func1();
  11. console.log(array[0]); //输出0
  12. console.log(array[1]); //输出1
  13. console.log(array[2]); //输出2

注意下上面的输出不一样,array后面没有(),因为在匿名函数后面加了个(i),在函数后面加括号表示执行,所以此时func1()函数里面arr保存的是数值。

2.this问题

  1. var name = "The Window";
  2. var object = {
  3. name:"My Object",
  4. getNameFunc : function(){
  5. return function(){
  6. return this.name;
  7. };
  8. }
  9. }
  10. console.log(object.getNameFunc()()); // "The Window"

这里暂时理解为匿名函数的执行环境具有全局性的(其他更好的答案还没发现,如果有日后在改)。 修改

  1. var name = "The Window";
  2. var object = {
  3. name:"My Object",
  4. getNameFunc : function(){
  5. var that = this;
  6. return function(){
  7. return that.name;
  8. };
  9. }
  10. }
  11. console.log(object.getNameFunc()()); // "My Object"

这里将object的this先赋值给that,然后再匿名函数中使用that就可以找到object的this了. 
看了上面的关于this的代码,在看看下面

  1. var scope = "global scope";
  2. function checkscope(){
  3. var scope = "local scope";
  4. function f() {
  5. return scope;
  6. }
  7. return f();
  8. }
  9. console.log(checkscope()); // local scope

是不是混淆了,为什么上面的输出全局变量,而这段代码输出的局部变量,一定要注意上面的那个是this.name,而这里的代码只是输出变量,我们现在看看这段代码的this指向就明了了。

  1. var scope = "global scope";
  2. function checkscope(){
  3. var scope = "local scope";
  4. function f() {
  5. // 输出[object Window] global scope
  6. console.log(this + " " + this.scope);
  7. return scope;
  8. }
  9. return f();
  10. }
  11. console.log(checkscope()); // local scope

所以对于this来说匿名函数总是指向window的,但是变量确实符合传递链的。所谓变量传递链就是层层向上查找,如

  1. var a = 1;
  2. function func1(){
  3. function func2(){
  4. console.log(a);
  5. }
  6. return func2;
  7. }
  8. func1()(); // 1

func2要输出a,但是func2没有,就向func1查找,发现func1也没有,就找到了全局的a。

关于全局变量还有局部变量的内容,以后还会专门写篇文章进行总结,尽情期待,由于闭包博主也是刚刚学,肯定有写的不周到的地方,希望大家能够指出,闭包的内容就到此为止。

参考文献:

定义

闭包:有权访问另一个函数作用域中的变量的函数。

解析

相信刚看到这个定义,很多人肯定很迷糊,现在给出示例。

  1. function func1(){
  2. var a = 1;
  3. function func2(){
  4. a++;
  5. console.log(a);
  6. };
  7. return func2;
  8. }
  9. var res = func1();//等于func2
  10. res();//等于func2(),输出2

首先定义了func1,func2两个函数,函数func1嵌套函数func2,然后再func1中定义变量a,再在func2中调用a。 这里的func2就是一个闭包,他有权访问func1函数中的变量。

作用

①可以读取函数内部的变量,且私有。
②变量可以长期驻扎在内存中。
现在我们需要实现一个变量完成计数器的任务,每次调用+1

1.不使用闭包,使用全局变量完成
  1. var count = 1;
  2. function func(){
  3. count++;
  4. console.log(count);
  5. }
  6. func();// 输出2
  7. func();// 输出3
2.不使用闭包,使用局部变量完成
  1. function func(){
  2. var count = 1;
  3. count++;
  4. console.log(count);
  5. }
  6. func();// 输出2
  7. func();// 输出2

很明显,只是使用局部变量无法达成我们要求,因为局部变量有一个性质,当函数执行完毕时局部变量即刻销毁,所以我们调用两次func函数,对于变量count来说每次都是新的。上面的全局变量方法虽然能够实现要求,但是不合符数据私有安全,而且也容易造成数据交错,不利于程序的移植。 这个时候就体现出闭包的作用性了,既可以保证数据私有,又可以长期驻扎内存中不会被销毁。

  1. function func1(){
  2. var count = 0;
  3. function func2(){
  4. count++;
  5. console.log(count);
  6. }
  7. return func2;
  8. }
  9. var obj = func1(); // func1() ==> func2
  10. obj(); // 1
  11. obj(); // 2
  12. obj(); // 3

解惑

虽然闭包的概念很好理解,但是它衍生出来的程序却不一定很好理解,下面的疑问也是博主我在学习的时候遇到的,进行总结一下。

1.函数数组

  1. function func1(){
  2. var arr = new Array();
  3. for(var i = 0;i<3;i++){
  4. arr[i] = function(){
  5. return i;
  6. }
  7. }
  8. return arr;
  9. }
  10. var array = func1();
  11. console.log(array[0]()); //输出3
  12. console.log(array[1]()); //输出3
  13. console.log(array[2]()); //输出3

这段程序的本意也很明了,就是新建一个数组arr,并且将值0,1,2分别赋给a[0],a[1],a[2],但是结果却出乎意料。 这里比较通俗易懂的解释是,func1中的for循环先执行匿名函数并不执行(因为没有调用),当for循环执行完毕后,在我们范围arrayi的时候才会调用arr[i]的匿名函数,而for循环结束以后i的值为3,匿名函数中由于i并没有赋值,所以他会从外部寻找i值,而这个i值此时是3,所以arr[0-2]的值均为3。造成的主要原因就是这里所有的匿名函数都调用的同一个func1中的同一个i。所以,我们可以创建另一个匿名函数强制让闭包行为符合预期

  1. function func1(){
  2. var arr = new Array();
  3. for(var i = 0;i<3;i++){
  4. arr[i] = function(num){
  5. return num;
  6. }(i);
  7. }
  8. return arr;
  9. }
  10. var array = func1();
  11. console.log(array[0]); //输出0
  12. console.log(array[1]); //输出1
  13. console.log(array[2]); //输出2

注意下上面的输出不一样,array后面没有(),因为在匿名函数后面加了个(i),在函数后面加括号表示执行,所以此时func1()函数里面arr保存的是数值。

2.this问题

  1. var name = "The Window";
  2. var object = {
  3. name:"My Object",
  4. getNameFunc : function(){
  5. return function(){
  6. return this.name;
  7. };
  8. }
  9. }
  10. console.log(object.getNameFunc()()); // "The Window"

这里暂时理解为匿名函数的执行环境具有全局性的(其他更好的答案还没发现,如果有日后在改)。 修改

  1. var name = "The Window";
  2. var object = {
  3. name:"My Object",
  4. getNameFunc : function(){
  5. var that = this;
  6. return function(){
  7. return that.name;
  8. };
  9. }
  10. }
  11. console.log(object.getNameFunc()()); // "My Object"

这里将object的this先赋值给that,然后再匿名函数中使用that就可以找到object的this了. 
看了上面的关于this的代码,在看看下面

  1. var scope = "global scope";
  2. function checkscope(){
  3. var scope = "local scope";
  4. function f() {
  5. return scope;
  6. }
  7. return f();
  8. }
  9. console.log(checkscope()); // local scope

是不是混淆了,为什么上面的输出全局变量,而这段代码输出的局部变量,一定要注意上面的那个是this.name,而这里的代码只是输出变量,我们现在看看这段代码的this指向就明了了。

  1. var scope = "global scope";
  2. function checkscope(){
  3. var scope = "local scope";
  4. function f() {
  5. // 输出[object Window] global scope
  6. console.log(this + " " + this.scope);
  7. return scope;
  8. }
  9. return f();
  10. }
  11. console.log(checkscope()); // local scope

所以对于this来说匿名函数总是指向window的,但是变量确实符合传递链的。所谓变量传递链就是层层向上查找,如

  1. var a = 1;
  2. function func1(){
  3. function func2(){
  4. console.log(a);
  5. }
  6. return func2;
  7. }
  8. func1()(); // 1

func2要输出a,但是func2没有,就向func1查找,发现func1也没有,就找到了全局的a。

关于全局变量还有局部变量的内容,以后还会专门写篇文章进行总结,尽情期待,由于闭包博主也是刚刚学,肯定有写的不周到的地方,希望大家能够指出,闭包的内容就到此为止。

参考文献:
 
分类: Javascript

发表评论

0/200
194 点赞
0 评论
收藏