初识JavaScript闭包
一个问题引发的思考
在我学习javascript的事件时,有一个小任务是使用JS来实现 li 列表项在鼠标悬浮时会有背景阴影的动态效果,很自然想到用for 来为每个列表项添加onmouseover 和 onmouseout事件来改变和恢复 li 的类名。
如下:
<script type="text/javascript">
var Lis = document.getElementsByTagName("li"); function addevent() {
for (var i = 0; i < Lis.length; i++) {
Lis[i].onmouseover = function() {
console.log(i);
Lis[i].className = "lihover";
}
Lis[i].onmouseout = function() {
Lis[i].className = "";
}
}
}
addevent();
// console.log(i);
</script>
看起来很有道理的代码会什么不能正常工作?
先看一下为什么
在第8行和11行,通过改变 li 元素的classname来实现鼠标悬浮的动态效果改变,根据我以前学习的语言(C和JAVA)这明显是不对的,怎么能在函数内使用外面的局部变量呢,可是浏览器为什么没有报错,我在7行加了一句console.log(i);看一下
浏览器输出了3 并报错:closure.html:44 Uncaught TypeError: Cannot set property 'className' of undefined
当这两个事件发生时,函数会执行,函数会访问Lis 和 i 这两个变量,但是注意循环和事件函数不是同时执行的,事件函数发生时i的 已经是3了, 而Lis里并没有Lis[3]这个元素。
可是这两个函数为什么能访问外面的局部变量呢,答案是 闭包 (closure)
闭包初识
闭包就是在创建函数时为其保存一份创建时的外部环境,所以这两个事件函数能够访问到i这个变量,虽然访问的是循环执行完后的i的值。这也已经很神奇了对吧,源自函数式编程的魔法。那么怎么让其访问的 i 是函数自身被创建时的 i 呢,我再创建一个函数专门用来返回这个事件函数,如下:
function makeevents(i) {
console.log(i);
return function() {
Lis[i].className = "lihover";
}
}
function addevent() {
for (var i = 0; i < Lis.length; i++) {
Lis[i].onmouseover = makeevents(i);
Lis[i].onmouseout = function() {
this.className = "";
}
}
}
addevent();
我只是在mouseover事件用了这个机制,在mouseout使用了this关键词这个等最后在讨论。
在浏览器里运行一下发现效果已经实现了,并且在页面加载完后控制台就打印出了0 1 2
makeevent函数的作用正是创建闭包,在每一次循环创建一个,这样Lis[i]引用的就是正确的 li 元素了,看起来好像很繁琐的样子,事实上我们因为可以对makeevent传入参数来改变返回的函数的一些特点。这好像面向对象的工厂模式对吧。事实上我们完全可以用闭包来实现面向对象的封装。
关于this
我在第二个事件函数里使用了this,它表示了响应这个事件的当前对象,也即表示当前的列表项。并且在必要时还可以往Lis[i]元素对象里加入其他东西,如下:
Lis[i].index = i;
Lis[i].hehe = "hehe";
Lis[i].onmouseout = function() {
console.log(this.hehe);
Lis[this.index].className = "";
}
这样写也是可以的。通过对象本身可以动态的添加属性这一个JavaScript的特点来完成的。
是的,JavaScript真是一门神奇的语言
初识JavaScript闭包的更多相关文章
- Javascript闭包和C#匿名函数对比分析
C#中引入匿名函数,多少都是受到Javascript的闭包语法和面向函数编程语言的影响.人们发现,在表达式中直接编写函数代码是一种普遍存在的需求,这种语法将比那种必须在某个特定地方定义函数的方式灵活和 ...
- Day15 HTML补充、初识JavaScript
一.上节回顾 上节回顾: HTML 头部信息:编码.title.style.link(导入css文件) 身体: 内联 块级 --->inline-block(既有内联效果又有块级效果) a标签: ...
- 《Web 前端面试指南》1、JavaScript 闭包深入浅出
闭包是什么? 闭包是内部函数可以访问外部函数的变量.它可以访问三个作用域:首先可以访问自己的作用域(也就是定义在大括号内的变量),它也能访问外部函数的变量,和它能访问全局变量. 内部函数不仅可以访问外 ...
- JavaScript 闭包深入浅出
闭包是什么? 闭包是内部函数可以访问外部函数的变量.它可以访问三个作用域:首先可以访问自己的作用域(也就是定义在大括号内的变量),它也能访问外部函数的变量,和它能访问全局变量. 内部函数不仅可以访问外 ...
- JavaScript闭包(Closure)
JavaScript闭包(Closure) 本文收集了多本书里对JavaScript闭包(Closure)的解释,或许会对理解闭包有一定帮助. <你不知道的JavsScript> Java ...
- javascript闭包理解
//闭包理解一 function superFun(){ var _super_a='a'; function subfuc(){ console.log(_super_a); } return su ...
- Javascript闭包深入解析及实现方法
1.什么是闭包 闭包,官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.闭包的特点:1. 作为一个函数变量的一个引用,当函数返回时 ...
- javascript闭包和作用域链
最近在学习前端知识,看到javascript闭包这里总是云里雾里.于是翻阅了好多资料记录下来本人对闭包的理解. 首先,什么是闭包?看了各位大牛的定义和描述各式各样,我个人认为最容易一种说法: 外部函数 ...
- JavaScript闭包深入解析
for (var i=1; i<=5; i++) { setTimeout( function timer() { console.log( i ); }, i*1000 ); } --上面这段 ...
随机推荐
- JavaScript变量提升的本质
变量提升 先说三句总结性的话: let 的「创建」过程被提升了,但是初始化没有提升. var 的「创建」和「初始化」都被提升了. function 的「创建」「初始化」和「赋值」都被提升了. 所以,我 ...
- 快速排序及优化(Java实现)
普通快速排序 找一个基准值base,然后一趟排序后让base左边的数都小于base,base右边的数都大于等于base.再分为两个子数组的排序.如此递归下去. public class QuickSo ...
- Javascript实现继承
以下转自阮一峰的文章:http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance_continued.h ...
- protobuf lib库的使用
问题记录: 1.在使用protobuf反射机制动态加载解析proto文件时,发现当proto文件中含有import系统proto文件的语句时,无法解析文件,解决方法是添加路径映射. google::p ...
- 网络通信 --> TCP三次握手和四次挥手
TCP三次握手和四次挥手 建立TCP需要三次握手才能建立,而断开连接则需要四次握手.整个过程如下图所示: 一.TCP报文格式 如下图: (1)序号:Seq序号,占32位,用来标识从TCP源端向目的端发 ...
- Myeclipse快速补充返回值快捷键
比如 image.getGraphics(); 按ALT+Shift+L键,弹出一个确认变量名称框,确认之后会补全语句Graphics graphics = image.getGraphics();
- shell死循环脚本示例
1.设计一个脚本,监控远程的一台机器(假设ip为192.168.0.28)的存活状态,当发现宕机时发一封邮件给你自己. 提示:1. 你可以使用ping命令 ping -c10 www.baidu. ...
- vmware虚拟机和网络中的桥接和NAT
vmware虚拟机和网络中的桥接和NAT 有许多人在网上回答类似的问题,但大多说的不够简单,且互相抄袭的嫌疑很大,这里我尽自己努力把问题说的明白一些 首先解释一下什么是NAT(network addr ...
- 常用排序算法的Java实现与分析
由于需要分析算法的最好时间复杂度和最坏时间复杂度,因此这篇文章中写的排序都是从小到大的升序排序. 带排序的数组为arr,arr的长度为N.时间复杂度使用TC表示,额外空间复杂度使用SC表示. 好多代码 ...
- qt中控件的使用函数
1.Text Edit编辑框 //将编辑框中的内容转化成Utf8编码 ui->textEdit->toPlainText().toUtf8(); 2.Combo Box下拉框的应用 (1) ...