高阶函数

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. mysql计算时间差值,单位分钟数

    TIMESTAMPDIFF(MINUTE, 开始时间, 结束时间) as 时间差(单位:分钟数) TIMESTAMPDIFF(interval,datetime_expr1,datetime_expr ...

  2. 【mysql】关于InnoDB存储引擎 text blob 大字段的存储和优化

    最近在数据库优化的时候,看到一些表在设计上使用了text或者blob的字段,单表的存储空间已经达到了近100G,这种情况再去改变和优化就非常难了 一.简介 为了清楚大字段对性能的影响,我们必须要知道i ...

  3. iOS中CGRectDividede中布局用法

    - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typica ...

  4. (原)torch中的序列化

    转载请注明出处: http://www.cnblogs.com/darkknightzh/p/6591667.html 参考网址: https://github.com/torch/torch7/bl ...

  5. Linux查看文件安装路径与文件所在路径

    一.查看文件安装路径: 由于软件安装的地方不止一个地方,所有先说查看文件安装的所有路径(地址). 这里以Oracle为例.比如说我安装了Oracle,但是不知道文件都安装在哪些地方.放在哪些文件夹里, ...

  6. Tensorflow默认占满全部GPU的全部资源

    一台服务器上装了多块GPU,默认情况下启动一个深度学习训练任务时,这个任务会占满每一块GPU的几乎全部存储空间.这就导致一个服务器基本上只能执行一个任务,而实际上任务可能并不需要如此多的资源,这相当于 ...

  7. Python装饰器几个有用又好玩的例子

    装饰器是一种巧妙简洁的魔术,类似于Java中的面向切面编程,我们可以再函数执行前.执行后.抛出异常时做一些工作.利用装饰器,我们可以抽象出一些共同的逻辑,简化代码.而简化代码的同时,就是在增加代码鲁棒 ...

  8. ios实例开发精品文章推荐(7.22)

    UIView 基本方法 UIView的一些基本方法理解:loadView.viewDidLoad.viewDidUnload.viewWillAppear,viewWillDisappear init ...

  9. iOS 播放远程网络音乐的核心技术点

    一.前言 这两天做了个小项目涉及到了远程音乐播放,因为第一次做这种音乐项目,边查资料边做,其中涉及到主要技术点有: 如何播放远程网络音乐 如何切换当前正在播放中的音乐资源 如何监听音乐播放的各种状态( ...

  10. C# 自定义控件,日期时间选择输入插件

    权声明:本文为博主原创文章,未经博主允许不得转载. // 为textBox1添加一个日期时间选择控件 DateTimeChoser.AddTo(textBox1); DateTimeChoser.De ...