作用域链和闭包理解

一   作用域链
1.在理解什么事作用域链之前我们首先要了解几个概念:变量、变量的声明、变量声明提前
变量:变量包括两种,普通变量和函数变量。 
普通变量:凡是用var标识的都是普通变量
var x=1;               
var object={};
var  getA=function(){};  //以上三种均是普通变量,但是这三个等式都具有赋值操作。所以,要分清楚声明和赋值。声明是指 var x; 赋值是指 x=1; 

函数变量函数变量特指的是下面的这种,fun就是一个函数变量。

function fun(){} ;// 这是指函数变量. 函数变量一般也说成函数声明。

为了便于理解,我们在这里将将分开讨论普通变量的声明和函数变量的声明

普通变量声明是在函数第一行代码执行之前就已经完成,而赋值是在函数执行时期才开始赋值。所以,声明总是存在于赋值之前。
而且,普通变量的声明时期总是等于undefined.所以如果在赋值之前就调用普通变量的话就会是undefined;

alert(x);
var x=3;//undefined




函数变量函数声明也是提前到在函数第一行代码之前,所以如果在一段代码中先调用函数,然后再写出函数,也是可以被正确执行的,因为函数声明会提前。
f(5);

function f(n){alert(n)}//5

2 作用域的组成

函数的作用域,也就是函数的执行环境,所以函数作用域内肯定保存着函数内部声明的所有的变量。

所以函数的变量的来源无非三个方面:A:函数的参数
B:函数内部的变量
C:函数外部的作用域的变量

举个栗子:
var x = 1;

function add(num){
var y = 2;
return x+y+num;
}

console.log(add(5));//console  7

在函数调用的时候函数的作用域才存在,调用之前开始创建函数的作用域(执行环境)

创建步骤:a.函数形参的声明
b.函数内部变量的声明
c.普通变量的声明(执行函数外部的变量的声明)
d.函数内部this指针赋值
..........函数内部代码开始执行


这也解释了为什么声明提前,在函数开始执行之前,需要声明所有函数作用域内变量将其储存在一个“变量对象”里

关于变量的声明这里有几点需要注意:

ps1.函数的形参在声明时已经指定了值

function show(num){

return(num);
}

show(5);//5
ps2.第二步函数变量的声明,会覆盖以前声明过得同名变量

function add(num) {
 var num = 10;
  alert(num);   
}
add(1);//出现10 而不是1

ps.3第三步中普通变量的声明不会覆盖以前声明过得同名变量

var num = 10;

function add(num) {
 
  alert(num);   
}
add(1)//1

会覆盖和不会覆盖的原因解释:作用域链!

3 作用域链的组成

在js的函数是允许嵌套的比如

var a = 1;


function A(d){
   var b = 2;
   function B(){
   var c=3;
   return a+b+c+d;//这里可以访问abcd
}
return B()+a+b+d;//这里可以访问abd
}
//这里只能访问a
alert(A(4));

当执行函数B的时候,会从函数内部开始搜寻变量abcd,只找到c然后向包围他的函数A搜寻再向全局环境搜寻直到找到标示符
如果执行的是函数A则作用域是A内的变量和全局变量这是A的执行环境,将A的执行环境里面的变量保存在一个变量对象里面,作用域链的头从A的变量开始尾巴是全局变量,在这个过程中开始匹配标示符知道全部找到,如果都找不到,抛出错误,在这里也解释了为何会有同名变量的覆盖



二 闭包

关于闭包好像不是那么的容易理解,现在的我翻译几个外国网站上的例子来加深自己的理解:


闭包(与镶嵌函数有关一个函数内还定义了另个函数,其中的函数一般是第一类型函数)是一种支持第一函数的方法,可以这样表达,,当他被宣布为第一函数的时候,他被分配给一个变量,或者是
作为结果返回到上一级的函数,这个第一函数它能够访问他的作用域范围(作用域链向上延伸知道全局变量)内的变量。现在,有一个变量!它里面存储的是作为第一函数的那个函数
只要这个变量被执行,那么这个第一函数能访问到的其他变量就会一直存在,即便是他所在的函数已经执行完毕(通常来说,某个变量只存在他所在的函数被执行阶段
,执行完毕后被内存释放)内存不会释放。所以如果你调用闭包函数(存储第一函数的变量)那么有可能改变了上一级的变量,然后再执行闭包函数是访问到的上一级的
变量已经是改变后的变量值。

举个例子:
function sayHello2(name){
    var text='hello'+name;
    var say=function(){console.log(text);}
   return say;
}
var say2=sayHello2('bob');
say2();

也可以换种写法同样还是这个函数

var say2=undefined;
function sayHello2(name){
var text="hello"+name;
say=function(){console.log(text);}

}
sayHello2('Wei');
say();

闭包函数因为作用域内的变量无法释放会产生一些有意思的现象:

举个例子:
function A(){
var a=10;
var B=function(b){


return  console.log( b+a++);
}
return B;


}
var C=A();
C(10);//20
C(10);//21
C(10);//22

下面来看另个类似的函数:
var B=function(b){
var a=10;
return  console.log( b+a++);
}
B(10);//20
B(10);//20
B(10);//20

思考一下为什么会出现上面两种情况:理解的重点在于变量的存活时间,第一个a变量因为闭包函数的存在内存无法释放所以每次调用闭包函数a的值都会加1,而下次再调用闭包函数
访问的a的值是加1后的,下面的情况是:函数执行完毕后内存被释放销毁,再次调用函数时重新将变量a放入栈中,所以值是没有变化的
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值