javascript中的作用域和预解析

作用域以及预解析

        在javascript中作用域是非常重要的,本文章将会说明作用域以及我们在工作,以及面试中的一些面试题,如果有不足的地方希望大家可以评论指出来,自己一定会及时的改正错误,避免大家走入一些误区。

        谈及作用域先就必须要说明预解析和词法作用域。

        下面我们先说明一下:

        预解析

  1. 代码在正常执行操作之前会对文档进行一次解析,这个操作就是将声明提升,

  2. 声明包括全局范围内  1.带有var的变量, 2.函数  

  3. 文档预解析后会把文档中在全局函数中的内容储存起来,将全局中带有var的变量(var和变量名,注意:变量体不会随着提升,加载var只是告诉我们在文档中有一个全局变量是fn,并不会有其他的作用)提升到最前面

  4. 预解析后会正常的读取代码(由上至下由左到右)

    下面举例说明一下预解析:

var fn=456;
function fn(){
}

        在上述的代码中我们预解析后会变成: 

var fn;
function fn(){
}
fn=456;

        所以不管function是在前面还是后面我们打印fn出来的结果都是456。

        词法作用域

  1. 词法作用域在书写代码的时候就已经决定了,与运行无关

  2. 可以分割词法作用域的只是函数,别的不可以分割

        下面举例说明一下词法作用域     

  if(true){
      function fn(){
          alert("true");
      }
  }else{
      function fn(){
          alert("false");
      }
  }
fn();

            语句在预解析后,会得到以下结果

function fn(){
    alert("true");
    }
function fn(){
    alert("false");
    }
if(true){
    }
else{
    }
fn();

        再逐行进行解析;所以上面的函数会被下面的函数覆盖(函数内部的内容会被保存);

       在函数执行时会进入判断语句为true的语句中,但是这个时候fn这个函数在预解析后已经变成了false,所以这个时候打印出来的应该是false。

        注意:

  1. 全局中的函数在预解析之后内容会被保存,在执行时不会被二次解析,会被直接拿来用

  2. 上述例子我们写代码中不可能出现,因为函数不能被语句进行包裹,上面只是为了给大家进行演示而做的例子

    正式进入主题介绍作用域

  • 不被函数包裹的带有var的变量他们的存在于作用域链的零级链
  • 被函数包裹的带有var变量他们是存在于作用域中的一级链
  • 构造函数原型属性(prototype)等是存在于作用域中的二级链中 ,
  • 以此类推,函数一直包裹那么,作用域链也会一直递增下去
  • 在此中要特别注意的是隐式全局变量,隐式全局变量如果在函数内部只有在函数在执行时才会被调用

             下面通过一个复杂的面试题来为大家讲解作用域链:

       

function Foo(){
   getName = function(){
      console.log(1);
   };
   return this;
}
Foo.getName = function(){ console.log(2); };
Foo.prototype.getName = function(){ console.log(3); };
var getName = function(){ console.log(4); };
function getName(){ console.log(5); }

Foo.getName();             
getName();                 
Foo().getName();           
getName();                 
new Foo.getName();         
new Foo().getName();       
new new Foo().getName();   
          第一步当文档加载的时候会进行预解析,将声明和带有var的全局变量名提升,

        解析完之后零级链上回出现两个函数foo和getname

              第二步会由上往下加载给foo函数添加一个名为getname的函数给foo的原型中添加一个getname;在刚才的预解析中,零级链上有一个getname,加载之后出现另一个getname会把之前加载的getname覆盖掉

        第三步进入函数的执行,

       函数在执行时如果在原来的作用域链中有这个函数时,不用从新二次加载,如果出现同名会直接拿来用,

  1. foo.getname在刚才已经定义了,所以第一个直接得到的是2;
  2. getname在刚才的作用域零级链中已经被从新定义覆盖,所以第二个直接得到的是4;
  3. 会先执行foo函数,但是在foo的函数中存在了一个隐式的全局变量(getmame函数);所以当foo执行时会将这个全局变量函数释放出来,getname覆盖原来零级链上的getname;最后执行return this,这个时候this指向的是window,所以会这句话翻译过来也就是window.getname;全局变量中的getname得到的是刚才foo释放出来的隐式全局变量。所以第三个得到的是1;
  4. getname和刚才第三个翻译过来的结果是一样的,只不过是将window省略; 所以第四个的结果同样是1;
  5. new foo.getname,    foo不是一个函数,所以不能被new,会先执行后面的Foo.getname;foo.getname和第一个一样;第五个所以得到的是2;最后执行new随后foo.getname变成了一个空对象
  6. new foo().getname()   foo是一个函数;new之后就是指被构造函数实例化的对象,对象.getname,但是现在foo这个函数中没有this.getname;所以直接沿着Foo的原型链忘上找;得到了存在于原型链中foo.prototype.getname所以第五个得到的数值是3;
  7. 第七个和第六个一样,会执行后面的new Foo().getname;得到一个结果是3;随后把这个函数new了一下,变成了一个空的对象

        所以得到的结果是2,4,1,1,2,3,3     

        注意:

  1. 本文章文字比较多,看的过程可能比较有的不太好理解,
  2. 如果有疑问可以留下评论,只要在自己会得范畴之内一定会帮忙解答
  3. 学东西不要觉得烦,这个案例弄通之后作用域至少会百分之80了
  4. 希望大家在看完之后如果真的学到了对象请不要吝啬你的点赞,收藏,谢谢
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值