本文是我学习JavaScript做用域整理的笔记,若有不对,请多指出。html
一个变量的做用域是程序源代码中定义这个变量的区域。前端
而在ES5中只分为全局做用域和函数做用域,也就是说for,if,while等语句是不会建立做用域的。ES6(let,const)除外。程序员
//全局做用域 var a = 123; function aa () { //局部做用域 var b = 456; }
JavaScript函数里声明的全部变量(但不涉及赋值)都被“提高”至函数体的顶部,在代码开始运行以前。这个特性被称为声明提早。segmentfault
var a = "g"; function f() { console.log(a); //输出undefined var a = "l"; console.log(a); //输出"l" }
因为函数做用域的特性,局部变量在整个函数体始终是有定义的,也就是说,函数体的局部变量覆盖了同名全局变量。在函数体内,变量a被“提早”了,提早至函数体的顶部,因此第一次输出的是undefined,那时候还没赋值,但代码执行到var语句时候,局部变量才会被赋值。所以第二次输出则是“l”。此代码过程以下:浏览器
var a = "g"; function f() { var a; console.log(a); //输出undefined a = "l"; console.log(a); //输出"l" }
所以一些程序员特地将变量声明放在函数体的顶部,而不是将声明靠近放在使用变量之处。函数
先看一段简单代码,代码以下:学习
var name = "wythe"; function one () { console.log(name); //wythe var firend = "zero"; } one(); console.log(firend); //报错
看到代码可知,name是在全局做用域中声明的全局变量,而firend则是在函数做用域中声明的局部变量。在执行时候你会发现函数做用域可以访问到在全局做用域中name这个变量,而全局做用域却不能访问到函数做用域的friend的变量,缘由是做用域链!
做用域链的规则:
外部不能访问内部变量,内部能够访问外部变量
为何会有这样规则?由于是执行环境所规定的。this
执行环境定义了变量或函数有权访问其余数据,决定了它们的行为。每一个执行环境都有一个与之关联的变量对象(variable object),环境中定义的全部变量和函数都保存在这个对象中。
全局执行环境是最外围的一个执行环境。在Web浏览器中,全局执行环境被认为是window对象。某个执行环境中全部全部代码执行完毕后,该环境被销毁,保存在其中的全部的变量和函数定义也随之销毁。spa
补充说明:须要了解一些概念,变量对象(Variable Object)、活动对象(Activation Object)、函数的属性[[scope]].设计
变量对象指的是变量对象(缩写为VO)是一个与执行上下文相关的特殊对象,它存储着在上下文中声明的内容有:变量 (var, 变量声明)、函数声明和函数的形参。
执行上下文(执行环境):每次当控制器转到ECMAScript可执行代码的时候,即会进入到一个执行上下文。执行上下文(简称-EC)是ECMA-262标准里的一个抽象概念,用于同可执行代码(executable code)概念进行区分。
活动对象指的是由函数的运行期上下文(代码执行前)建立,在运行时可变,初始时只有 arguments 属性,经过变量的初始化,包含了局部变量、命名参数、 this 等
函数属性[[scope]]指的是函数对象都有一个内部属性 [[scope]],函数被建立后,函数 [[scope]] 属性会被建立此函数的做用域中可访问的数据对象填充,是全部父变量对象的层级链。[[scope]] 在函数被建立时静态存储,永远不会改变,直至销毁。
每一个函数都有本身的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行以后,栈将环境弹出,把控制权返回以前的执行环境。当代码在一个环境执行时候,会建立变量对象的一个做用域链(scope chain)。做用域的前端,始终都是当前执行的代码所在环境的变量对象。如何这个环境是函数,则将其活动对象(activation object)做为变量对象。活动对象在最开始只包含一个变量,即arguments对象(这个对象在全局环境是不存在)。做用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含对象。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是做用域中的最后一个对象。
根据这个概念图解上面代码:
在函数one建立时,它的做用域链中会填入一个全局对象,该全局对象包含了全部全局变量,当执行流执行到one()语句时,会建立函数one执行环境。将函数one执行环境。若是这个环境是函数,则建立一个活动对象,而后此对象会被推入做用域链的前端,当函数执行完毕后,活动对象也随之销毁。新的做用域链以下图所示:
标识符解析是沿着做用域一级一级地搜素标识符的过程。搜素过程始终从做用域的前端开始,而后逐级地向后回溯,直到找到标识符为止,找不到,会致使错误发生。内部环境能够经过做用域链访问全部外部环境,但外部环境不能访问内部环境中任何变量和函数。这些环境之间的联系是线性,有次序的。
这是初步了解做用域,如想更深刻了解做用域,请看下面连接:
JavaScript做用域原理
JavaScript做用域链
由一道题图解JavaScript的做用域或者看《JavaScript权威指南》和《JavaScript高级程序设计》