深入彻底理解原生js的作用域、作用域链(以及浏览器是怎样解析js代码的)

news/2024/7/19 16:14:50 标签: js, 作用域和作用域链, js解析器

在学习闭包的过程中,我发现,要想真正的理解闭包,光知道什么闭包是不行的,我们还需要知道什么是作用域以及作用域链、函数声明和函数表达式,当然还牵扯到的知识有,垃圾回收机制和内存泄漏等,所以,今天我就多用一点时间把这些知识点都总结一下,为了防止内容太多造成大家眼睛疲劳,我会把这些知识点分开写在不同的页面,如果觉得不错的话,再看后续的就可以。

首先说函数作用域之前,我们先唠唠浏览器是怎么解析js代码的

浏览器在读取HTML文件的时候,只有当遇到<script>标签的时候,才会唤醒所谓的“JavaScript解析器”开始工作。

JavaScript解析器工作步骤

1、“找一些东西”:var、function、参数(也被称之为预解析)。

如果遇到重名分以下两种情况:

  • 遇到变量和函数重名,只留下函数
  • 遇到函数重名了,根据代码的上下文顺序,留下最后一个

2、逐行解读代码(表达式可以修改预解析的值)

js解析器在执行第一步预解析的时候,会从代码的开始搜索知道结尾,找的时候,只会找var、function和参数等内容。一般把第一步称之为“JavaScript的预解析”,而且,当找到这些内容时,所有的变量,在正式运行代码之前,都提前赋了一个值:变量都赋值为undefined,所有函数在正式运行代码之前,都是整个函数块。

强调一点:JavaScript解析器不只是这两个步骤,只是这里我们只介绍了这两步骤,方便我们理解作用域。

作用域:所谓作用域——作用(可以进行读、写)的域(空间、范围、区域):也即可访问对象、变量、函数的集合

其实我们也可以把作用域分为:全局作用域(网页中所有脚本和函数均可使用)和局部作用域(函数内部)

注意:只要是域,就会进行预解析和逐行解读代码这两步

全局变量拥有全局作用域(函数外声明的变量以及没有用var关键字定义的变量都是全局变量)

局部变量(函数内部声明的变量,形参都是局部变量)只能在函数内部访问,因为局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量

全局变量在页面关闭后销毁,局部变量在函数执行完毕销毁。

OK,铺垫完了,现在我们来看几个变态的面试题

alert(a);//function a(){alert(4);}
var a = 1;
alert(a);//1
function a(){alert(2);}
alert(a);//1
var a = 3;
alert(a);//3
function a(){alert(4);}
alert(a);//3
//a();如果我们再添加上这句代码,程序会报错

看完上面的代码,你可知道是为什么吗?还是像我刚看到那样一脸懵逼,这都是what?

哈哈,下来就我就来分析一下呗:

首先我们说过,在执行这段代码之前会先进行预解析,所以当变量和函数重名留下函数,当函数和函数重名留下最后一个函数,所以预解析的结果就是存入了一个代码块:function  a(){alert(4);},所以在逐行解读代码的时候第一句弹出的结果也就没什么疑问了,又因为在代码解读的时候,表达式可以修改预解析的值,所以,执行到最后一行的时候a=3,再然后我们说如果要是再后面加一个a(),会报错,因为此时就相当于3();你说能不报错嘛?

然后,继续看代码

var a = 1;
function fn1(){
  alert(a);//undefined
  var a = 2;
}
fn1();
alert(a);//1

再看

var a = 1;
function fn1(){
  alert(a);//1
   a = 2;
}
fn1();
alert(a);//2

再看

var a = 1;
function fn1(a){
  alert(a);//undefined
  a = 2;
}
fn1();
alert(a);//1

再看

var a = 1;
function fn1(a){
  alert(a);//1
  a = 2;
}
fn1(a);
alert(a);//1

看完上面四段代码,有木有想跳楼的冲动,ok,冷静冷静,现在且听我娓娓道来

首先我们来说一下第一段代码:函数调用时,弹出的是undefined,这个没啥疑问,注意一点就是函数内也要进行预解析,然后函数在预解析的时候会给变量赋值为未定义,alert弹出1,因为局部变量不能改变全局变量的值

然后我们说第二段:过程和第一段差不多,需要注意的是,函数内部的a没有用var关键字定义,所以它就是全局变量,函数调用的时候弹出1(这里其实涉及到了作用域链的问题,有里向外,子级作用域返回父级作用域找到了全局变量a=1),那么当执行到a=2的时候,全局变量a自然就是2了

第三段代码解读:首先我们需要知道的是,形参实际上就是一个局部变量,相当于var a,然后知道了这个,接下来的过程就和第一段一样了

第四段代码:首先我们看到第四段代码和第三段代码唯一的不同之处,就是第四段代码多了一个实参,然后函数内部预解析之后,在逐行解读代码的时候,因为从function fn1(a){}这一行开始,实参把a=1传给了局部变量a,所以函数调用时弹出的a为1。

好了,看完解释有没有豁然开朗,嗯哼,接下来我们再看一段代码,不要怕,这次的很简单,真的很简单

<script>
  alert(a);
</script>
<script>
   var a=1;
</script>//这段代码会报错
<script>
   var a=1; 
</script>
<script>
   alert(a);//弹出1
</script>

这个想必大家都知道答案,但我还是唠叨一下,其实这是因为两个<script></script>其实是两个域,而域是从上而下,一个一个执行的,只有执行完了上面的才会开始执行下面的,这也就是为什么第一个报错,而第二段代码可以正常弹出1了。

下面我们来说一下什么是作用域链:

当代码在一个环境中执行时,都会创建一个作用域链。 作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。整个作用域链的本质是一个指向变量对象的指针列表。作用域链的最前端,始终是当前正在执行的代码所在环境的变量对象。 

  如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,就是函数内部的arguments对象。作用域链中的下一个变量对象来自该函数的包含环境,而再下一个变量对象来自再下一个包含环境。这样,一直延续到全局执行环境,全局执行环境的变量对象始终是作用域链中的最后一个对象。

    不同的层级作用域上对象的分布

  • 在javascript的最顶层(也就是不包含任何函数定义内的代码),作用域链由一个全局对象组成。
  • 在不包含嵌套的函数体内,作用域链上有两个对象,第一个是定义函数参数和局部变量的对象,第二个是全局对象。
  • 在一个嵌套的函数体内,作用域链上至少有三个对象。当调用这个函数时,它创建一个新的对象来存储它的局部变量,它实际上保存在同一个作用域链。
js">   对于嵌套函数来讲,事情更有趣,每次调用外部函数时,内部函数又会重新定义一遍。因为每次调用外部函数的时候,作用域链都是不同的。内部函数每次定义的时候都有微妙的差别——在每次调用外部函数时,内部函数的代码都是不相同的,而且关联这段代码的作用域链也不相同。

终于把关于作用域的知识想唠的唠完了,接下来如果有兴趣看函数声明和函数表达式的请点击js函数声明和函数表达式


http://www.niftyadmin.cn/n/952187.html

相关文章

最近比较烦,哪位大哥指点一二!

最近比较烦 &#xff0c;现在感觉自己不知道学点什么&#xff01;现在感觉就会点数据库操作&#xff0c;很菜&#xff01;.net的具体哪个方向比较好啊&#xff1f;发展前途光明啊&#xff1f;哪位大哥指点一二 转载于:https://www.cnblogs.com/zhangzheny/archive/2007/03/07/…

状态机设计的一般步骤_PLC编程状态机如何实现

在PLC程序的编写过程中&#xff0c;可以使用状态机的控制思路&#xff0c;将一些复杂的控制过程使用状态机的方法处理。这里简单给大家介绍一下什么是状态机?如下图所示&#xff0c;为一个状态机的状态图。从以上图中可以看到&#xff0c;其是将动作执行的各个状态进行了一个划…

深入理解函数声明和函数表达式、深入理解立即执行函数(自执行函数)

在学习函数声明和函数表达式之前如果你对作用域和作用域链掌握的不是特别的好&#xff0c;建议您先看完js深入理解函数作用域和作用域链&#xff0c;再进行接下来的学习 函数声明&#xff1a;function 函数名(){} 函数表达式&#xff1a;function 函数名(){},函数名&#xff0…

高兴!

积分快到1W了&#xff01;&#xff01;转载于:https://www.cnblogs.com/zhangzheny/archive/2007/03/14/674789.html

弱引用什么时候被回收_Java进阶:搞定 JVM 垃圾回收就是这么简单

原创&#xff1a; Snailclimb来源&#xff1a; JavaGuide写在前面本节常见面试题&#xff1a;问题答案在文中都有提到如何判断对象是否死亡(两种方法)。简单的介绍一下强引用、软引用、弱引用、虚引用(虚引用与软引用和弱引用的区别、使用软引用能带来的好处)。如何判断一个常量…

浅谈js中闭包、闭包中的this指向、垃圾回收机制、内存泄漏等问题

友情提示&#xff0c;如果你对作用域、作用域链、函数表达式这些知识掌握的不是很好的话&#xff0c;建议您可以先看看深入理解js中的作用域以及作用域 1、什么是闭包&#xff1f; 关于什么是闭包&#xff0c;官方的解释是&#xff1a;闭包是一个拥有许多变量和绑定了这些变量…

飞翔,还是飞行!

来北京已经基本上一年多了&#xff0c;老是想写些感受&#xff0c;可是老是踏不下心来&#xff0c;可能被北京的繁华和人群给淹没了&#xff01;有时真的觉得自己好差&#xff0c;不能呼风唤雨&#xff0c;不能买这买那&#xff0c;真的是一种漂的感觉&#xff01;这里不是我的…

原生js实现苹果菜单效果

主要用到的知识点 Math.pow(a,b);//a的b次方 Math.sqrt(a);//a的开方 其他的并不难&#xff0c;直接上代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>苹果菜单练习</title><style>#…