高阶函数

JavaScript的函数其实都指向某个变量。既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,也可以返回一个函数,这种函数就称之为高阶函数。

函数作为参数

示例如下:

 function absAdd(x, y, f) {
return f(x) + f(y);
}
console.log(absAdd(-1, 2, Math.abs)); //

函数作为参数的好处是我们可以通过修改参数就可以改变函数的行为。

函数作为返回值

示例如下:

 function arrSum(arr) {
return function(){
return arr.reduce(function (x, y) {
return x + y;
});
};
} var f1 = arrSum([1, 2, 3, 4, 5]);
var f2 = arrSum([2, 4, 6, 8, 10]); console.log(f1 === f2); // false console.log(f1()); //
console.log(f2()); //

每次调用arrSum方法返回的都是一个新创建的函数,所以判断是不相等的。

返回函数时,可以决定在何时执行该函数。

闭包

我们注意到上面例子里返回的函数在其定义内部引用了局部变量arr,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,这种情况就称为闭包。

我们看下一个例子:

 function foo() {
var r = [];
for (var i = 0; i < 3; i++) {
r[i] = function() {
return i;
};
}
return r;
} var arr = foo();
for (var i = 0; i < 3; i++) {
console.log( arr[i]() );
} //
//
//

我们希望打印0,1,2这几个数字,但是实际上打印的都是3,这是由于返回的函数保存的是变量i,实际上在循环之后变量i就变成了3,所以会出现这样的情况。

立即执行函数

那么如何才能打印出0,1,2这几个数字呢,这里需要用到立即执行函数,立即执行函数的意思是在定义好函数之后立即执行,一般这样的函数都是匿名函数。

格式如下:

 (function (x) {
return x * x;
})(3);

即用一个括号将函数包含,后面紧跟另一个括号进行调用,同时可以进行参数传递。

我们再看下面的例子:

 function foo() {
var r = [];
for (var i = 0; i < 3; i++) {
r[i] = (function(index) {
return function() {
return index;
};
})(i);
}
return r;
} var arr = foo();
for (var i = 0; i < 3; i++) {
console.log( arr[i]() );
} //
//
//

我们来看看这个例子,每个闭包函数实际上引用的是index参数,而index参数是在立即执行函数执行时传入的i,所以不存在改变的情况,就可以打印出对应的索引值了。

关于this对象

我们来看下面的例子:

 var name = "Window";

 var obj = {
name: "Object",
func: function() {
return function() {
return this.name;
};
}
}; console.log( obj.func()() ); // Window var f = obj.func();
console.log( f() ); // Window

我们发现返回的是全局的name属性,而不是我们期望的obj的name属性。

我们知道每个函数在调用时都会获得this及arguments两个参数,而this参数指向调用该方法的对象。

所以我们可以看一下第14和15行,调用obj.func时this是指向obj对象的,返回的函数实际上被绑定到全局对象上了,所以当调用f函数时,实际上是window进行调用的,所以拿到的name就是window.name。

解决方法如下:

 var name = "Window";

 var obj = {
name: "Object",
func: function() {
var that = this;
return function() {
return that.name;
};
}
}; console.log( obj.func()() ); // Object var f = obj.func();
console.log( f() ); // Object

利用了闭包会持有调用链上的变量的原理即可。

私有属性

在JavaScript中,没有私有属性的概念,所有属性都是公开的。

但是有私有变量的概念,在函数中声明的变量,都是该函数私有的,函数以外的地方不能访问。

我们利用闭包和私有变量的特性可以创建出类似于私有属性的变量。

 function Person(name) {
// 私有变量
var age = 0;
// 私有函数
function foo() {
console.log("call private function!");
} this.setName = function(value) {
name = value;
foo();
};
this.getName = function() {
return name;
}; this.setAge = function(value) {
age = value;
foo();
};
this.getAge = function() {
return age;
};
} var p = new Person("Li Lei");
p.age = 28;
console.log(p.getAge()); //
p.setAge(30);
console.log(p.getAge()); //
console.log(p.age); //

我们会发现,在函数内部是直接使用age来访问私有变量的,而如果是this.age则表示当前对象的age公开属性,所以p.age和p.getAge会取得不同的数值。外部是无法访问到内部变量age和参数name的。

使用立即执行函数创建

我们发现上面的方法只能将所有代码都写在构造函数中才能访问到私有变量,其实还有一种写法:

 (function(){
// 使用 var 定义的变量外部无法访问
var _name;
var age = 0;
// 定义的函数外部无法访问
function foo() {
console.log("call private function!");
} // 不使用 var 定义的对象外部可访问
Person = function(name) {
_name = name;
} Person.prototype.setName = function(value) {
_name = value;
foo();
}
Person.prototype.getName = function() {
return _name;
} Person.prototype.setAge = function(value) {
age = value;
foo();
}
Person.prototype.getAge = function() {
return age;
}
})(); var p = new Person("Li Lei");
p.age = 28;
console.log(p.getAge()); //
p.setAge(30);
console.log(p.getAge()); //
console.log(p.age); //

通过一个立即执行的匿名函数来包裹即可实现。

模块模式

模块模式可以实现对象的私有属性和方法,如下:

 var instance = function(){
var name = "Han Meimei"; function foo(){
console.log("call private function");
} return {
setName: function(value) {
name = value;
foo();
},
getName: function() {
return name;
}
};
}(); instance.name = "Li Lei";
console.log(instance.getName()); // Han Meimei
instance.setName("Uncle Wang");
console.log(instance.getName()); // Uncle Wang
console.log(instance.name); // Li Lei

当然,如果需要创建指定类型的实例,可以使用下面的代码:

 var instance = function(){
var name = "Han Meimei"; function foo(){
console.log("call private function");
} // 这里可以创建指定类型的实例
var obj = new Object(); // 添加方法
obj.setName = function(value) {
name = value;
foo();
};
obj.getName = function() {
return name;
}; return obj;
}(); instance.name = "Li Lei";
console.log(instance.getName()); // Han Meimei
instance.setName("Uncle Wang");
console.log(instance.getName()); // Uncle Wang
console.log(instance.name); // Li Lei

HTML5学习笔记(十八):闭包的更多相关文章

  1. python3.4学习笔记(十八) pycharm 安装使用、注册码、显示行号和字体大小等常用设置

    python3.4学习笔记(十八) pycharm 安装使用.注册码.显示行号和字体大小等常用设置Download JetBrains Python IDE :: PyCharmhttp://www. ...

  2. (C/C++学习笔记) 十八. 继承和多态

    十八. 继承和多态 ● 继承的概念 继承(inheritance): 以旧类为基础创建新类, 新类包含了旧类的数据成员和成员函数(除了构造函数和析构函数), 并且可以派生类中定义新成员. 形式: cl ...

  3. Java基础学习笔记十八 异常处理

    什么是异常?Java代码在运行时期发生的问题就是异常. 在Java中,把异常信息封装成了一个类.当出现了问题时,就会创建异常类对象并抛出异常相关的信息(如异常出现的位置.原因等). 异常的继承体系 在 ...

  4. MYSQL进阶学习笔记十八:MySQL备份和还原!(视频序号:进阶_37)

    知识点十九:MySQL的备份的还原(38) 一.mysql的备份 1.通过使用mysqldump的命令备份 使用mysqldump命令备份,mysqldump命令将数据库中的数据备份成一个文本文件.表 ...

  5. JavaScript权威设计--事件冒泡,捕获,事件句柄,事件源,事件对象(简要学习笔记十八)

    1.事件冒泡与事件捕获 2.事件与事件句柄   3.事件委托:利用事件的冒泡技术.子元素的事件最终会冒泡到父元素直到跟节点.事件监听会分析从子元素冒泡上来的事件. 事件委托的好处:     1.每个函 ...

  6. python 学习笔记十八 django深入学习三 分页,自定义标签,权限机制

    django  Pagination(分页) django 自带的分页功能非常强大,我们来看一个简单的练习示例: #导入Paginator>>> from django.core.p ...

  7. SharpGL学习笔记(十八) 解析3ds模型并显示

    笔者设想的3D仿真中的元件,是不可能都是“画”出来的.这样就玩复杂了,应该把任务分包出去,让善于制作模型的软件来制作三维模型,我们只需要解析并且显示它即可. 3dsmax制作三维模型的方便,快捷,专业 ...

  8. PHP学习笔记十八【构造函数】

    <?php class Person{ public $name; public $age; //定义构造函数 function 空格__construct 构造方法没有返回值,对象自动调用 p ...

  9. Python3学习笔记十八

    1.    MTV M:   model     与数据库相关 T:   Template    与html相关 V:   views      与逻辑相关 一.    URL配置 启动:python ...

  10. HTML5学习笔记(八):CSS定位

    CSS 定位 (Positioning) 属性允许你对元素进行定位. 定位和浮动 CSS 为定位和浮动提供了一些属性,利用这些属性,可以建立列式布局,将布局的一部分与另一部分重叠.定位的基本思想很简单 ...

随机推荐

  1. java第七节 IO

    /* *第七讲 IO/输入与输出 * * File类 * RandomAccessFile类 * 各种节点流类 * 字符编码 * 各种过滤流与包装类 * *File类 * File类是IO包中唯一代表 ...

  2. docker-machine为节点安装指定版本的docker-ce的思路

    对于指定版本的问题,翻遍官网文档和github上的issue,始终没有一个好的回答,而且该产品的开发人员不知道为什么总不正面提供方法,也许是为了推广新版本, 但是这样真的好吗?docker swarm ...

  3. 【转】IP地址、子网掩码、网络号、主机号、网络地址、主机地址以及ip段

    背景知识 IP地址 IP地址被用来当做Internet上的电脑的身份编号.大家日常见到的情况是每台联网的PC上都需要有IP地址,才能正常通信.我们可以把“个人电脑”比作“一台电话”,那么“IP地址”就 ...

  4. 转:关于 OGRE 与 OSG 的简单比较

    1   前言 我曾经细致阅读过 OGRE 和 OSG 官方提供的文档,有<Pro OGRE 3D Programming>.OGRE自带手册(manual).王锐老师等翻译的<Ope ...

  5. Spring MVC 教程,快速入门,深入分析[1-11]

    资源下载: Spring_MVC_教程_快速入门_深入分析V1.1.pdf SpringMVC核心配置文件示例.rar     作者:赵磊 博客:http://elf8848.iteye.com   ...

  6. Git提交代码报错Git push error:src refspec XXX matches more than one解决方案

    Git提交代码push时,报错这个 error: src refspec master matches more than one. error: failed to push some refs t ...

  7. PCRE函数简介和使用示例【转】

    PCRE函数简介和使用示例 标签: 正则表达式listbuffercompilationnullperl 原文地址:http://blog.csdn.net/sulliy/article/detail ...

  8. zabbix数据库需要多大硬盘?我告诉你

    本次案例:100台服务器,每台服务器有30个监控项,每个监控项60秒刷新一次,需要多大的硬盘呢?众所周知,zabbix基本都是通过web配置,这些配置数据也是存放到数据库里的,但是它对硬盘容量的要求基 ...

  9. SPI、I2C、UART三种串行总线协议的区别和SPI接口介绍(转)

    SPI.I2C.UART三种串行总线协议的区别 第一个区别当然是名字: SPI(Serial Peripheral Interface:串行外设接口); I2C(INTER IC BUS) UART( ...

  10. openstack nova 深入

    一.概述: 由nova --debug list查看到: #nova --debug list DEBUG (session:195) REQ: curl -g -i -X GET http://19 ...