[JavaScript] JavaScript作用域深度解析
JavaScript作用域
JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里.
—— JS权威指南在JS里,一切皆对象,函数也是。
一、有什么用
什么时候会用到它?它的存在有什么意义?这是我在学习一个新知识的时候首先发起的疑问。那么,在JavaScript里,作用域有什么用?
作用域规定了变量和函数的可访问范围,这个可访问范围可以细分为两个场景:变量和函数的可见性和生命周期。
- 可见性:当访问某个变量的时候,访问的是局部变量还是全局变量?重复命名了多个变量,到底访问的是哪个值?这个变量现在能不能用,为什么有时候会返回undefined?
- 生命周期:变量是一直存在在内存中么?什么时候会被销毁?比如定义在函数内部的变量被称为局部变量,这个变量只存在函数内部,函数调用后将会被销毁,等等...当然如果有闭包什么的就要另当别论,在这里只讨论最一般的情况。
二、全局作用域 & 局部作用域
//函数一
var authorName = "jennifer";
function doSomething() {
anotherName = "james";
var blogName = "smoothlily";
function innerSay() {
alert(blogName);
}
innerSay();
}
console.log(authorName); //jennifer
doSomething(); //smoothlily
console.log(anotherName); //james
console.log(blogName); //脚本错误
innerSay(); //脚本错误
针对可见性这个问题,可以聊一聊JS中规定的两个作用域:全局作用域,局部作用域。
- 全局作用域(Global Scope)
在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域:
最外层函数和在最外层函数外面定义的变量拥有全局作用域:
如函数一的变量authorName。
所有未定义直接赋值的变量自动声明为拥有全局作用域:
如函数一的anotherName。
这种情况在写代码时最好不要出现,想要拥有全局作用域的变量还是乖乖的在脚本最开始显性声明全局变量。
所有window对象的属性拥有全局作用域,例如window.name、window.location、window.top等等
- 局部作用域(Local Scope)
和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,最常见的就是函数内部:如函数一的blogName。
三、作用域链(Scope Chain)
在说作用域链的时候,要分为两个“时间点”:函数创建时以及函数执行时。
这里借用梦想天空做的两个图:
函数创建时:
function add(num1,num2) {
var sum = num1 + num2;
return sum;
}
函数在创建时,会将它定义时刻的scope chain链接到这个函数对象的[[scope]]属性,使其包含创建函数的那个时刻下,作用域中对象的集合,也就是函数的作用域链,这个作用域链直接决定哪些数据被函数访问。
函数执行时:
var total = add(5, 10)
函数执行时,创建一个活动对象,活动对象在一开始的时候,只包含arguments对象。
当遇到变量声明时:
var a = 10;
会将这个变量复制到活动对象中。
当遇到变量访问时,会沿着作用域链做标识符解析(后续会提到),直至找到这个变量。
活动对象包含了函数的所有局部变量、命名参数、参数集合以及this,这些值按照它们出现在函数中的顺序被复制到活动对象中。然后此对象会被推入作用域链的前端,当运行期上下文被销毁,活动对象也随之销毁。
标识符解析
作用域链的用途,是为了保证对执行环境有权访问的对象的有序访问。标识符解析沿着作用域链一级一级地搜索标识符。搜索过程始终从作用域链的最前端开始(活动对象),逐级向后访问(直到全局对象),直至找到标识符为止,如果找不到,则会报错。
其他书单
JavaScript框架设计-司徒正妹