很多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中闭包与作用域的理解的更多相关文章

  1. Javascript中闭包的作用域链

    作用域定义了在当前上下文中能够被访问到的成员,在Javascript中分为全局作用域和函数作用域,通过函数嵌套可以实现嵌套作用域. 闭包一般发生在嵌套作用域中.闭包是JavaScript最强大的特性之 ...

  2. 在Javascript中闭包(Closure)

    在Javascript中闭包(Closure) 什么是闭包 “官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. ...

  3. javascript中闭包最简单的简绍

    javascript中闭包是什么 JavaScript 变量可以是局部变量或全局变量.私有变量可以用到闭包.闭包就是将函数内部和函数外部连接起来的一座桥梁. 函数的闭包使用场景:比如我们想要一个函数来 ...

  4. 在JavaScript中闭包的作用和简单的用法

    在JavaScript中闭包的作用和简单的用法 一.闭包的简介 作用域链:在js中只有函数有作用域的概念,由于函数内能访问函数外部的数据,而函数外部不能访问函数内部的数据,由上述形成一种作用域访问的链 ...

  5. javascript中的this作用域详解

    javascript中的this作用域详解 Javascript中this的指向一直是困扰我很久的问题,在使用中出错的机率也非常大.在面向对象语言中,它代表了当前对象的一个引用,而在js中却经常让我觉 ...

  6. JavaScript中call、apply个人理解

    JavaScript中call.apply个人理解 一句话即通俗的说:call.apply 是为了改变this的状态而存在的 }; } function personInfo(name,age){ t ...

  7. 对js中闭包,作用域,原型的理解

    前几天,和朋友聊天,聊到一些js的基础的时候,有一种‘好像知道,好像又不不知道怎么讲的感觉’...于是捡起书,自己理一理,欢迎拍砖. 闭包 理解闭包首先要理解,js垃圾回收机制,也就是当一个函数被执行 ...

  8. 对JavaScript中闭包的理解

    在前端开发中闭包是一个很重要的知识点,是面试中一定会被问到的内容.之前我对闭包的理解主要是"通过闭包可以在函数外部能访问到函数内部的变量",对闭包运用的也很少,甚至自己写过闭包自己 ...

  9. Javascript中闭包的个人理解

       Javascript的一个特殊点就在于它的闭包和回调特性,这两个特性让初学Javascript的我是云里雾里,至今仍在苦苦摸索与理解.在一番苦思之后,整理了一下资料,将自己的理解思路记录下来,以 ...

随机推荐

  1. [LeetCode][Java] Remove Duplicates from Sorted List II

    题意: Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct  ...

  2. Java-ReentrantReadWriteLock的简单样例

    内容:读锁时共享的.写锁时相互排斥的(可见执行结果).都是通过AQS实现的. public class ReentrantReadWriteLockTest { static class MyObje ...

  3. UIButton的图片和文字相对位置调整

    通常.假设直接设置UIButton的图片和文字,默认的两者相对位置可能不是我们想要的,那么须要进行调整. 须要用到的函数例如以下: UIEdgeInsetsMake(CGFloat top, CGFl ...

  4. web 开发之js---ajax 异步处理

    本文介绍了如何创建能够适应不同浏览器的XMLHttpRequest实例,建立和发送请求,并响应服务器.您将开始接触最基本和基础性的有关Ajax的全部对象和编程方法:XMLHttpRequest对象.该 ...

  5. 【iOS开发】-NSString的扩展使用

    第一:基本数据类型与字符串转换 //基本数据类型(int float,double char) 1)基本数据类型->NSString //1.int类型换换成字符串 int a = 88; NS ...

  6. STL_算法_元素计数(count、count_if)

    C++ Primer 学习中.. . 简单记录下我的学习过程 (代码为主) count . count_if #include<iostream> #include<cstdio&g ...

  7. UIwebView缩放

    首先就是需要让webView去设置下可以支持缩放 [__webView setScalesPageToFit:YES]; 如果网页支持缩放只需要上面的一句就可以了.你可以加在谷歌的试一下,但是你要加在 ...

  8. POJ之01背包系列

    poj3624 Charm Bracelet 模板题 没有要求填满,所以初始化为0就行 #include<cstdio> #include<iostream> using na ...

  9. tomcat项目重复加载问题

    主要是通过配置<Tomcat安装目录>/conf/server.xml文件 步骤: 1.打开server.xml,在</Host>的上一行添加内容格式如下 <Contex ...

  10. HBase 数据迁移

    最近两年负责 HBase,经常被问到一些问题, 本着吸引一些粉丝.普及一点HBase 知识.服务一点阅读人群的目的,就先从 HBase 日常使用写起,后续逐渐深入数据设计.集群规划.性能调优.内核源码 ...