高阶函数

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. 利用JQuery 来操作 ListBox和ListBox内移动

    [导读]利用jquery 来操作 listbox和listbox内移动function listbox_move(listfrom,listto) { var size = $(" &quo ...

  2. SpringBoot集成MyBatis的分页插件PageHelper

    俗话说:好

  3. httpclient获取响应实体和信息的封装方法(解耦更新)

    转自:https://blog.csdn.net/fhaohaizi/article/details/77850302 2018年07月19日更新,主要是解耦之后方法很多地方发生了变化,httpcli ...

  4. netstat统计的tcp连接数与⁄proc⁄pid⁄fd下socket类型fd数量不一致的分析

    最近,线上一个应用,发现socket数缓慢增长,并且不回收,超过警告线之后,被运维监控自动重启了. 首先到zabbix上观察JVM历史记录,发现JVM-Perm space最近两周没有数据,猜测是程序 ...

  5. STC单片机串口输出ADXL335角度值

    STC单片机串口输出ADXL335角度值: //***************************************************** //名称:单片机串口输出ADXL335角度值 ...

  6. HDU 3951 Coin Game (简单博弈)

    Coin Game Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...

  7. 微信支付中的jsapi返回提示信息

    jsapi中跳转到微信支付中触发的方法是js中的getBrandWCPayRequest方法. 改方法中的返回结果msg提示信息如下: err_msg:get_brand_wcpay_request: ...

  8. SqlServer 2005 将已存在大量数据的表更改为分区表

    一.分区表简介: 使用分区表的主要目的,是为了改善大型表以及具有各种访问模式的表的可伸缩性和可管理性.分区一方面可以将数据分为更小.更易管理的部分,为提高性能起到一定的作用:另一方面,对于如果具有多个 ...

  9. C# 进制转换(二进制、十六进制、十进制互转)

    原文地址:https://www.cnblogs.com/icebutterfly/p/8884023.html C# 进制转换(二进制.十六进制.十进制互转)由于二进制数在C#中无法直接表示,所以所 ...

  10. 自定义View之圆形水波扩散动效

    这个效果做出来以后,真的美极了!放在你的应用中,无疑增添了光彩! 效果图    其实,第一种效果,才是产品的需求要的效果.第三种效果,是不是很熟悉?支付宝的咻一咻!哈哈,无意中,我就写出来了. 实现步 ...