高阶函数

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. linux下安装和卸载vmware产品

    1.安装 一般的发行版都不会带有vmware,所以通常是下载安装包来安装. 具体的可以见 http://www.cnblogs.com/oloroso/p/5845227.html 2.卸载 这里主要 ...

  2. 转 php 5.3.6中php-fpm 配置

    php 5.3.6 中 php-fpm 配置 成 服务启动 从php5.3.3开始 源码中开始包含 php-fpm,不用专门再打补丁了,只需要解开源码直接configure, 关于php-fpm的编译 ...

  3. Java循环中标签的作用(转)

    转自:http://lihengzkj.iteye.com/blog/1090034 以前不知道在循环中可以使用标签.最近遇到后,举得还是有其独特的用处的.我这么说的意思是说标签在循环中可以改变循环执 ...

  4. yml转properties

    推荐一个在线工具,可以将yaml转换为properties,同时也支持反向转换 http://www.toyaml.com 非常好记的地址,to yaml,直接在地址栏里输入toyaml.com,省去 ...

  5. OSWorkFlow 学习

    1.OSWorkFlow基本概念 在商用和开源世界里,OSWorkflow 都不同于这些已有的工作流系统.最大不同在于 OSWorkflow 有着非常优秀的灵活性.在开始 接触 OSWorkflow  ...

  6. 自己开发Visual studio插件-一个nvelocity高亮插件

    首先,有一个项目用到了nvelocity模板引擎,但是用vs开发模板的时候,没有高亮效果,所以非常不方便,鉴于这个,于是有了自己开发插件的念头,但是在vs sdk开发上面,这方面的资料真是少之又少,网 ...

  7. c++ 静态类成员函数(static member function) vs 名字空间 (namespace)

    好多人喜欢把工具函数做成static member function.这样以增加隐蔽性和封装性,由其是从C#,java转而使用c++的开发人员. 例如: class my_math { public: ...

  8. Weex学习与实践

    Weex学习与实践(一):Weex,你需要知道的事 本文主要介绍包括Weex基本介绍.Weex源码结构.初始化工程.we代码结构.Weex的生命周期.Weex的工作原理.页面间通信.boxmodel ...

  9. 函数waitpid和WTERMSIG说明(转)

    waitpid系统调用在Linux函数库中的原型是: #include <sys/types.h> #include <sys/wait.h> pid_t waitpid(pi ...

  10. window 64bit 下react navtive安装

    1.安装jdk 去这里安装对应的jdk:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.h ...