高阶函数

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

函数作为参数

示例如下:

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

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

函数作为返回值

示例如下:

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

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

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

闭包

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

我们看下一个例子:

 1 function foo() {
 2     var r = [];
 3     for (var i = 0; i < 3; i++) {
 4         r[i] = function() {
 5             return i;
 6         };
 7     }
 8     return r;
 9 }
10
11 var arr = foo();
12 for (var i = 0; i < 3; i++) {
13     console.log( arr[i]() );
14 }
15
16 // 3
17 // 3
18 // 3

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

立即执行函数

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

格式如下:

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

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

我们再看下面的例子:

 1 function foo() {
 2     var r = [];
 3     for (var i = 0; i < 3; i++) {
 4         r[i] = (function(index) {
 5             return function() {
 6                 return index;
 7             };
 8         })(i);
 9     }
10     return r;
11 }
12
13 var arr = foo();
14 for (var i = 0; i < 3; i++) {
15     console.log( arr[i]() );
16 }
17
18 // 0
19 // 1
20 // 2

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

关于this对象

我们来看下面的例子:

 1 var name = "Window";
 2
 3 var obj = {
 4     name: "Object",
 5     func: function() {
 6         return function() {
 7             return this.name;
 8         };
 9     }
10 };
11
12 console.log( obj.func()() ); // Window
13
14 var f = obj.func();
15 console.log( f() ); // Window

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

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

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

解决方法如下:

 1 var name = "Window";
 2
 3 var obj = {
 4     name: "Object",
 5     func: function() {
 6         var that = this;
 7         return function() {
 8             return that.name;
 9         };
10     }
11 };
12
13 console.log( obj.func()() ); // Object
14
15 var f = obj.func();
16 console.log( f() ); // Object

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

私有属性

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

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

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

 1 function Person(name) {
 2     // 私有变量
 3     var age = 0;
 4     // 私有函数
 5     function foo() {
 6         console.log("call private function!");
 7     }
 8
 9     this.setName = function(value) {
10         name = value;
11         foo();
12     };
13     this.getName = function() {
14         return name;
15     };
16
17     this.setAge = function(value) {
18         age = value;
19         foo();
20     };
21     this.getAge = function() {
22         return age;
23     };
24 }
25
26 var p = new Person("Li Lei");
27 p.age = 28;
28 console.log(p.getAge()); // 0
29 p.setAge(30);
30 console.log(p.getAge()); // 30
31 console.log(p.age); // 28

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

使用立即执行函数创建

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

 1 (function(){
 2     // 使用 var 定义的变量外部无法访问
 3     var _name;
 4     var age = 0;
 5     // 定义的函数外部无法访问
 6     function foo() {
 7         console.log("call private function!");
 8     }
 9
10     // 不使用 var 定义的对象外部可访问
11     Person = function(name) {
12         _name = name;
13     }
14
15     Person.prototype.setName = function(value) {
16         _name = value;
17         foo();
18     }
19     Person.prototype.getName = function() {
20         return _name;
21     }
22
23     Person.prototype.setAge = function(value) {
24         age = value;
25         foo();
26     }
27     Person.prototype.getAge = function() {
28         return age;
29     }
30 })();
31
32 var p = new Person("Li Lei");
33 p.age = 28;
34 console.log(p.getAge()); // 0
35 p.setAge(30);
36 console.log(p.getAge()); // 30
37 console.log(p.age); // 28

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

模块模式

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

 1 var instance = function(){
 2     var name = "Han Meimei";
 3
 4     function foo(){
 5         console.log("call private function");
 6     }
 7
 8     return {
 9         setName: function(value) {
10             name = value;
11             foo();
12         },
13         getName: function() {
14             return name;
15         }
16     };
17 }();
18
19 instance.name = "Li Lei";
20 console.log(instance.getName()); // Han Meimei
21 instance.setName("Uncle Wang");
22 console.log(instance.getName()); // Uncle Wang
23 console.log(instance.name); // Li Lei

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

 1 var instance = function(){
 2     var name = "Han Meimei";
 3
 4     function foo(){
 5         console.log("call private function");
 6     }
 7
 8     // 这里可以创建指定类型的实例
 9     var obj = new Object();
10
11     // 添加方法
12     obj.setName = function(value) {
13         name = value;
14         foo();
15     };
16     obj.getName = function() {
17         return name;
18     };
19
20     return obj;
21 }();
22
23 instance.name = "Li Lei";
24 console.log(instance.getName()); // Han Meimei
25 instance.setName("Uncle Wang");
26 console.log(instance.getName()); // Uncle Wang
27 console.log(instance.name); // Li Lei

HTML5学习笔记(十八):闭包相关推荐

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

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

  2. windows内核开发学习笔记十八:IRP 处理的标准模式

    windows内核开发学习笔记十八:IRP 处理的标准模式 在 Windows 内核中的请求基本上是通过 I/O Request Packet 完成的. I/O manager ---> Dis ...

  3. Polyworks脚本开发学习笔记(十八)-用SDK开发Polyworks插件

    Polyworks脚本开发学习笔记(十八)-用SDK开发Polyworks插件 插件是由PolyWorks加载的动态链接库(DLL文件),然后查询Polyworks模块,以确定它们具有哪些功能,提供给 ...

  4. 学习笔记(十八):MoRe-Fi用深度学习网络从非线性信号中恢复呼吸波形

    <MoRe-Fi: Motion-robust and Fine-grained Respiration Monitoring via Deep-Learning UWB Radar>学习 ...

  5. 【D3D11游戏编程】学习笔记十八:模板缓冲区的使用、镜子的实现

    (注:[D3D11游戏编程]学习笔记系列由CSDN作者BonChoix所写,转载请注明出处:http://blog.csdn.net/BonChoix,谢谢~) 模板缓冲区(Stencil Buffe ...

  6. three.js学习笔记(十八)——调整材质

    介绍 到现在为止,我们都在创建新的着色器材质,但是如果我们想要修改一个Three.js内置的材质呢?或许我们对MeshStandardMaterial的处理结果感到满意,但是希望往里边添加顶点动画. ...

  7. 【theano-windows】学习笔记十八——混合蒙特卡洛采样

    #前言 继续之前的Theano学习,本次主要学习混合蒙特卡洛(Hybrid Monte-Carlo Sampling)采样算法. 国际惯例,参考网址 Hybrid Monte-Carlo Sampli ...

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

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

  9. JSTL标签引入(web基础学习笔记十八)

    一.JSTL包下载和引入 1.0.简介 JSTL全名为JavaServer Pages Standard Tag Library 1.1.下载包 下载地址:http://archive.apache. ...

  10. Go语言学习笔记(十八)之文件读写

    25.文件读写 1.文件打开和读 A.文件分类:文本和二进制文件 B.文件存取方式:随机存取和顺序存取 文件打开代码示例: 1: package main 2: 3: import ( 4: &quo ...

最新文章

  1. 芯片组x299是服务器主板吗,最强的酷睿i9只能用它!X299主板首发评测
  2. Android stadio 关联源码
  3. python最大堆_用Python实现最大堆
  4. Css3-锚链接和伪类tartet
  5. Java之什么是序列化以及为什么要序列化
  6. sellhis股票平面图
  7. python log日志级别_python – 日志记录:如何为处理程序设置最大日志级别
  8. 5行Python 代码就能让你的电脑永不息屏
  9. Oracle查看用户、用户权限、用户表空间、用户默认表空间
  10. MIMO-OTFS in High-Doppler Fading Channels:Signal Detection and Channel Estimation(5)
  11. 根据单词列表通过网络下载单词发音
  12. 2019新版c智播客h马程序员H5全栈工程师培训项目实战
  13. win10c盘扩容_系统C盘满了空间不足的扩容?
  14. 震惊!世界海底光缆分布图!
  15. 最佳实践:MySQL CDC 同步数据到 ES
  16. [转载]疯狂的程序员(1)
  17. 彻底搞懂弹性布局flex
  18. php绕过授权下载,Discuz附件下载权限绕过方法
  19. ArcGIS基于爬虫数据绘制人口分布密度图
  20. 贝叶斯垃圾邮件分类问题中联合概率的推导

热门文章

  1. Android 本地搭建Tomcat服务器供真机测试
  2. 【原创】开源Math.NET基础数学类库使用(06)直接求解线性方程组
  3. 巨人也能身手敏捷?看美国KeyBank的实战经验
  4. ansible-playbook jdk安装
  5. 程序员需要牢记的一点
  6. 25个让Java程序员更高效的Eclipse插件
  7. TMG2010 之创建访问规则
  8. VeriSign SSL证书apache安装
  9. 八种反应表示员工认可你
  10. [HNOI2013]切糕