JavaScript 匿名函数(anonymous function)与闭包(closure)


Posted in Javascript onOctober 04, 2011

引入
匿名函数
闭包
变量作用域
函数外部访问函数内部的局部变量
用闭包实现私有成员
引入
闭包是用匿名函数来实现。闭包就是一个受到保护的变量空间,由内嵌函数生成。“保护变量”的思想在几乎所有的编程语言中都能看到。

先看下 JavaScript 作用域:
JavaScript 具有函数级的作用域。这意味着,不能在函数外部访问定义在函数内部的变量。
JavaScript 的作用域又是词法性质的(lexically scoped)。这意味着,函数运行在定义它的作用域中,而不是在调用它的作用域中。这是 JavaScript 的一大特色,将在后面说明。
把这两个因素结合在一起,就能通过把变量包裹在匿名函数中而对其加以保护。你可以这样创建类的私有变量:

var baz; 
(function() { 
var foo = 10; 
var bar = 2; 
baz = function() { 
return foo * bar; 
}; 
})(); 
baz();

尽管在匿名函数外执行,但 baz 仍然可以访问 foo 和 bar。

说明:

1,第 1 行,baz 是全局变量;

2,第 3 ~第 9 行,定义一个匿名函数;

3,第 4 和 5 行,foo 和 bar 是匿名函数内的局部变量;第 6 ~ 8 行,在匿名函数内定义一个匿名函数,并将其赋值给全局变量 baz;

4,第 10 行,调用 baz。若改成 "alert(baz());",将显示 20;

5,按理说,在匿名函数外不能访问 foo 和 bar,但是现在可以。

在说明闭包前,先了解一下匿名函数。

匿名函数
匿名函数是指那些无需定义函数名的函数。匿名函数与 Lambda 表达式(拉姆达表达式)是一回事。唯一的不同——语法形式不同。Lambda 表达式更进一步。本质上,它们的作用都是:产生方法——内联方法,也就是说,省去函数定义,直接写函数体。

Lambda 表达式一般形式:

(input parameters) => {statement;}
其中:

参数列表,可以有多个、一个或者无参数。参数可以隐式或者显式定义。
表达式或者语句块,也就是函数体。
上面代码,第 6 ~ 8 行,没有函数名,是个匿名函数,采用 Lambda 表达式,严格意义上,虽然语法有差异,但目的一样。

示例1:

var baz1 = function() { 
var foo = 10; 
var bar = 2; 
return foo * bar; 
}; 
function mutil() { 
var foo = 10; 
var bar = 2; 
return foo * bar; 
}; 
alert(baz1()); 
var baz2 = mutil(); 
alert(baz2);

说明:

1,baz1 与 baz2 完全一样,但 baz1 与 baz2 相比,省去了函数定义,直接函数体——看上去多简约。

闭包
变量作用域
示例2:函数内部可以访问全局变量。

var baz = 10; 
function foo() { 
alert(baz); 
} 
foo();

这是可以。

示例3:函数外部不能访问函数内部的局部变量。

function foo() { 
var bar = 20; 
} 
alert(bar);

这会报错。

另外,函数内部声明变量时,一定要使用 var 关键字,否则,声明的是一个全局变量。

示例4:

function foo() { 
bar = 20; 
} 
alert(bar);

 函数外部访问函数内部的局部变量
实际情况,需要我们从函数外部获得函数内部的局部变量。先看示例5。

示例5:

function foo() { 
var a = 10; 
function bar() { 
a *= 2; 
} 
bar(); 
return a; 
} 
var baz = foo(); 
alert(baz);

a 定义在 foo 内,bar 可以访问,因为 bar 也定义在 foo 内。现在,如何让 bar 在 foo 外部被调用?

示例6:

function foo() { 
var a = 10; 
function bar() { 
a *= 2; 
return a; 
} 
return bar; 
} 
var baz = foo(); 
alert(baz()); 
alert(baz()); 
alert(baz()); 

var blat = foo(); 
alert(blat());

说明:

1,现在可以从外部访问 a;
2,JavaScript 的作用域是词法性的。a 是运行在定义它的 foo 中,而不是运行在调用 foo 的作用域中。 只要 bar 被定义在 foo 中,它就能访问 foo 中定义的变量 a,即使 foo 的执行已经结束。也就是说,按理,"var baz = foo()" 执行后,foo 已经执行结束,a 应该不存在了,但之后再调用 baz 发现,a 依然存在。这就是 JavaScript 特色之一——运行在定义,而不是运行的调用。

其中, "var baz = foo()" 是一个 bar 函数的引用;"var blat= foo()" 是另一个 bar 函数引用。

用闭包实现私有成员
现在,需要创建一个只能在对象内部访问的变量。用闭包再适合不过,因为通过闭包你可以创建只允许特定函数访问的变量,而且这些变量在这些函数的各次调用间依然存在。

为了创建私有属性,你需要在构造函数的作用域中定义相关变量。这些变量可以被定义于该作用域中的所有函数访问,包括那些特权方法。

示例7:

var Book = function(newIsbn, newTitle, newAuthor) { 
// 私有属性 
var isbn, title, author; 
// 私有方法 
function checkIsbn(isbn) { 
// TODO 
} 
// 特权方法 
this.getIsbn = function() { 
return isbn; 
}; 
this.setIsbn = function(newIsbn) { 
if (!checkIsbn(newIsbn)) throw new Error('Book: Invalid ISBN.'); 
isbn = newIsbn; 
}; 
this.getTitle = function() { 
return title; 
}; 
this.setTitle = function(newTitle) { 
title = newTitle || 'No title specified.'; 
}; 
this.getAuthor = function() { 
return author; 
}; 
this.setAuthor = function(newAuthor) { 
author = newAuthor || 'No author specified.'; 
}; 
// 构造器代码 
this.setIsbn(newIsbn); 
this.setTitle(newTitle); 
this.setAuthor(newAuthor); 
}; 

// 共有、非特权方法 
Book.prototype = { 
display: function() { 
// TODO 
} 
};

说明:

1,用 var 声明变量 isbn、title 和 author,而不是 this,意味着它们只存在 Book 构造器中。checkIsbn 函数也是,因为它们是私有的;

2,访问私有变量和方法的方法只需声明在 Book 中即可。这些方法称为特权方法。因为,它们是公共方法,但却能访问私有变量和私有方法,像 getIsbn、setIsbn、getTitle、setTitle、getAuthor、setAuthor(取值器和构造器)。

3,为了能在对象外部访问这些特权方法,这些方法前边加了 this 关键字。因为这些方法定义在 Book 构造器的作用域里,所以它们能够访问私有变量 isbn、title 和 author。但在这些特权方法里引用 isbn、title 和 author 变量时,没有使用 this 关键字,而是直接引用。因为它们不是公开的。

4,任何不需要直接访问私有变量的方法,像 Book.prototype 中声明的,如 display。它不需要直接访问私有变量,而是通过 get*、set* 简介访问。

5,这种方式创建的对象可以具有真正私有的变量。其他人不能直接访问 Book 对象的任何内部数据,只能通过赋值器和。这样一切尽在掌握。

但这种方式的缺点是:

“门户大开型”对象创建模式中,所有方法都创建在原型 prototype 对象中,因此不管生成多少对象实例,这些方法在内存中只有一份。
而采用本节的做法,没生成一个新的对象实例,都将为每个私有方法(如,checkIsbn)和特权方法(如,getIsbn、setIsbn、getTitle、setTitle、getAuthor、setAuthor)生成一个新的副本。
因此,本节方法,只适于用在真正需要私有成员的场合。另外,这种方式也不利于继承。

Javascript 相关文章推荐
JQuery困惑—包装集 DOM节点
Oct 16 Javascript
jquery 打开窗口返回值实现代码
Mar 04 Javascript
ajax中get和post的说明及使用与区别
Dec 23 Javascript
js 加密压缩出现bug解决方案
Nov 25 Javascript
简介JavaScript中的push()方法的使用
Jun 09 Javascript
js密码强度实时检测代码
Mar 02 Javascript
利用原生js和jQuery实现单选框的勾选和取消操作的方法
Sep 04 Javascript
微信小程序 rich-text的使用方法
Aug 04 Javascript
JS实现根据数组对象的某一属性排序操作示例
Jan 14 Javascript
微信小程序如何通过用户授权获取手机号(getPhoneNumber)
Jan 21 Javascript
vue实现的封装全局filter并统一管理操作示例
Feb 02 Javascript
详解vue beforeEach 死循环问题解决方法
Feb 25 Javascript
通过JavaScript控制字体大小的代码
Oct 04 #Javascript
jQuery图片预加载 等比缩放实现代码
Oct 04 #Javascript
jQuery EasyUI API 中文文档 - Menu菜单
Oct 03 #Javascript
Dom 结点创建 基础知识
Oct 01 #Javascript
JavaScript 的继承
Oct 01 #Javascript
Jquery 的扩展方法总结
Oct 01 #Javascript
jQuery EasyUI API 中文文档 - Tabs标签页/选项卡
Oct 01 #Javascript
You might like
社区(php&&mysql)一
2006/10/09 PHP
php中get_object_vars()方法用法实例
2015/02/08 PHP
浅谈PHP中的那些魔术常量
2020/12/02 PHP
Javascript 错误处理的几种方法
2009/06/13 Javascript
jquery ready()的几种实现方法小结
2010/06/18 Javascript
Javascript 静态页面实现随机显示广告的办法
2010/11/17 Javascript
jQuery控制输入框只能输入数值的小例子
2013/03/20 Javascript
jquery+ajax+C#实现无刷新操作数据库数据的简单实例
2014/02/08 Javascript
jquery插件hiAlert实现网页对话框美化
2015/05/03 Javascript
jquery checkbox无法用attr()二次勾选问题的解决方法
2016/07/22 Javascript
搭建简单的nodejs http服务器详解
2017/03/09 NodeJs
Vue表单验证插件Vue Validator使用方法详解
2017/04/07 Javascript
vue-router路由懒加载的实现(解决vue项目首次加载慢)
2018/08/28 Javascript
jQuery扩展方法实现Form表单与Json互相转换的实例代码
2018/09/05 jQuery
Vue渲染过程浅析
2019/03/14 Javascript
[01:32]2016国际邀请赛中国区预选赛IG战队首日赛后采访
2016/06/27 DOTA
python3+PyQt5重新实现QT事件处理程序
2018/04/19 Python
python解决字符串倒序输出的问题
2018/06/25 Python
python3爬虫获取html内容及各属性值的方法
2018/12/17 Python
python使用参数对嵌套字典进行取值的方法
2019/04/26 Python
python使用MQTT给硬件传输图片的实现方法
2019/05/05 Python
docker-py 用Python调用Docker接口的方法
2019/08/30 Python
Python tkinter和exe打包的方法
2020/02/05 Python
Python操作Excel工作簿的示例代码(\*.xlsx)
2020/03/23 Python
Pandas对每个分组应用apply函数的实现
2020/12/13 Python
CSS3制作精致的照片墙特效
2016/06/07 HTML / CSS
html5+svg学习指南之SVG基础知识
2014/12/17 HTML / CSS
英国优质鞋类专家:Robinson’s Shoes
2017/12/08 全球购物
拉夫劳伦爱尔兰官方网站:Ralph Lauren爱尔兰
2020/04/10 全球购物
个性大学生自我评价
2013/12/04 职场文书
协议书模板
2014/04/23 职场文书
《棉鞋里的阳光》教学反思
2014/04/24 职场文书
学雷锋先进个人事迹
2014/05/26 职场文书
生物技术专业求职信
2014/06/10 职场文书
国际金融专业自荐信
2014/07/05 职场文书
成事在人观后感
2015/06/16 职场文书