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

<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. [PHP] PHP源码常用代码中的宏定义

    PHP源码常用代码宏定义:#define 宏名 字符串#表示这是一条预处理命令,所有的预处理命令都以#开头.define是预处理命令.宏名是标识符的一种,命名规则和标识符相同.字符串可以是常数.表达式 ...

  2. Yii2.0源码阅读-从路由到控制器

    之前的文章弄清了一次请求的开始到结束.主要讲了Yii Applicaton实例的创建.初始化,UrlManager如何返回Yii中的路由信息,到runAction,最后将Response发送给客户端. ...

  3. 用户空间网络提升 NFV 的性能

    本文是一篇翻译,翻译自https://software.intel.com/en-us/blogs/2015/06/12/user-space-networking-fuels-nfv-perform ...

  4. c/s与b/s 动态网站与静态网站 (网站编码统一“UTF-8”)

    1.c/s和b/s 第一张图是b/s  可以随时随地的浏览  (在服务器增加网页就能增加功能,只要改变网页就能使用户同步更新,共享性也强,开发也简单,在广域网和局域网都能建造b/s结构,然后通过int ...

  5. Two 观察者 observer pattern

    Two 观察者 定义 在对象之间定义一对多的依赖,当一个对象改变状态,依赖它的对象都会收到通知.并自动更新. 可以观察者自取,也可以主题去推送 其实就是设计两个接口. 相关原则 为交互对象之前的松耦合 ...

  6. bzoj 3874: [Ahoi2014]宅男计划

    Description  [故事背景] 自从迷上了拼图,JYY就变成了个彻底的宅男.为了解决温饱问题,JYY 不得不依靠叫外卖来维持生计. [问题描述] 外卖店一共有N种食物,分别有1到N编号.第i种 ...

  7. Prime Ring Problem(dfs水)

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1016 Prime Ring Problem Time Limit: 4000/2000 MS (Jav ...

  8. excel数据导出新妙招

    之前在做项目的时候需要将数据库中的数据导出为excel表格一遍打印查阅,在网上找了很多插件也没有找到自己理想的好用的插件(也就是说没有找到令我满意的插件),最近在学习a标签的相关知识时理解到,a标签不 ...

  9. oracle 数据库——知识点总结(加示例)

    新入oracle数据库,把目前学到的知识点记录下来,可能都比较基础,但还是比较全的,里面的示例都是自己在PL/SQL中跑过的,如果有错误,还望各位大侠指出哈. 创建用户 1.创建用户(使用管理员身份创 ...

  10. GO开发[五]:golang结构体struct

    Go结构体struct Go语言的结构体(struct)和其他语言的类(class)有同等的地位,但Go语言放弃了包括继承在内的大量面向对象特性,只保留了组合(composition)这个最基础的特性 ...