从面试题分析变量作用域

         首先还是从题目入手吧,很经典,虽然这些题目在网上能找到很多解释方法,不过还是那句话,还是需要自己去理解,这样这个知识点才是你的。

————————————————————————————————————————————————————

if (!("a" in window)) {
    var a = 1;
}
alert(a);

答案是undefined


if (!("a" in window)) {
     a = 1;
}
alert(a);


答案是1

————————————————————————————————————————————————————

不知道你做对了吗?

由这道题我们可以引出一个知识点-----变量的作用域


还是跟以前一样,首先列出一些“预习”的知识点,供我们后面分析使用:

1.JS中是以函数作为作用域,而我们平时所用的if 或者 for这样的代码块中,是没有作用域的,所以里面的变量对外是可见的。

2.全局变量是指定义在所有函数之外的变量,与之相对应的就是局部变量,它是指定义在某个函数中的变量。

3.函数内的代码可以访问全局变量,反之若函数外的代码去访问函数内的变量则不行。

function a(){
     var b=1;
     alert("函数内:b="+b);
}
a();
alert("函数外:b="+b);

这个例子就可以佐证第三条知识点,只会打印出函数内的值,函数外的则为未定义)


4.如果我们声明一个变量时没有使用var语句,该变量就会被默认为全局变量。

function a(){
     b=1;
     alert("函数内:b="+b);
}
a();
alert("函数外:b="+b);

(这个例子正好佐证了第四条知识点)


5.当JS执行过程中进入新的函数时,这个函数内被声明的所有变量都会被移动(提升)到函数最开始的地方,而且只有函数体内声明的变量在该函数执行开始时就存在,但是与之相关的赋值操作并不会被提升,还是在原来的位置上。

function a(){
    alert("函数内:b="+b);
    var b=1;
}
a();

会alert出undefined

function a(){
    alert("函数内:b="+b);
    b=1;
}
a();

这个程序就会报错,b未定义

(第1个例子正好佐证了第五条知识点,为什么要写第2个例子呢?就是因为在上一条中我们说未被var的变量我们定义为一个全局变量,那既然是个全局变量的话,我们第三点又说函数内可以访问全局变量,那为什么还是错误的呢?原因在于在该函数被调用之前,这个变量是不存在的,该变量会在它首次被调用的时候创建被赋予全局作用域,而刚才我们在第五点中又说到了只有声明的变量才会被提前,赋值并不会,所以这里会报错)

——————————————————————————————————————————————————


哇,不知不觉都总结出了5个知识点,感觉认真总结完之后,那个题目是不是就迎刃而解了呢?

还是回到我们的题目上来吧


第一个题目中 ("a" in window)的意思是变量a是否是window的属性,即a存不存在,如果是不存在我们就把它赋值1后再打印,存在则直接打印出来。

if (!("a" in window)) {
    var a = 1;
}

这里的答案是undefined,是存在的,但是未定义,意思就是没进入这个if里面的语句,利用我们刚才说的知识点,变量声明的时候会被提前,var a=1其实可以被分解为var a;  a=1; 而且if是不存在作用域的,所以我们可以把这个代码改写一下。

var a;

if(!("a") in window)) {

    a=1'

}

这样是不是就好理解了呢?


然后再来看我们的第二个题目

if (!("a" in window)) {
     a = 1;
}

这里答案是1,不用我多说了吧,跟我第5个知识点举的第二个例子差不多,只有当我们去执行a=1这句的时候才会把a给赋予全局变量,所以在此之前a都是不存在的,它就会引入if里面的语句,被赋值于1,并定义为全局变量。


—————————————————————————————————————————————————


当然这里还有一个额外的知识点需要提一下,函数声明和变量声明如果在一起的时候,函数声明提前。

var a=1;
function a() { }
alert(a);

因为上面的例子可以改写成:

function a() { }
var a;
a=1;
alert(a);


但是如果你没赋值的话

var a;
function a() { }
alert(a);

就会打印出函数a

为什么呢?因为a没赋值,所以没覆盖a以前的值(函数),不知道我这样理解对不对呢?


2017/8/9更新:

如何理解作用域和作用域链?

我个人目前的一个理解水平:

作用域我可以理解为数据的有效范围;一个函数就是一个单独的作用域,如果我数据定义在函数里面,那么我这个数据就是局部数据(变量),如果不是则为全局数据(变量);JS中有一个全局作用域和局部作用域的概念,我个人认为是以数据是否定义在函数中区分的。

 

我们知道函数里面能够使用全局变量,但是函数外面不能够使用局部变量;也就是我们的局部作用域中的数据不能被外部所使用,但是全局作用域中的数据可以被内部所使用;

 

当我们在函数中去使用某一个变量的时候,发现这个数据不在函数里面,那么就会去它的上一层作用域中去寻找(因为函数可以是进行嵌套的,所以没说全局作用域),发现有这个数据,那么我们就拿来使用;这种结果我个人认为就是作用域链的结果;不过这种链是单向的,毕竟我们也可以想象得到,变量的命名是很容易重复的,如果我们可以反向去搜索,那到底哪一个变量才是我们需要的呢?会造成混乱。

 

如果再进一步去思考的话就会涉及到“闭包”这个概念:

我们知道当一个函数执行完毕后会自动销毁,那么也就意味着它的作用域也会被销毁,里面的数据也会被销毁;那我们刚才说我们为什么可以在函数中使用到全局变量呢?作用域链是一方面,我们能够找到这个变量,还有就是这个全局作用域一直存在(因为你页面一直打开的话,js进程就会一直在运行,这个作用域就会一直存在),所以也使得我们可以能够使用它;如果我们是函数嵌套呢?也就是说变量的作用域不是全局,而是在它上一层的局部作用域里面呢?

就比如下面这个函数:

function a(){
  var name='lzj';
  return function b(){
   console.log(name);
  }
}


我们调用发现a( )( )  仍然可以访问到name这个变量,说明a( )执行完后name这个变量并没有随之销毁,其实是因为b这个函数中用到了这个变量,导致其一直存在我们的内存中;

这个现象就是我们平时中遇到的“闭包”吧,我们可以利用其保存我们想要的私有变量,不被污染,因为如果我们不这样做,就得实现一个全局变量去保存,这样很容易造成命名空间污染;


2017/8/18更新:

 var z=20;
 
 (function(){
    var z=10;
    function foo(){
       console.log(z);
     }
   foo();
 })( )   //10

 var z=20;
 
 function foo(){
    console.log(z);
 }
 (function(){
   var z=10;
   foo(); //20
 })()


上面两个例子的结果完全不一样,这里有一点非常重要,就是函数的作用域在其定义的时候,并不是在其使用的时候~

由于第二个例子的作用域在其定义的时候就在外面,并不是在匿名函数里面,所以它的作用域链式指全局变量的,并不是这个匿名函数,所以这里并没有形成我们的“闭包”!

这里非常重要!因为今天面试我就答错了~!!!!!!!!!!!!哎 悲剧


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值