JavaScript基础篇(6)之函数表达式闭包


Posted in Javascript onDecember 11, 2015

 其实js支持函数闭包的主要原因是因为js需要函数能够保存数据。这里的保存数据是只函数在运行结束以后函数内变量的值也会进行保存。至于为什么js需要在函数内可以保存数据,那就是js是一种函数式语言。在函数内保存数据是函数式语言的一大特征。

回顾前面介绍过的三种定义函数方式

functiosu(numnumreturnunum//函数声明语法定义
vasufunction(numnum)returnunum}//函数表达式定义
vasuneFunction("num""num""returnunum")//Functio构造函数

在分析闭包之前我们先来看看,定义和调用函数容易犯的错误。

例1:

sayHi(); //错误:函数还不存在
var sayHi = function () {
  alert("test");
};

例2:

if (true) {
  function sayHi() {
    alert("1");
  }
} else {
  function sayHi() {
    alert("2");
  }
}
sayHi();//打印结果并不是我们想要的

例3:

var fun1 = function fun2() {
  alert("test");
}
fun2();//错误:函数还不存在

在例1中,我们不能在使用函数声明式语法定义之前调用函数。解决方案:

1.如果使用函数表达式定义函数的话,需要在表达式定义后调用。

var sayHi = function () {
  alert("test");
};
sayHi()

2.使用函数声明式。(这里浏览器引擎会 函数声明提升, 在所有代码执行之前先读取函数声明)

sayHi(); 
function sayHi () {
  alert("test");
};

在例2中,我们预期的结果应该是打印1,实际结果是打印2。

if (true) {
  function sayHi() {
   alert("1");
  }
  } else {
  function sayHi() {
   alert("2");
  }
}
sayHi();//打印结果并不是我们想要的

为什么会这样?正因为 函数声明提升 ,所以浏览器在预解析的时候不会判断if条件,直接解析第二个函数定义的时候覆盖了第一个。

解决方案:

var sayHi;
if (true) {
  sayHi = function () {
   alert("1");
  }
  } else {
  sayHi = function () {
   alert("2");
  }
}
sayHi();

在例3中,发现只能只用fun1()调用,而不能使用fun2()调用。

我自己的理解,真正原因不知道。没找到资料。

因为1: function fun3() { }; 等效与  var fun3 = function fun3() { }; 如图:

JavaScript基础篇(6)之函数表达式闭包 

所以只能只用fun1()调用,而不能使用fun2()调用。

其实这里我还是有疑问的?哪位大神知道,望告知。

既然,fun2在外面不能调用为什么在函数内部能调用?虽然在debugger还是得不到fun1。

JavaScript基础篇(6)之函数表达式闭包 

好了,通过上面的三道题目热身。我们继续今天的主题“闭包”。

1.什么是闭包?

定义:就是有权访问另一个函数作用域的变量的函数

我们先从一个示例函数开始:

例1:

function fun() {
  var a = "张三";
}
fun();//在我们执行完后,变量a就被标记为销毁了

例2:

function fun() {
  var a = "张三";
  return function () {
    alert("test");
  }
}
var f = fun();//同样,在我们执行完后,变量a就被标记为销毁了

例3:

function fun() {
  var a = "张三";
  return function () {
    alert(a);
  }
}
var f = fun();//【现在情况发生变化了,如果a被销毁,显然f被调用的话就不能访问到变量a的值了】
f();//【然后变量a的值正常的被访问到了】
//这就是闭包,当函数A 返回的函数B 里面使用到了函数A的变量,那么函数B就使用了闭包。
示例:
function fun() {
  var a = "张三";
  return function () {
   alert(a);
  }
}
var f = fun();//【现在情况发生变化了,如果a被销毁,显然f被调用的话就不能访问到变量a的值了】
f();//【然后变量a的值正常的被访问到了】

显然,滥用闭包会增大内存的使用。所以非特殊情况尽量不要使用闭包。如果用到了,记得手动设置空引用,内存才能被回收 f = null ;

图解:(不了解作用域链的同学请先看前面的文章 作用域和作用域链 )

JavaScript基础篇(6)之函数表达式闭包 

2.什么是匿名函数? (仅仅只是解释这个概念)

如:(即,没有名字的函数)

JavaScript基础篇(6)之函数表达式闭包 

关于对象中函数的返回值是匿名函数时,this的怪异现象

讲解之前,先清醒下头脑,不要越看越迷糊了。如果迷糊了,那就直接忽略下面的。

var name1 = "张三";
var obj = {
  name1: "李四",      
  fun2: function () {
    alert(this.name1);
  },
  fun3: function () {
    return function () {
      alert(this.name1);
    }
  }
}

obj.fun2();//打印结果"李四"意料之中的。
obj.fun3()();//因为这里返回的是一个函数,所以要再加一对()来调用。打印结果是"张三",意料之外。
//真是百事不得其解啊,什么this指向了全局?
我们前面讲过“ 哪个对象点出来的方法,this就是哪个对象 ”,那我们的  obj.fun3()() 打印的是“张三”也就是说this执行了全局作用域。

我们看看下面的示例也许就知道为什么了。

var name1 = "张三";
var obj = {
  name1: "李四",      
  fun2: function () {
    alert(this.name1);
  },
  fun3: function () {
    return function () {
      alert(this.name1);
    }
  }
}    
//obj.fun3()();
var obj2 = {};
obj2.name1 = "test";
obj2.fun = obj.fun3();
obj2.fun();//打印结果"test",再次证明了“哪个对象点出来的方法,this就是哪个对象”.
var name1 = "张三";
var obj = {
  name1: "李四",
  fun2: function () {
   alert(this.name1);
  },
  fun3: function () {
    return function () {
     alert(this.name1);
    }
  }
}
//obj.fun3()();
var obj2 = {};
obj2.name1 = "test";
obj2.fun = obj.fun3();
obj2.fun();//打印结果"test",再次证明了“哪个对象点出来的方法,this就是哪个对象”.

我们来分解下 obj.fun3()() 先是  obj.fun3() 返回一个匿名函数到了window作用域,然后接着调用this就指向了window了。( 感觉解释有点勉强,也不知道对不,暂时自己先是这么理解的 )

闭包形成的原因:内存释放问题

一般,当函数执行完毕后,局部活动对象会被销毁,内存中仅保存全局作用域,但闭包的情况是不一样的。

闭包的活动对象依然会保存在内存中,于是像上例中,函数调用返回后,变量i是属于活动对象里面的,就是说其栈区还没有释放,但你调用c()的时候i变量保存的作用域链从b()->a()->全局去寻找作用域var i声明所在,然后找到了var i=1;然后在闭包内++i;结果,最后输出的值就是2了;

以上所述是小编给大家分享的JavaScript基础篇(6)之函数表达式闭包,希望大家喜欢。

Javascript 相关文章推荐
3Z版基于jquery的图片复选框(asp.net+jquery)
Apr 12 Javascript
Jquery命名冲突解决的五种方案分享
Mar 16 Javascript
通过实例理解javascript中没有函数重载的概念
Jun 03 Javascript
javascript中字体浮动效果的简单实例演示
Nov 18 Javascript
jQuery EasyUI提交表单验证
Jul 19 Javascript
微信小程序三级联动地址选择器的实例代码
Jul 12 Javascript
vue渲染时闪烁{{}}的问题及解决方法
Mar 28 Javascript
JavaScript callback回调函数用法实例分析
May 08 Javascript
JS实现520 表白简单代码
May 21 Javascript
使用Angular-CLI构建NPM包的方法
Sep 07 Javascript
mpvue项目中使用第三方UI组件库的方法
Sep 30 Javascript
微信小程序云开发实现云数据库读写权限
May 17 Javascript
jQuery弹簧插件编写基础之“又见弹窗”
Dec 11 #Javascript
jQuery点击改变class并toggle及toggleClass()方法定义用法
Dec 11 #Javascript
Express实现前端后端通信上传图片之存储数据库(mysql)傻瓜式教程(二)
Dec 10 #Javascript
Express的路由详解
Dec 10 #Javascript
在 Express 中使用模板引擎
Dec 10 #Javascript
Express实现前端后端通信上传图片之存储数据库(mysql)傻瓜式教程(一)
Dec 10 #Javascript
基于jQuery实现复选框是否选中进行答题提示
Dec 10 #Javascript
You might like
星际争霸 Starcraft 游戏介绍
2020/03/14 星际争霸
PHP array_multisort()函数的使用札记
2011/07/03 PHP
取得单条网站评论以数组形式进行输出
2014/07/28 PHP
jquery制作漂亮的弹出层提示消息特效
2014/12/23 Javascript
对js中回调函数的一些看法
2016/08/29 Javascript
微信小程序tabBar用法实例详解
2017/12/04 Javascript
JS 实现百度搜索功能
2018/02/01 Javascript
ionic grid(栅格)九宫格制作详解
2018/06/30 Javascript
vue-router beforeEach跳转路由验证用户登录状态
2018/12/26 Javascript
jQuery实现根据身份证号获取生日、年龄、性别等信息的方法
2019/01/09 jQuery
ES10 特性的完整指南小结
2019/03/04 Javascript
[01:09:50]VP vs Pain 2018国际邀请赛小组赛BO2 第二场
2018/08/20 DOTA
python检测远程服务器tcp端口的方法
2015/03/14 Python
详解Python中for循环的使用方法
2015/05/14 Python
Python的Flask开发框架简单上手笔记
2015/11/16 Python
用tensorflow实现弹性网络回归算法
2018/01/09 Python
利用Opencv中Houghline方法实现直线检测
2018/02/11 Python
django ListView的使用 ListView中获取url中的参数值方式
2020/03/27 Python
html特殊符号示例 html特殊字符编码对照表
2014/01/14 HTML / CSS
Dr. Martens马汀博士澳大利亚官网:马丁靴鼻祖
2019/07/02 全球购物
环境工程与管理大学毕业生求职信
2013/10/02 职场文书
应届毕业生自我鉴定范文
2013/12/27 职场文书
三好学生个人先进事迹材料
2014/05/17 职场文书
建筑投标担保书
2014/05/20 职场文书
人力资源管理毕业求职信
2014/08/05 职场文书
质量在我心中演讲稿
2014/09/02 职场文书
超市仓管员岗位职责范本
2014/09/18 职场文书
建筑横幅标语
2014/10/09 职场文书
盗窃罪辩护词范文
2015/05/21 职场文书
办公用品管理制度
2015/08/04 职场文书
优秀班干部主要事迹材料
2015/11/04 职场文书
公司员工违法违章行为检讨书
2019/06/24 职场文书
Oracle设置DB、监听和EM开机启动的方法
2021/04/25 Oracle
Vue组件更新数据v-model不生效的解决
2022/04/02 Vue.js
详解OpenCV曝光融合
2022/04/29 Python
MySql数据库触发器使用教程
2022/06/01 MySQL