折腾自己的js闭包(二)
前面我大致探讨了js里的闭包的相关概念,那么,到底在什么时候用它最好呢?存在即真理,只不过以前没发现它而已,先来看看下面的这几个用途吧
一、我首先想到的就是从函数外面访问它的内部变量,从而达到自己的一些目的,还避免了设定为全局变量的全局变量污染,例如如下这个封装:
var person = function(){
//变量作用域为函数内部,外部无法访问
var name = "default name";
return {
getName : function(){
return name;
},
setName : function(newName){
name = newName;
}
}
}();
console.log(person.name); //结果为undefined
console.log(person.getName()); //结果为default name
person.setName("xiangxiao");
console.log(person.getName()); //结果为xiangxiao
但是后来想想觉得不大对劲,为何不直接用新建对象的方式来完成呢?或者angular严格的作用域控制其实也可以。要使用的时候就加上 object.变量名/object.方法名();或者在用到angular时的$scope.变量名/$scope.方法名()。猛的反应过来,局部变量,如果用了上述的那些解决办法,那还能叫函数的内部变量吗?不过这种封装的方式在我目前见过的项目里还很少用到。
二、查阅了些资料,发现了它的第二个用途,就是模拟面向对象的基类,对啊,JavaScript虽然没提供明显的类继承,但是仍然有另外的路径来实现同java一样 的继承呀,比如说prototype原型就是一个解决方案,严格的说prototype并不是继承,而是对函数prototype属性的一种拷贝来实现的类似于继承的东西。现在先来看看闭包是怎么模拟的吧,例如:
function Empolyee(){
var station = "default";
return {
getStation : function(){
return station ;
},
setStation : function(newStation){
station = newStation;
}
};
var xiaoming = Empolyee();
console.log(xiaoming.station); //结果为default
xiaoming.setStation("C++ coder");
console.log(xiaoming.getStation()); //结果为C++ coder
var xiaozhang = Empolyee();
console.log(xiaozhang.station); //结果为default
xiaozhang.setStation("java coder");
console.log(xiaozhang.getStation());//结果为java coder
其实仔细一看感觉模拟面向对象这个说法有些牵强,但确实实现了xiaoming和xiaozhang这两个雇员都是来自方法Empolyee的,而且互不影响,xiaoming的岗位是C++程序员,xiaozhang的gangwei是java程序员,相互独立。这点和函数原型prototype超级接近,所以说啊,我们的JavaScript还是包罗万象的。
此处还真的必须用闭包来实现,不写这个return一个对象的话,两个实体的还真没有共同方法setStation和getStation。
三、某些只需执行一次的函数,也即是常说的自执行匿名函数,这个在我最近参与的项目里好几处都有用到,基本都是在一个界面的初始化中,初始化需要立马下发一次ajax请求,查上来的数据立马要反馈在页面上显示,而且只需执行这一次即可,内部变量无需再次利用,这时候,用到自执行匿名函数就最合适不过了。例如:
var datamodel = {
table : [],
tree : {}
};
(function(dm){
for(var i = 0; i < dm.table.rows; i++){
var row = dm.table.rows[i];
for(var j = 0; j < row.cells; i++){
drawCell(i, j);
}
};
})(datamodel);
这就是一个初始化渲染树结构的自执行匿名函数,我们创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,因此在执行完后很快就会被释放,关键是这种机制不会污染全局对象。
记得以前初识闭包时,遇见过一个的闭包的例子,是这样的:
function cal(num1){
var num2 = 3;
function innerCal1(num2){
var num3 = 3;
function innerCal2(num3){
return num3*num2*num1;
}
return innerCal2;
};
return innerCal1;
};
cal(3)(3)(3); //结果为27
其实这个结果倒不意外,但是这么写很让人费解,当时一看特别懵,当了解了闭包的这一系列知识之后,再来看这例子就轻松些了,这算是一个闭包嵌套,内部返回函数innerCal2,外部再返回一个函数innerCal1,两次叠加,相当于可执行2次的闭包,再依次传入实参,就可以进行计算了。
另外,还有一个经常被各公司面试官用来考人的面试题(其实是以前有2个同事出去应聘时都被问到了),看来不可小觑,比较经典,是这样的:
<!DOCTYPE html>
<html lang="en">
<head> <meta charset="UTF-8">
<title></title>
<script type="text/javascript">
function onMyLoad(){
var arr = document.getElementsByTagName("p");
for(var i = 0; i < arr.length;i++){
arr[i].onclick = function(){
alert(i);
}
};
};
</script>
</head>
<body onload="onMyLoad()">
<p>产品一</p>
<p>产品二</p>
<p>产品三</p>
<p>产品四</p>
<p>产品五</p>
</body>
</html>
抛出问题: 此题的目的是想每次点击对应目标时弹出对应的数字下标 0~4,但实际是无论点击哪个目标都会弹出数字5 问题所在: arr 中的每一项的 onclick 均为一个函数实例(Function 对象),这个函数实例也产生了一个闭包域, 这个闭包域引用了外部闭包域的变量,其 function scope 的 closure 对象有个名为 i 的引用, 外部闭包域的私有变量内容发生变化,内部闭包域得到的值自然会发生改变 。
这么一大段话,解释得够清楚的了,也即是在整个for循环之后,i的值已经变成4了,这时候点击执行的onclick函数,当然内部变量i也随之变成4了,我们想要的效果并不能实现,针对这个问题,也可用闭包来解决,如下:
function onMyLoad(){
var arr = document.getElementsByTagName("p");
for(var i = 0; i < arr.length;i++){
(function(arg){
arr[i].onclick = function(){
alert(i);
};
})(i);
};
};
这么做的思路是:既然onclick函数里的i是随着for循环时 i变化而跟着变化的,那我们干脆不用这个i,用一个跟它保持关联的变量来代替它,不就行了?当然,这么做的前提是基本变量类型,要知道,对象或者其他引用类型是不会这么听话的,一边变化,保存它的内存那儿也跟着变化,立马跟它相等的变量一股脑儿全变了。现在来看看这么做的原理是什么。
此处定义了一个闭包函数,并立即执行它,该函数有一个私有的变量arg,该函数的function scope的闭包对象有2个引用,arg和i,虽然i随着for循环是不断在变化着的,但是函数的形参arg是不会立即跟i保持变化,毕竟不是引用类型的变量,内存保存的是值,并不是一个指针,因此这里arg并不会立马随着i的变化而变化。同时,i是这个匿名函数的实参,被传入匿名函数中,因此,i等于多少,里面arg也就等于多少。这样子,我们的目的就达到了。运行一下,果真如此,弹出的值依次是0~4。
网上还有一个超赞的解决办法,将i变为这个arr数组中每个元素的某个属性,其实这种方法也是基于变量值转移的思路,不直接使用变量i,使用另一个跟i值相等的变量来代替它,好了,来看看这样子又是怎么实现的吧,如下:
function onMyLoad() {
var arr = document.getElementsByTagName("p");
for(var i = 0; i<arr.length;i++){
arr[i].number = i; //注意此处,将i的值赋给数组arr中每个元素的属性number,这样就实现了变量转移。
arr[i].onclick = function(){
alert(this.number);
}
}
};
运行一下,果真也是如此,弹出的值依次是0~4。
关于闭包一些使用,暂时就告一段落了,最近在追一部红遍大江南北的国产剧《人民的名义》,不多说了,去看看侯局长怎么略施小计收拾那帮拿钱不干人事的贪官的,达康书记的表情包也跟着网红了,哈哈哈。
折腾自己的js闭包(二)的更多相关文章
- 折腾自己的js闭包(一)
闭包是什么鬼? 15年10月份初到现在的公司时,有天晚上加班后临下班时,当时的组长问我知道闭包不,由于我是半路出家来做程序的,几乎很少用到闭包这个东东,并不是很了解这个概念,组长写出了这么段代码. v ...
- Js闭包常见三种用法
Js闭包特性源于内部函数可以将外部函数的活动对象保存在自己的作用域链上,所以使内部函数的可以将外部函数的活动对象占为己有,可以在外部函数销毁时依然存有外部函数内的活动对象内容,这样做的好处是可 ...
- js闭包理解实例小结
Js闭包 闭包前要了解的知识 1. 函数作用域 (1).Js语言特殊之处在于函数内部可以直接读取全局变量 <script type="text/javascript"> ...
- js闭包(closure),个人理解
一.闭包概念理解 各种专业文献上对js"闭包"(closure)定义非常抽象,贼难看懂.我的理解是,闭包就是能够读取某函数内部变量的函数.由于在Javascript语言中只有在函数 ...
- js闭包vs Java内部类
前言: 昨天写了一个关于Java内部的博客,在内部类的最后一点中谈到了Java闭包的概念,他是这样定义闭包的:闭包是一个可调用的对象,它记录了一些信息,这些信息来自创建它的作用域.结合Java的内部类 ...
- js闭包实例汇总
本文是通过实例来帮助大家深刻理解js闭包,是篇非常不错的文章,这里推荐给大家,有需要的小伙伴可以参考下 Js闭包 闭包前要了解的知识 1. 函数作用域 (1).Js语言特殊之处在于函数内部可以直接读取 ...
- js闭包的作用
js闭包的用途详解 js闭包可以用在许多地方.它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中.具体怎么理解呢,各位看官请仔细看好下文 我们来看 ...
- 探讨js闭包
背景:爱就要大胆说出来,对于编程我只想说,喜欢就大胆写出来.喜欢却不行动那就意味着失败.所以,对于在研究编程的猿们,我对同伴们说,大胆的学,大胆的写.呵呵,说这些其实无非是给我自己点动力,写下去的勇气 ...
- javascript深入理解js闭包(转)
javascript深入理解js闭包 转载 2010-07-03 作者: 我要评论 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. ...
随机推荐
- Python3中正则的贪婪匹配模式
什么是贪婪模式 正则在进行匹配时,从开始位置查找最远的结束位置,这种模式称之为贪婪模式. 在进行HTML标签类似内容获取时,贪婪模式会导致整个内容的返回,需要使用非贪婪模式. 固定的书写规则 : .* ...
- Spring Cloud Alibaba系列(三)使用feign进行服务调用
什么是Feign Feign是spring cloud提供的一个声明式的伪http客户端,它使得调用远程服务就像调用本地服务一样简单,只需要创建一个接口并添加一天注解即可. Nacos很好的兼容了Fe ...
- Print输出颜色字体方法
书写格式: 开头部分:\033[显示方式;前景色;背景色m + 结尾部分:\033[0m 注意:开头部分的三个参数:显示方式,前景色,背景色是可选参数,可以只写其中的某一个:另外由于 ...
- struts2 进阶--异常捕获机制
在SpringMvc中有自己的异常处理机制,struts2当然会有此功能,主要是在struts.xml中配置: <bean type="com.opensymphony.xwork2. ...
- Zabbix-部署
目录 一. apt安装 Zabbix 部署结构图和主机环境 1.1 Zabbix-server 安装配置 1.1.1 安装zabbix仓库 1.1.2 安装Zabbix server.web前端.ag ...
- Vue Element-UI使用第三方icon图标(转)
转载自:https://www.jianshu.com/p/59dd28f0b9c9 1.打开阿里icon,注册 >登录>图标管理>我的项目 2.新建项目 3. 添加icon到项 ...
- import * as x from 'xx' 和 import x from 'xx'
普通xx.js文件 //不可变的依赖模拟数据 module.exports=[ { id: "d52dccfc-656d-11e8-b153-7cd30ad3aa7a", regi ...
- JavaScript高级技术总结
正则表达式 正则表达式的作用 正则表达式的作用: 匹配字符串的一种规则,正则表达式的作用主要是用于匹配字符串的 需求: 校验手机号 <!DOCTYPE html> <html la ...
- 检查可执行App类型是否为executable (腾讯上线预审核报错)otool工具使用
https://blog.csdn.net/lovechris00/article/details/81561627 查看IPA文件的路径 1,解压缩 xcode导出的xxx.ipa文件 2,然后在解 ...
- 一、$HTML, HTTP,web综合问题
1.前端需要注意哪些SEO 合理的title.description.keywords:搜索对着三项的权重逐个减小,title值强调重点即可,重要关键词出现不要超过2次,而且要靠前,不同页面title ...