HTML5学习笔记(十八):闭包
高阶函数
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学习笔记(十八):闭包的更多相关文章
- python3.4学习笔记(十八) pycharm 安装使用、注册码、显示行号和字体大小等常用设置
python3.4学习笔记(十八) pycharm 安装使用.注册码.显示行号和字体大小等常用设置Download JetBrains Python IDE :: PyCharmhttp://www. ...
- (C/C++学习笔记) 十八. 继承和多态
十八. 继承和多态 ● 继承的概念 继承(inheritance): 以旧类为基础创建新类, 新类包含了旧类的数据成员和成员函数(除了构造函数和析构函数), 并且可以派生类中定义新成员. 形式: cl ...
- Java基础学习笔记十八 异常处理
什么是异常?Java代码在运行时期发生的问题就是异常. 在Java中,把异常信息封装成了一个类.当出现了问题时,就会创建异常类对象并抛出异常相关的信息(如异常出现的位置.原因等). 异常的继承体系 在 ...
- MYSQL进阶学习笔记十八:MySQL备份和还原!(视频序号:进阶_37)
知识点十九:MySQL的备份的还原(38) 一.mysql的备份 1.通过使用mysqldump的命令备份 使用mysqldump命令备份,mysqldump命令将数据库中的数据备份成一个文本文件.表 ...
- JavaScript权威设计--事件冒泡,捕获,事件句柄,事件源,事件对象(简要学习笔记十八)
1.事件冒泡与事件捕获 2.事件与事件句柄 3.事件委托:利用事件的冒泡技术.子元素的事件最终会冒泡到父元素直到跟节点.事件监听会分析从子元素冒泡上来的事件. 事件委托的好处: 1.每个函 ...
- python 学习笔记十八 django深入学习三 分页,自定义标签,权限机制
django Pagination(分页) django 自带的分页功能非常强大,我们来看一个简单的练习示例: #导入Paginator>>> from django.core.p ...
- SharpGL学习笔记(十八) 解析3ds模型并显示
笔者设想的3D仿真中的元件,是不可能都是“画”出来的.这样就玩复杂了,应该把任务分包出去,让善于制作模型的软件来制作三维模型,我们只需要解析并且显示它即可. 3dsmax制作三维模型的方便,快捷,专业 ...
- PHP学习笔记十八【构造函数】
<?php class Person{ public $name; public $age; //定义构造函数 function 空格__construct 构造方法没有返回值,对象自动调用 p ...
- Python3学习笔记十八
1. MTV M: model 与数据库相关 T: Template 与html相关 V: views 与逻辑相关 一. URL配置 启动:python ...
- HTML5学习笔记(八):CSS定位
CSS 定位 (Positioning) 属性允许你对元素进行定位. 定位和浮动 CSS 为定位和浮动提供了一些属性,利用这些属性,可以建立列式布局,将布局的一部分与另一部分重叠.定位的基本思想很简单 ...
随机推荐
- python模块之HTMLParser(原理很大程度上就是对类构造的熟练运用)
# -*- coding: utf-8 -*- #python 27 #xiaodeng #python模块之HTMLParser(原理很大程度上就是对类构造的熟练运用) import HTMLPar ...
- python之函数用法isupper()
# -*- coding: utf-8 -*- #python 27 #xiaodeng #python之函数用法isupper() #http://www.runoob.com/python/att ...
- checkbox 多选框 :jquery之全选、全不选、反选
javascriptjqueryselectAll [html] view plaincopy <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1 ...
- java第六节 字符串/集合
/* *String类和StringBuffer类 * 位于java.lang包中 * String类对象中的内容一旦被初始化就不能再改变 * StringBuffer类中用于封装内容可以改变的字符串 ...
- 一个进程(Process)最多可以生成多少个线程(Thread)
1.进程中创建线程的限制 默认情况下,一个线程的栈要预留1M的内存空间,而一个进程中可用的内存空间只有2G,所以理论上一个进程中最多可以开2048个线程,但是内存当然不可能完全拿来作线程的栈,所以实际 ...
- NTP国内时钟服务器
阿里云linux时钟服务器 ntp1.aliyun.comntp2.aliyun.comntp3.aliyun.comntp4.aliyun.comntp5.aliyun.comntp6.aliyun ...
- ASP.NET HttpModule URL 重写 (一) 【Z】
大家好,又来和大家见面了,此次给大家带来的URL重写,关于URL重写是什么,有什么好处,如何重写,今天我和大家一起分享一下我的经验 一.URL重写 URL重写就是首先获得一个进入的URL请求然后把它重 ...
- Swift 互斥锁写法
oc中的互斥锁@synchronized(self) { //需要执行的代码块} swift中的互斥锁objc_sync_enter(self)//需要执行的代码块objc_sync_exit(sel ...
- YAML 语言教程(转载)
用YAML语言读取配置是最快的,之前的suricata中用yaml读取了配置,并且在代码运行期间,对配置进行了维护,所以抽点时间,来了解一下YAML语言编程,下文虽然对YAML语言和JAVAScrip ...
- 代码管理(五)git 删除分支
1.删除远程分支 在远程下面,选择需要删除的分支,右击,选择删除 2. 删除不存在远程对应分支的本地分支 在远程上建立了一个分支feature,后来leader觉得不合理,就把远程feature分支 ...