作用域和作用域链

什么是作用域

作用域(scope)是指程序源代码中定义变量的区域,简单来说,一段程序代码中所用到的变量并不总是有效的,而限定这个变量的可用性的代码范围就是这个变量的作用域。

在JavaScript中使用的作用域是词法作用域(静态作用域),特点就是变量的作用域在变量定义时确定。下文所说的作用域都指代词法作用域。

全局作用域

全局作用域是最外层的一个作用域。是根据ECMAScript实现所在的宿主环境而改变的,在浏览器中,全局作用域就是Winodw对象,node则是global对象。

在全局作用域的变量可以在所有作用域中被访问,假如将全局作用域比作中国,中国人这个属性就拥有了全局作用域,无论你是湖北人还是武汉人都可以说自己是中国人,

在JAVAScript中

1.window(global)

global.country ='Chinese'
function province (){
	var province = '湖北'
	console.log('province'} 
province() //Chinese

2最外层的变量或者函数

var country = '中国'
function province(){
	var province = '湖北'
	return province
}
function city(){
	var city ='武汉'
	console.log(contry,province(),city)
}
city() //中国 湖北 武汉

3.未定义直接赋值的变量

function province(){
	var province = '湖北'
	country = '中国'
	return province
}
function city(){
	var city ='武汉'
	console.log(contry,province(),city)
}
city() //中国 湖北 武汉

局部作用域

和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,最常见的是函数内部。

函数作用域
定义在函数中的变量就处于函数作用域中。不同函数作用域中,变量不能相互访问。还是按照上面的例子举例,

如果你是浙江人,你可以说你是中国人,但是你不能说你是福建人。当然如果你是武汉人,你可以说自己是湖北人,因为湖北的函数作用域包含了武汉的函数作用域。

简单来说就是里面的可以访问外面的作用域,但是不能访问里层的和同级的作用域
接下来在学习作用域链前 我们需要先学习预编译

预编译

执行期上下文 :一个函数在运行之前,创建一块内存空间,这个空间中会存放所有该函数执行所需要的用到的数据,为该函数提供支持;this,AO,arguments[0]

// 变量声明提升 //函数声明整体提升

函数预编译:
从函数执行的前一刻开始:
1.创建一个函数的AO对象(Activation Object),执行期上下文对象
2.函数的形参,成为AO对象的属性,值为实参的值,若未传值,值为undefined
3.将var关键字声明的变量,成为AO对象的属性,值为undefined,遇到重名,不做任何变化
4.将function声明的函数(函数声明)成为AO对象的属性,值为函数体,重名直接覆盖

全局预编译:

1.创建一个函数的GO对象(Global Object),执行期上下文对象
3.将var关键字声明的变量,成为AO对象的属性,值为undefined,遇到重名,不做任何变化
4.将function声明的函数(函数声明)成为AO对象的属性,值为函数体,重名直接覆盖

作用域链

消化吸收了之前的预编译之后,我们就可以去了解作用域链了,作用域链最泛用的场景就是函数,所以下面例子都是以函数为主体,话不多说,先看例子。

var z = 1 
function tim(){
   function cope(){
       var x= 2;
       y=3
   }
   var y = 4
   cope()
   console.log(y)  // 3
}
tim()

运行代码之后我们就可以发现输出为3,那么作为cope作用域中的值3是怎么在tim作用域中输出的呢?

答案就是作用域链。

每个JavaScript对象都有属性,有些可以被我们访问,有些却不行,这些属性仅供js引擎存取,而我们接下来要讲的[[scope]]就是这样的一个属性。

当函数被定义时它就被绑定了[[scope]]属性,而[[scope]]中存储的就是执行上下文的集合(GO | AO),从作用域链的顶端依次向下查找其呈链式链接,我们称之为作用域链。让我们再回头来看之前的例子

var z = 1 
//1.tim 定义 tim.[[scope]] = GO:{z:1,tim:[Function: tim]}
function tim(){
  //3.cope定义 cope.[[scope]] = tim.[[scope]] = tim(AO):{cope:[Function: cope],y:undefined}=>GO
   function cope(){
       var x= 2;
         // 6.cope(AO)上没有y属性,就会沿着作用域链往上找,一直没有就会挂载在GO上
       y=3
   }
  //4.tim(AO):{cope:[Function: cope],y:4}
   var y = 4
   //5.cope.[[scope]] = cope(AO):{x:undefined}=>tim(AO)=>GO
   cope()
  //7.tim(AO):{cope:[Function: cope],y:4}
   console.log(y)  // 3
}
//2.tim 执行 tim.[[scope]] = tim(AO):{cope:[Function: cope],y:undefined}=>GO:{z:1,tim:[Function: tim]}
tim()

按照代码运行的顺序大致就是如此,不熟悉的可能需要三五分钟仔细消化一下这个流程。

简单总结一下

1.作用域链存储的就是执行上下文的集合

2.当前作用域中没有使用未在在作用域定义的变量时,会沿着顶端依次向下查。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值