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 相关文章推荐
实例:尽可能写友好的Javascript代码
Oct 09 Javascript
javascript按位非运算符的使用方法
Nov 14 Javascript
JavaScript实现Flash炫光波动特效
May 14 Javascript
干货分享:让你分分钟学会javascript闭包
Dec 25 Javascript
jQuery基于ajax实现页面加载后检查用户登录状态的方法
Feb 10 Javascript
layer弹出层中H5播放器全屏出错的解决方法
Feb 21 Javascript
seajs实现强制刷新本地缓存的方法分析
Oct 16 Javascript
Servlet3.0与纯javascript通过Ajax交互的实例详解
Mar 18 Javascript
用npm安装vue和vue-cli,并使用webpack创建项目的方法
Sep 28 Javascript
vue计算属性computed、事件、监听器watch的使用讲解
Jan 21 Javascript
koa大型web项目中使用路由装饰器的方法示例
Apr 02 Javascript
详解javascript中var与ES6规范中let、const区别与用法
Jan 11 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
php socket客户端及服务器端应用实例
2014/07/04 PHP
php启用sphinx全文搜索的实现方法
2014/12/24 PHP
php实现对象克隆的方法
2015/06/20 PHP
PHP实现腾讯与百度坐标转换
2017/08/05 PHP
Laravel-admin之修改操作日志的方法
2019/09/30 PHP
laravel-admin 实现在指定的相册下添加照片
2019/10/21 PHP
在 Laravel 6 中缓存数据库查询结果的方法
2019/12/11 PHP
javascript+dom树型菜单类,希望朋友们一起进步
2007/05/03 Javascript
Wordpress ThickBox 点击图片显示下一张图的修改方法
2010/12/11 Javascript
js里怎么取select标签里的值并修改
2012/12/10 Javascript
js中用window.open()打开多个窗口的name问题
2014/03/13 Javascript
html文本框提示效果的示例代码
2014/06/28 Javascript
JS实现一个列表中包含上移下移删除等功能
2014/09/24 Javascript
JavaScript利用闭包实现模块化
2017/01/13 Javascript
整理关于Bootstrap导航的慕课笔记
2017/03/29 Javascript
AngularJS常见过滤器用法实例总结
2017/07/06 Javascript
不使用 JS 匿名函数理由
2017/11/17 Javascript
vue下使用nginx刷新页面404的问题解决
2019/08/02 Javascript
js 判断当前时间是否处于某个一个时间段内
2019/09/19 Javascript
详解微信小程序工程化探索之webpack实战
2020/04/20 Javascript
[43:24]VG vs Serenity 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
基于python时间处理方法(详解)
2017/08/14 Python
python+influxdb+shell编写区域网络状况表
2018/07/27 Python
在Pandas中处理NaN值的方法
2019/06/25 Python
python实现猜数游戏
2020/03/27 Python
Python-jenkins 获取job构建信息方式
2020/05/12 Python
英国巧克力贸易公司:Chocolate Trading Company
2017/03/21 全球购物
美国首屈一指的礼品篮供应商:GiftTree
2018/01/06 全球购物
new修饰符是起什么作用
2015/06/28 面试题
员工培训心得体会
2013/12/30 职场文书
渡河少年教学反思
2014/02/12 职场文书
学习标兵获奖感言
2014/02/20 职场文书
销售内勤岗位职责
2014/04/15 职场文书
写字楼租赁意向书
2014/07/30 职场文书
幼儿园庆元旦主持词
2015/07/06 职场文书
2019企业文化管理制度范本!
2019/08/06 职场文书