javascript中闭包与作用域的理解
很多js的框架与插件编写都用到了闭包,所以,阅读和掌握闭包很有必要。最近学习vue框架时,经常会猜想很多功能的native js实现,很多都应用到了闭包,闭包除了目前已知的一些特性,如:可以保持局部变量以减少对全局作用域的污染外,一些情况下必须用闭包才能实现。
先贴一个根据自己的理解绘制的闭包原理:
关于闭包的几个重要的知识点需要理解:
1. javascript中,每次运行函数,都会创建一个新的对象,用来保存函数的内部变量,称之为——局部变量保存对象
2. 每次运行外部函数,内部闭包函数都会被重新定义一遍
3. 函数运行时,局部变量保存对象会保存在函数定义时的作用域链中,当函数执行完毕后,没有其他引用指向该作用域链时,会释放上述对象;若有其他引用指向时(如函数运行结束后返回一个闭包的引用),该对象不会被释放。
一些遇到的,必须用闭包的情况:
一、for循环后的外部引用指向同一个作用域,和设计期待背道而驰
在模拟vue的数据监控逻辑时,刚开始写的代码如下:
function Observe (obj){
for( prop in obj ){
var oldval = obj[prop];
Object.defineProperty( obj,prop,{
get:function(){
return oldval;
},
set:function(newval){
console.log( prop+'的数据改变了,老的值:'+oldval+"新的值:"+newval );
oldval = newval;
}
} );
}
} var data = {
name:'tester',
age:16,
class:'04071135'
}; Observe( data );
在firefox控制台打印data.name,状况如下:
很明显,虽然这里为data的每个属性(name,age,class)定义了getter和setter存取器——即每个属性的getter/setter外部应用都指向了每次重新定义的闭包,但这些闭包共享了一个父函数作用域,在循环执行完毕后,该作用域内挂在的变量对象信息如下:
prop: 'class';
oldval:'04071135'
所以,当调用虽有的外部引用——属性的存取器getter和setter时,访问的是同一个变量对象,所以,执行结果都是一样的,再多打印一些信息看看:
经上述分析可知,因为这些外部引用都指向了同一个作用域里的变量对象才导致的bug,解决的办法就是,让每个外部引用都能保有自己独立的作用域,这就想到了闭包:
修改上述Observe函数为:
function Observe( obj ){
function defineprop( obj,prop ){
//在没有开始define之前获取属性值,这样就不会陷入死循环
var oldval = obj[prop];
Object.defineProperty(obj,prop,{
get:function(){
return oldval;
},
set:function(newval){
console.log( prop+'的数据改变了,老的值:'+oldval+"新的值:"+newval );
oldval = newval;
}
});
}
var keys = Object.keys( obj );
for( var i=0;i<keys.length;i++ ){
defineprop( obj,keys[i] );
}
} Observe( data );
再在控制台打印测试,结果如下:
这里就是运用了闭包的特点,虽然内部定义了defineprop函数后就调用了,如果没有外部引用指向时,就会释放defineprop里的prop,oldval等,但因为data的属性存取器指向了defineprop内的闭包(get和set),所以,defineprop里的局部变量得得以保留。每个属性存取器都有了自己的作用域—持久化的变量保存对象,因此,访问起来就没什么问题了。
再看下面一个小例子,是上面的简化版;
var obj_arr = [{},{},{}]; for( var i=0;i<obj_arr.length;i++ ){
obj_arr[i]["getdata"] = function(){
return i;
}
}
此时在控制台访问一下对象的getdata看看:
这些引用均访问同一块公用的作用域;
根据上述的闭包思路重新改写代码:
var obj_arr = [{},{},{}];
function addData( val ){
obj_arr[val]["getdata"] = function(){
return val;
}
}
for( var i=0;i<obj_arr.length;i++ ){
addData(i);
}
打印结果如下,正常。
二、闭包在作用域绑定方面也很有用
将方法的执行绑定在对象上以后,用户不用再关注方法执行的作用域,直接调用即可,这种思路的实现可以参考:https://www.cnblogs.com/surfer/p/9625725.html
未完待续...
javascript中闭包与作用域的理解的更多相关文章
- Javascript中闭包的作用域链
作用域定义了在当前上下文中能够被访问到的成员,在Javascript中分为全局作用域和函数作用域,通过函数嵌套可以实现嵌套作用域. 闭包一般发生在嵌套作用域中.闭包是JavaScript最强大的特性之 ...
- 在Javascript中闭包(Closure)
在Javascript中闭包(Closure) 什么是闭包 “官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. ...
- javascript中闭包最简单的简绍
javascript中闭包是什么 JavaScript 变量可以是局部变量或全局变量.私有变量可以用到闭包.闭包就是将函数内部和函数外部连接起来的一座桥梁. 函数的闭包使用场景:比如我们想要一个函数来 ...
- 在JavaScript中闭包的作用和简单的用法
在JavaScript中闭包的作用和简单的用法 一.闭包的简介 作用域链:在js中只有函数有作用域的概念,由于函数内能访问函数外部的数据,而函数外部不能访问函数内部的数据,由上述形成一种作用域访问的链 ...
- javascript中的this作用域详解
javascript中的this作用域详解 Javascript中this的指向一直是困扰我很久的问题,在使用中出错的机率也非常大.在面向对象语言中,它代表了当前对象的一个引用,而在js中却经常让我觉 ...
- JavaScript中call、apply个人理解
JavaScript中call.apply个人理解 一句话即通俗的说:call.apply 是为了改变this的状态而存在的 }; } function personInfo(name,age){ t ...
- 对js中闭包,作用域,原型的理解
前几天,和朋友聊天,聊到一些js的基础的时候,有一种‘好像知道,好像又不不知道怎么讲的感觉’...于是捡起书,自己理一理,欢迎拍砖. 闭包 理解闭包首先要理解,js垃圾回收机制,也就是当一个函数被执行 ...
- 对JavaScript中闭包的理解
在前端开发中闭包是一个很重要的知识点,是面试中一定会被问到的内容.之前我对闭包的理解主要是"通过闭包可以在函数外部能访问到函数内部的变量",对闭包运用的也很少,甚至自己写过闭包自己 ...
- Javascript中闭包的个人理解
Javascript的一个特殊点就在于它的闭包和回调特性,这两个特性让初学Javascript的我是云里雾里,至今仍在苦苦摸索与理解.在一番苦思之后,整理了一下资料,将自己的理解思路记录下来,以 ...
随机推荐
- 挖掘更合适的MVP模式的架构设计
关于MVP,关于android,不得不说这篇博客已经来的非常晚了,这篇博客早就想写了,一直都在偷懒,就不给自己这么久的偷懒找借口了.尽管这篇文章po出来的比較晚.可是我所接触的程序猿一些朋友之 ...
- UISlider无法拖动进度条的问题解决
UISlider无法拖动进度条的问题解决 最近业务中的视频播放使用到了UISlider,但是有一个奇怪的问题,就是在Modar出来的控制器中UISlider是可以正常使用的,但是在Push出来的控制器 ...
- ASP.NET MVC 提供与访问 Web Api
ASP.NET MVC 提供与访问 Web Api 一.提供一个 Web Api 新建一个项目,类型就选 "Web Api".我用的是MVC5,结果生成的项目一大堆东西,还编译不过 ...
- vs2013发布网站提示 “未能将文件**复制到**”
原因:年久失修,原来在项目中的一些文件给删掉或移除了 解决方法:打开.csproj文件(记事本打开),把提示的文件给删除掉.
- Lightoj 1002 - Country Roads(prim算法)
I am going to my home. There are many cities and many bi-directional roads between them. The cities ...
- POJ3692 Kindergarten —— 二分图最大团
题目链接:http://poj.org/problem?id=3692 Kindergarten Time Limit: 2000MS Memory Limit: 65536K Total Sub ...
- mac idea 内存
vim /Applications/IntelliJ\ IDEA.app/Contents/bin/idea.vmoptions -Xms512m -Xmx2048m -XX:ReservedCode ...
- 字符串输出输入函数,const修饰符,内存分区,动态内存管理,指针和函数,结构体
1.字符串输出输入函数 读入字符串的方法: 1) scanf 特点:不能接收空格 2) gets 特点:可以接受含有空格的字符串 ,不安全 3) fgets(); 特点:可以帮我们自动根据数组的长度截 ...
- 微信小程序中如何使用setData --- 修改数组对象、修改对象
看代码吧~ 这是修改对象 this.setData({ allStageIndex: e.detail.value, [`projectDetailsData.stage`]: this.data.a ...
- JAVA 添加、修改和删除PDF书签
当阅读篇幅较长的PDF文档时,为方便我们再次阅读时快速定位到上一次的阅读位置,可以插入一个书签进行标记:此外,对于文档中已有的书签,我们也可以根据需要进行修改或者删除等操作.本篇文章将通过Java编程 ...