作用域
变量作用域的类型:全局变量和局部变量
全局作用域
对于最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的

<script>
var outerVar = "outer";
function fn(){
console.log(outerVar);
}
fn();//result:outer
</script>

局部作用域
和全局用域相反,局部作用域一般只在固定的代码片段内可访问到,对于函数外部是无法访问的

<script>
function fn(){
var innerVar = "inner";
}
fn();
console.log(innerVar);// ReferenceError: innerVar is not defined
</script>

注意
需要注意的是,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!

<script>
function fn(){
innerVar = "inner";
}
fn();
console.log(innerVar);// result:inner
</script>

作用域链
我的理解就是,根据在内部函数可以访问外部函数变量的这种机制,用链式查找哪些数据能被内部函数访问
执行环境
每个函数在运行时都会产生一个执行环境。js为每个执行环境关联了一个变量对象。环境中定义的所有变量和函数都保存在这个对象中
作用域链理解

<script>
var scope = "global";
function fn1(){
return scope;
}
function fn2(){
return scope;
}
fn1();
fn2();
</script>

当某个函数第一次被调用时,就会创建一个执行环境(execution context)以及相应的作用域链,并把作用域链赋值给一个特殊的内部属性([scope])。然后使用this,arguments(arguments在全局环境中不存在)和其他命名参数的值来初始化函数的活动对象(activation object)。当前执行环境的变量对象始终在作用域链的第0位。
以上面的代码为例,当第一次调用fn1()时的作用域链如下图所示:

可以看到fn1活动对象里并没有scope变量,于是沿着作用域链(scope chain)向后寻找,结果在全局变量对象里找到了scope,所以就返回全局变量对象里的scope值。

闭包

闭包有两个作用: 
第一个就是可以读取自身函数外部的变量(沿着作用域链寻找) 
第二个就是让这些外部变量始终保存在内存中

关于第二点,来看一下以下的代码:

<script>
function outer(){
var result = new Array();
for(var i = 0; i < 2; i++){//注:i是outer()的局部变量
result[i] = function(){
return i;
}
}
return result;//返回一个函数对象数组
//这个时候会初始化result.length个关于内部函数的作用域链
}
var fn = outer();
console.log(fn[0]());//result:2
console.log(fn[1]());//result:2
</script>

返回结果很出乎意料吧,你肯定以为依次返回0,1,但事实并非如此 
来看一下调用fn[0]()的作用域链图:

可以看到result[0]函数的活动对象里并没有定义i这个变量,于是沿着作用域链去找i变量,结果在父函数outer的活动对象里找到变量i(值为2),而这个变量i是父函数执行结束后将最终值保存在内存里的结果。 
由此也可以得出,js函数内的变量值不是在编译的时候就确定的,而是等在运行时期再去寻找的。

那怎么才能让result数组函数返回我们所期望的值呢?

<script>
function outer(){
var result = new Array();
for(var i = 0; i < 2; i++){
//定义一个带参函数
function arg(num){
function innerarg(){
return num;
}
return innerarg;
}
//把i当成参数传进去
result[i] = arg(i);
}
return result;
}
var fn = outer();
console.log(fn[0]());
console.log(fn[1]());
</script>

由上图可知,当调用innerarg()时,它会沿作用域链找到父函数arg()活动对象里的arguments参数num=0. 
上面代码中,函数arg在outer函数内预先被调用执行了,对于这种方法,js有一种简洁的写法

function outer(){
var result = new Array();
for(var i = 0; i < 2; i++){
//定义一个带参函数
result[i] = function(num){
function innerarg(){
return num;
}
return innerarg;
}(i);//预先执行函数写法
//把i当成参数传进去
}
return result;
}

使用闭包的注意点

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

理解js中的作用域,作用域链以及闭包的更多相关文章

  1. 图文结合深入理解 JS 中的 this 值

    图文结合深入理解 JS 中的 this 值 在 JS 中最常见的莫过于函数了,在函数(方法)中 this 的出现频率特别高,那么 this 到底是什么呢,今天就和大家一起学习总结一下 JS 中的 th ...

  2. 深入理解Js中的this

    深入理解Js中的this JavaScript作用域为静态作用域static scope,但是在Js中的this却是一个例外,this的指向问题就类似于动态作用域,其并不关心函数和作用域是如何声明以及 ...

  3. 深度理解js中var let const 区别

    首先要理解js中作用域的概念 作用域:指的是一个变量的作用范围 1.全局作用域 直接写在script中的js代码,在js中,万物皆对象,都在全局作用域,全局作用域在页面打开时创建,在全局作用域中有一个 ...

  4. 如何理解js中的this和实际应用中需要避开哪些坑

    this是什么 this就是函数内部的关键字 看下面例子理解js中的this // 例子1 function fnOne () { console.log(this) } 'use strict' f ...

  5. 深入理解JS中的对象(二):new 的工作原理

    目录 序言 不同返回值的构造函数 深入 new 调用函数原理 总结 参考 1.序言 在 深入理解JS中的对象(一):原型.原型链和构造函数 中,我们分析了JS中是否一切皆对象以及对象的原型.原型链和构 ...

  6. 深入理解JS中的对象(三):class 的工作原理

    目录 序言 class 是一个特殊的函数 class 的工作原理 class 继承的原型链关系 参考 1.序言 ECMAScript 2015(ES6) 中引入的 JavaScript 类实质上是 J ...

  7. 怎么理解js中的事件委托

    怎么理解js中的事件委托 时间 2015-01-15 00:59:59  SegmentFault 原文  http://segmentfault.com/blog/sunchengli/119000 ...

  8. 如何更好的理解js中的this,分享2段有意思的代码

    关于js中this的浅析,大家可以点击[彻底理解js中this的指向,不必硬背]这篇博客了解. 今天遇到2段比较有意思的代码. ----------------第一段----------------- ...

  9. 理解js中的自由变量以及作用域的进阶

    如果你不知道什么是作用域,建议你先看什么是作用域链,什么是原型链.这篇文章,因为这些内容都是有关联性的. 什么是自由变量? 如我在全局中定义了一个变量a,然后我在函数中使用了这个a,这个a就可以称之为 ...

  10. js中变量的作用域、变量提升、链式作用域结构

    一:作用域 在ES6之前,javascript没有块级作用域(一对{}即为一个块级作用域),只有全局作用域和函数作用域(局部),因此,对应的有全局变量和局部变量.在函数内部可以访问到全局变量,但在函数 ...

随机推荐

  1. JAVA进阶--ThreadPoolExecutor机制

    ThreadPoolExecutor机制 一.概述 1.ThreadPoolExecutor作为java.util.concurrent包对外提供基础实现,以内部线程池的形式对外提供管理任务执行,线程 ...

  2. Nginx性能优化技巧(6)

    一.编译安装过程优化 1.减小Nginx编译后的文件大小 在编译Nginx时,默认以debug模式进行,而在debug模式下会插入很多跟踪和ASSERT之类的信息,编译完成后,一个Nginx要有好几兆 ...

  3. 下载的js文件本地编辑器打开中文乱码如何解决

    今天遇到的小问题,已解决,直接上图 下载直接打开是这样的 用记事本打开 另存为utf-8格式 正常了!

  4. Centos7 Zookeeper

    本文版权归博客园和作者吴双本人共同所有 转载和爬虫请注明原文地址 www.cnblogs.com/tdws 一.写在前面 ZK是一个高效的分布式协调服务,高可用的分布式管理协调框架. 朋友推荐一本书& ...

  5. Java集合源码分析(四)HashMap

    一.HashMap简介 1.1.HashMap概述 HashMap是基于哈希表的Map接口实现的,它存储的是内容是键值对<key,value>映射.此类不保证映射的顺序,假定哈希函数将元素 ...

  6. BZOJ 3680: 吊打XXX【模拟退火算法裸题学习,爬山算法学习】

    3680: 吊打XXX Time Limit: 10 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 3192  Solved: 1198[Sub ...

  7. 客户端一致性与多Leader机制------《Designing Data-Intensive Applications》读书笔记7

    接着上一篇的内容,我们继续来梳理分布式系统之中的副本机制与副本一致.上文我们聊到了在可用性与一致性之间的一个折中的一致性等级:最终一致性.我们顺着上篇的内容,由用户来分析一致性等级. 1. 客户端的困 ...

  8. TI-RTOS 之 PWM

    TI-RTOS 之 PWM CC1310 有4个定时器,8个PWM通道,在TI-RTOS它的驱动是写好的,引用时需要包含 PWM.h头文件即可. 一般是任务主体之前,或者主函数进行初始化. Board ...

  9. 给织梦DEDECMS添加栏目图片与英文名显示

    开始做微网站了,不同于传统手机网站,因为微信上的微网站是支持CSS3与HTML5的,好吧,各种要学习的还有很多很多阿~这么多新代码,叹! 本来想转战帝国CMS了,奈何这名字太不对味了,PHPCMS也懒 ...

  10. Uva 1599 Ideal Path - 双向BFS

    题目连接和描述以后再补 这题思路很简单但还真没少折腾,前后修改提交了七八次才AC...(也说明自己有多菜了).. 注意问题: 1.看清楚原题的输入输出要求,刚了书上的中文题目直接开撸,以为输入输出都是 ...