每个函数都有自己的作用域,当执行流进入一个函数时,函数就会被推入栈中,而在函数执行之后,栈将其执行环境弹出,把控制权放回给之前的作用域,全局作用域是最外围的一个作用域,因此,所有全局变量和函数都是作为window对象的属性和方法创建的。在某个方法函数的作用域中,所有代码执行完之后,该作用域被销毁,保存在其中的所有变量和函数定义也会随着被销毁,这就是局部作用域。

(PS:全局作用域直到应用程序退出,例如关闭网页活浏览器,才会被销毁。)

我个人理解的作用域链就是,当你声明一个函数时,局部作用域一级一级往上扣起来,就是作用域链。

如图:

作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。

好了好了~  书面语言终于说完了。我们还是来看看代码吧~

先来个基础点的:

		var color = "blue";
function changeColor(){
  var anothercolor = "red";
if(color==="blue"){
color = anothercolor;
}
  //这里可以访问anothercolor,color }
//这里只可以访问color
changeColor();
console.log(color);//red
console.log(anothercolor);// undefined

  解析:函数changeColor()的作用域链包含两个对象,它自己的变量对象和全局环境的变量对象。全局环境的变量对象(即window的对象)有color,changColor(),而changeColor()的变量对象是color,anothercolor。

(用我自己的话来解释就是:父级不能访问子级的变量,但是子级可以父级的变量,祖父的变量,曾祖父的变量......哈哈~。并且我们执行流的访问顺序也是从子级开始,一级一级往上查找你的需要的变量,最后一级就是全局变量。)

来看一下作用域链的典型栗子:

var x = 10;

	function foo(){

		var y = 20;

		function bar(){
var z = 30; console.log(x+y+z);
}; bar(); }; foo();//60

  解析:上面代码的输出结果是“60”,函数bar()可以直接访问"z",然后通过作用域链访问上层的“x”和“y”。

大概基本概念弄清楚了吧?!~~

那我要开始讲一些注意事情。。

JS没有块级作用域。

说明:在其他类C语言中,由{花括号}封闭的代码块都有自己的作用域,但是随和亲切的JS并非如此。

举个栗子:

if(true){
var color = "blue";
}
alert(color);//blue

  解析:如果在C,C++,或JAVA中,color会在if语句执行完毕被销毁。但在JS中,if语句中的变量声明会将变量添加到当前的作用域(这里的全局环境)。

四不四觉得有点郁闷?怎么color这个变量还存在,不应该执行完之后,被销毁了吗?

一开始小编也是很郁闷,再想想JS的执行流,小编目前的理解是:JS一切皆对象,只有函数方法可以封装,也只有函数的执行需要被调用,所以每个函数方法都有自己的作用域,而像if和for循环等等,这些直接执行的引用函数,所定义的变量都会被添加在与它同兄弟级的作用域中。

现在再来看个栗子:

for(var i = 0 ; i < 10;i++){
doSomething(i);
}
alert(i);//10

解析:由for语句创建的变量i即使在for循环执行结束后,也依旧会存在于循环外部的执行环境中。

 结合作用域看闭包


在JS中,闭包和作用域有紧密的关系。相信大家对下面的闭包栗子一定灰常熟悉,代码中通过闭包实现了一个简单的计算器。

function counter(){
var x = 0; return{
increase:function increase(){return ++x;},
decrease:function decrease(){return --x;}
};
} var ctor = counter(); console.log(ctor.increase());//1
console.log(ctor.decrease());//0

  解析:四不四又纳闷了,怎么counter()函数退出了执行上下文栈,但是变量x还没有被销毁。闭包有三大特性:1.函数嵌套函数 2.函数内部可以引用外部的参数和变量 3.参数和变量不会被垃圾回收机制回收。这里很明显,counter()函数利用闭包,把变量x引用到全局变量中。当var ctor = counter(),执行完毕后,ctor={increase:function(){...},decrease:function(){...}},这里需要注意的是,counter虽然退出了执行上下文栈,但是因为ctor中的成员仍然引用counter 的活动变量,所以counter的变量x依然在作用域中。

(附上个人对闭包的理解:在函数嵌套函数中,子函数获取父函数的私有变量,通过return引用到外部的作用域中。)

作用域链的主要作用就是用来变量查找,但是在JS中还有原型链的概念。

于是对于二维作用域链查找可以总结为:当代码需要查找一个属性或者描述符的时候,首先会通过作用域链来查找相关的对象,一旦对象被找到,就会根据对象的原型链来查找属性。

下面来举个栗子:

var foo = {}
function baz(){ Object.prototype.a = 'Set foo.a from prototype'; return function inner(){
console.log(foo.a);
}
}
baz()();//Set foo.a from prototype

  解析:对于这个栗子,流程是这样的,在inner()并没有发现foo,就通过作用域链去baz查找,也没有在baz里面找到作用域链,就去到全局环境,找到了foo,但是并没有找到属性a,于是就去到了foo._proto_的原型链中找到了属性a,便输出该值。

文章说明:个人查看各种资料,原创所得,观点不一定准确,欢迎各路大牛勘误,小女子感激不尽。

JS的作用域和作用域链的更多相关文章

  1. 了解 JS 作用域与作用域链

    (1)作用域 一个变量的作用域(scope)是程序源代码中定义的这个变量的区域. 1. 在JS中使用的是词法作用域(lexical scope) 不在任何函数内声明的变量(函数内省略var的也算全局) ...

  2. Js作用域与作用域链详解

    一直对Js的作用域有点迷糊,今天偶然读到Javascript权威指南,立马被吸引住了,写的真不错.我看的是第六版本,相当的厚,大概1000多页,Js博大精深,要熟悉精通需要大毅力大功夫. 一:函数作用 ...

  3. Js作用域与作用域链详解[转]

     一直对Js的作用域有点迷糊,今天偶然读到JavaScript权威指南,立马被吸引住了,写的真不错.我看的是第六版本,相当的厚,大概1000多页,Js博大精深,要熟悉精通需要大毅力大功夫. 一:函数作 ...

  4. js作用域与作用域链

    一直对Js的作用域有点迷糊,今天偶然读到JavaScript权威指南,立马被吸引住了,写的真不错.我看的是第六版本,相当的厚,大概1000多页,Js博大精深,要熟悉精通需要大毅力大功夫. 一:函数作用 ...

  5. js学习--变量作用域和作用域链

    作为一名菜鸟的我,每天学点的感觉还是不错的.今天学习闭包的过程中看到作用域与作用域链这两个概念,我觉得作为一名有追求的小白,有必要详细了解下. 变量的作用域 就js变量而言,有全局变量和局部变量.这里 ...

  6. JS -- The Scope Chain 作用域链

    The Scope Chain JavaScript is a lexically scoped language: the scope of a variable can be thought of ...

  7. js 作用域,作用域链,闭包

    什么是作用域? 作用域是一种规则,在代码编译阶段就确定了,规定了变量与函数的可被访问的范围.全局变量拥有全局作用域,局部变量则拥有局部作用域. js是一种没有块级作用域的语言(包括if.for等语句的 ...

  8. Js 作用域与作用域链与执行上下文不得不说的故事 ⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄

    最近在研究Js,发现自己对作用域,作用域链,活动对象这几个概念,理解得不是很清楚,所以拜读了@田小计划大神的博客与其他文章,受益匪浅,写这篇随笔算是自己的读书笔记吧~. 作用域 首先明确一个概念,js ...

  9. 理解js中的作用域,作用域链以及闭包

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

随机推荐

  1. SQL IN BETWEEN操作符

    IN 操作符 IN 操作符允许我们在 WHERE 子句中规定多个值. SQL IN 语法 SELECT column_name(s) FROM table_name WHERE column_name ...

  2. 《Linux内核分析》 week6作业-Linux内核fork()系统调用的创建过程

    一.进程控制块PCB-stack_struct 进程在操作系统中都有一个结构,用于表示这个进程.这就是进程控制块(PCB),在Linux中具体实现是task_struct数据结构,它主要记录了以下信息 ...

  3. javascript时钟

    <script language="JavaScript" type="text/javascript">         function now ...

  4. JQuery里属性赋值,取值prop()和attr()方法?

    1.赋值的时候 如果是<input type="checkbox" checked>这样的只有属性名就能生效的属性 推荐prop,即:$('input').prop(' ...

  5. OpenStack REST API使用

    以前一直想写博客,但因各种因素一直没写.最近在做OpenStack,而OpenStack对外提供REST API,今天就简要介绍一下OpenStack REST API 使用,关于什么是REST AP ...

  6. HashMap HashTable HashSet区别剖析

    HashMap.HashSet.HashTable之间的区别是Java程序员的一个常见面试题目,在此仅以此博客记录,并深入源代码进行分析: 在分析之前,先将其区别列于下面 1:HashSet底层采用的 ...

  7. C++构造函数 & 拷贝构造函数 & 派生类的构造函数 & 虚继承的构造函数

    构造函数 ,是一种特殊的方法 .主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中 .特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数 ...

  8. 最难忘的Bug调试经历

    摘要:目前,著名的社区问答网站Quora上出现一个很火的讨论:你调试过最难的Bug是什么?大家纷纷留言,把自己最痛苦的一次调试经验写下来. 相信每位程序员都有过一段不堪回首地Bug调试经历,程序员一听 ...

  9. 热门usb无线网卡

    拓实 N910 N95 N82 N81 N89 都是3070的 拓实 N87 G618 是8187的硬功夫 216 310 217 218 300 315 335 350 370 380 510 53 ...

  10. 2015年6月股灾永远载入A股史册

    之前很奇怪这次牛市行情的暴涨与暴跌,后来发现成交量是以往不可想象的,而这一切的源头就是融资融券,也就是杠杆. 杠杆的作用是无穷的,就像阿基米德说的那样“给我一个支点,我就能撬起地球”. 杠杆是个放大器 ...