一、介绍

本次博客主要介绍函数表达式的内容,主要是闭包。

二、函数表达式

定义函数的两种方式:一个是函数声明,另一个就是函数表达式。


//1.函数声明写法
function fn2(){
console.log('函数声明');
}
//2.函数表达式写法
var fn1 = function(){
console.log('函数表达式');
}

区别:

1.函数声明是用function后面有函数名,函数表达式是赋值形式给一个变量。

2.函数声明可以提升函数,而函数表达式不会提升

函数提升就是函数会被自动提升到最前方,以至于再调用函数后再声明函数也不会有错:

//例子:
//先调用运行
sayName();
//再声明函数
function sayName(){
console.log('ry');
} //运行结果
'ry'

函数表达式就不会被提升:

//先调用
sayBye();
//函数表达式
var sayBye = function(){
console.log('bye bye');
} //运行报错

但是下面的写法很危险:因为存在函数声明的提升

//书上代码
if(condition){
function sayHi(){
console.log('hi');
}
}
else{
function sayHi(){
console.log('yo');
}
}

解说一下: 这段代码想表达在condition为true时声明sayHi,不然就另一函数sayHi,但是运行结果往往出乎意料,在当前chrome或firefox可能能做到,但是在IE10以下的浏览器(我测试过)往往不会遵循你的意愿,不管condition是true还是false都会输出yo。

这时函数表达式能派上用场了:

//换成函数表达式,没问题因为不会被提升,只有当执行时才赋值
var sayHi = null;
if(condition){
sayHi = function (){
console.log('hi');
}
}
else{
sayHi = function sayHi(){
console.log('yo');
}
}

三、闭包

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

有人觉得闭包很难理解,一开始我也是这样的,我认为那是对一些概念还不够了解。

定义中说明了什么是闭包,最常见的形式就是在函数中再声明一个函数。

重点理解这里:

1.闭包能访问外围函数的变量是因为其作用域链中有外围函数的活动对象(这个活动对象即使在外围函数执行完还会存在,不会被销毁,因为被闭包引用着)。

2.闭包是函数中的函数,也就是说其被调用时也创建执行上下文,对于执行上下文这部分可以看看这篇:深入理解js执行--创建执行上下文这篇博客。

理解了上面之后我们再来看闭包的例子:

function a(){
//a函数的变量
var val_a = "我是a函数里的变量";
//声明函数b,b能访问函数a的变量
function b(){
console.log(val_a);
}
//a函数将b返回
return b;
} //全局变量fn,a执行返回了b给fn
var fn = a();
//调用fn,能够在全局作用域访问a函数里的变量
fn(); //我是a函数里的变量

这里fn能够访问到a的变量,因为b中引用着a的活动对象,所以即使a函数执行完了,a的活动对象还是不会被销毁的。这也说明过度使用闭包会导致内存泄漏。

再来个常见的例子(给多个li添加点击事件,返回对于li的下标):

<body>
<ul id="list">
<li>red</li>
<li>green</li>
<li>yellow</li>
<li>black</li>
<li>blue</li>
</ul>
</body>
//获得li标签组
var li_group = document.getElementsByTagName('li'); //错误例子:每个li都会跳出5
function fn(){
//为每一个li添加点击事件
var i = 0;
//使用for来给每个li添加事件
for(;i<li_group.length;i++){
//添加点击事件
li_group[i].addEventListener('click',function(){
// 输出对应得下标
console.log(i);
});
}
}
fn(); //正确的例子:
//在加一层的函数,作为闭包,用来保存每一次循环的i变量,就可以达到目的
function correctFn(){
var i = 0;
for(;i<li_group.length;i++){
//在外面套一层函数,这层函数会保存每次循环的i变量,也就是闭包了。
(function(num){
li_group[num].addEventListener('click',function(){
console.log(num);
});
}(i));
}
}
correctFn();

看下面解释之前我默认你已经知道活动对象是什么了,如果不懂可以看这篇:深入理解js执行--创建执行上下文

解释一下:

1.错误的例子:

屡一下思路,每个li都有click执行的函数,每个函数点击后才会执行是吧,那每个click的函数的外层函数是fn这个函数,那这5个click函数都会保存着fn的活动对象,那这个输出的i变量在fn这函数里面,所以当fn执行完后,i的值是5了,因此当每个里触发click的函数的时候输出的也就是5了。

再简单的说:每个li的click事件触发的函数引用的i在同一个活动对象中,所以值都一样。

2.正确执行的例子:

我们就在外层加了一层匿名函数并立即执行(虽然函数被执行了,如果有函数引用着它的活动对象,那其活动对象将不灭),这时每个li的click函数外层函数是每次循环产生的不同的匿名函数,这匿名也是有活动对象,每个li的click的函数保存着各自的匿名函数的活动对象,num这变量也根据每次循环产生不同的匿名函数传入的i的不同而不同,所以能够输出对应不同的值。

上面说的可能有点啰嗦,请原谅我[捂脸.jpg],我是希望尽可能的表达清楚。如果你看懂了,那对闭包的理解也更深一层了哦。

小结:

1.本篇主要讲的是闭包,闭包是有权访问另一个函数作用域中的变量的函数,主要是函数中的函数,因为能引用外层函数的活动对象所以能够访问其外层的变量。

2.我本篇主要讲的是原理,如果对一些东西不懂,可以看下面几篇。

相关的几篇:

深入理解js执行--单线程的JS

深入学习JS执行--创建执行上下文(变量对象,作用域链,this)

我发现,原来你是这样的JS全部文章汇总(点击此处)

本文出自博客园:http://www.cnblogs.com/Ry-yuan/

作者:Ry(渊源远愿)

欢迎访问我的个人首页:我的首页

欢迎访问我的github:https://github.com/Ry-yuan/demoFiles

欢迎转载,转载请标明出处,保留该字段。

JavaScript--我发现,原来你是这样的JS:函数表达式和闭包的更多相关文章

  1. JavaScript函数表达式、闭包、模仿块级作用域、私有变量

    函数表达式是一种非常有用的技术,使用函数表达式可以无需对函数命名,从而实现动态编程.匿名函数,是一种强大的方式,一下总结了函数表达式的特点: 1.函数表达式不同于函数声明,函数声明要求有名字,但函数表 ...

  2. 深入理解javascript系列(4):立即调用的函数表达式

    本文来自汤姆大叔 前言 大家学JavaScript的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行. 在详细了解这个之前,我们来谈了解一下“自执行”这个叫法,本文对这个功能的叫法 ...

  3. [JS]深入理解JavaScript系列(4):立即调用的函数表达式

    转自:汤姆大叔的博客 前言 大家学JavaScript的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行.在详细了解这个之前,我们来谈了解一下"自执行"这个叫法 ...

  4. 浅谈JavaScript的函数表达式(闭包)

    前文已经简单的介绍了函数的闭包.函数的闭包就是有权访问另一个函数作用域的函数,也就是函数内部又定义了一个函数. var Super=function(num){ var count=num; retu ...

  5. javascript高级程序设计第3版——第7章 函数表达式

    此张内容的难点在于闭包.而闭包又涉及到原型,原型链,执行上下环境,this的取值等知识点.(此章节对于闭包的内容篇幅较少,且写的很是艰涩难懂,推荐一位大牛的博客,对于闭包的前因后果以及作用机制写的很明 ...

  6. JavaScript高级程序设计(读书笔记)之函数表达式

    定义函数的方式有两种:一种是函数声明,另一种就是函数表达式. 函数声明的一个重要特征就是函数声明提升(function declaration hoisting),意思是在执行代码前会先读取函数声明. ...

  7. 一步步学习javascript基础篇(6):函数表达式之【闭包】

    回顾前面介绍过的三种定义函数方式 1. function sum (num1, num2) { return num1 + num2; }  //函数声明语法定义 2. var sum = funct ...

  8. JavaScript函数表达式

    函数表达式的基本语法形式 var functionName = function(arguments){ //函数体 } 递归建议 我们通过例子来一步步说明,递归的最佳实现方式.下面是普通递归调用的例 ...

  9. 详解Javascript 函数声明和函数表达式的区别

    Javascript Function无处不在,而且功能强大!通过Javascript函数可以让JS具有面向对象的一些特征,实现封装.继承等,也可以让代码得到复用.但事物都有两面性,Javascrip ...

随机推荐

  1. 本地idea调试spark2.x程序

    1.构建使用idea 构建maven 项目 选择org.scala-tools.archetypes:scala-archetype-simple,然后一直点next,maven最好选中本地配置国内源 ...

  2. LeetCode 104. Maximum Depth of Binary Tree (二叉树的最大深度)

    Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along the long ...

  3. Ubuntu16.04卸载opencv2.4.9并安装opencv3.2.0+contrib

    本文为作者原创,转载请注明出处(http://www.cnblogs.com/mar-q/)by 负赑屃 需要用到opencv中的surf和sift算法,机器上只有opencv3.2,没有扩展包,于是 ...

  4. Strategic Game

    Strategic Game Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) To ...

  5. Disharmony Trees

    /* 写完这篇博客有很多感慨,过去一段时间都是看完题解刷题,刷题,看会题解,没有了大一那个时候什么都不会的时候刷题的感觉,这个题做了一天半,从开始到结束都是从头开始自己构思的很有感觉,找回到当初的感觉 ...

  6. mongodb集群【】

    参考 http://www.jianshu.com/p/2825a66d6aed http://www.cnblogs.com/huangxincheng/archive/2012/03/07/238 ...

  7. ACID 数据库正确执行四要素

    ACID:数据库事务正确执行所必须满足的四个基本要素的缩写: 原子性(atomicity,或叫不可分割性),一致性(consistency),隔离性(isolation,又称独立性),持久性(dura ...

  8. 外键删除(T-SQL Drop Foreign Key)

    列出某张表相关的 FK Name select distinct name from sys.objects where object_id in (   select fk.constraint_o ...

  9. .net core 开发短网址平台的思路

    最近有个客户要求开发一套短网址网站,小编现在都使用.net core进行网站开发了,以是厘厘思路,想想使用.net core 的中间件应该很容易实现. 1. 构建一个中间件,监测网站的响应状态,代码如 ...

  10. mongoose返回值无法修改

    mongoose 查询方法 find 例:db.collections.find(query,function(err,doc) { 如果var res = doc[0]  是{name:'feife ...