Java中有一个叫Scope的特性。虽然对于很多新手开发者来说,范围的概念并不容易理解,但是我会尽量用最简单的方式来解释范围和范围链。希望大家有所收获!
范围)1。什么是范围
范围是运行时代码的某些部分中变量、函数和对象的可访问性。换句话说,范围决定了代码块中变量和其他资源的可见性。可能这两句不太好理解。我们先来看一个例子:
1功能输出2() {
2 var invisible = "内部变量2 ";
3}
4 outfun 2();//你得先执行这个函数,不然你不知道里面是什么。
5 console . log(Average);//未捕获的引用错误:未定义不变
从上面的例子中,我们可以理解范围的概念。变量InTerface没有在全局范围内声明,因此在全局范围内取值将会报告错误。我们可以这样理解:范围是一个独立的领地,这样变量就不会泄露或暴露。也就是说,作用域最大的用处就是隔离变量,同名的变量在不同的作用域下不会冲突。
在ES6之前,Java没有块级作用域,只有全局作用域和函数作用域。ES6的到来为我们提供了“块级范围”,这可以通过添加命令let和const来实现。
2.全局范围和功能范围
可以在代码中任何地方访问的对象都具有全局范围。一般来说,以下情况具有全局范围:
最外层函数 和在最外层函数外面定义的变量拥有全局作用域1 varoutVariable = "我是最外层变量"; //最外层变量2 functionout un(){//最外层函数
3 var invisible = "内部变量";
4 functioninnerrun(){//内层函数
5 console . log(Average);
6}
7innerFun();
8}
9 console . log(OutVariable);//我是最外层的变量
10 outfun();//内层变量
11 con sole . log(InVARIABLE);//不变量未定义
12 innerfun();//未定义innerFun
所有末定义直接赋值的变量自动声明为拥有全局作用域1 functionoutFun2() {2variable = "没有定义直接赋值的变量";
3varinVariable2 = "内部变量2 ";
4}
5 outfun 2();//你得先执行这个函数,不然你不知道里面是什么。
6console.log(变量);//没有定义直接赋值变量
7 console . log(Inviable2);//不变量2未定义
所有window对象的属性拥有全局作用域通常,窗口对象的内置属性具有全局范围,如window.name、window.location、window.top等。
全局范围有一个缺点:如果我们写很多行JS代码,变量定义没有被函数包含,那么它们都在全局范围内。这样会污染全局命名空,容易造成命名冲突。
1//在张三写的代码里,
2vardata= {a: 100}
三
4//在李四写的代码里,
5vardata= {x: true}
这就是为什么jQuery、Zepto等库的所有源代码都放在(function () {...}) ().因为里面的所有变量都不会泄露暴露,不会污染外部,也不会影响其他库或者JS脚本。这是功能范围的体现。
函数作用域是指在函数内部声明的变量。与全局范围相反,局部范围通常只能在固定的代码片段中访问,最常见的是在函数内部。
1 functiondosameting(){
2varblogName = "在波浪中划船";
3functioninnerSay(){
4 alert(blogName);
5}
6innerSay();
7}
8 alert(blogName);//脚本错误
9innerSay();//脚本错误
范围是分层的,内部范围可以访问外部范围的变量,但不能反过来。举个例子吧。使用气泡可能更容易理解范围:
最终输出结果是2、4和12
泡泡1是全局作用域,有标识符foo;泡泡2是作用域foo,有标识符a,bar,b;泡泡3是作用域bar,仅有标识符c。值得注意的是,block语句(大括号“{}”之间的语句),如if和switch条件语句或for和while循环语句,不会像函数一样创建新的作用域。块语句中定义的变量将保留在其现有范围内。
1if( true) {
2/“if”条件语句块不会创建新的作用域
3varname = ' Hammad//名称仍在全局范围内
4}
5console.log(名称);//日志' Hammad '
JS初学者往往需要花一些时间来习惯变量提升,但是如果不理解这种独特的行为,可能会导致bug。正因为如此,ES6引入了块级范围,使得变量的生命周期更加可控。
3.块级范围
块级范围可以通过添加命令let和const来声明,声明的变量不能在指定块的范围之外访问。块级范围是在以下情况下创建的:
在一个函数内部在一个代码块(由一对花括号包裹)内部let声明的语法与var的语法一致。基本上可以用let代替var来声明变量,但是会把变量的范围限制到当前的代码块。块级范围具有以下特征:
声明变量不会提升到代码块顶部let/const声明不会被提升到当前代码块的顶部,因此您需要手动将let/const声明放在顶部,以便变量可以在整个代码块中使用。
1函数getValue(条件){
2if(条件){
3letvalue = " blue
4returnvalue
5}其他{
6//此处没有值
7returnnull
8}
9//此处没有值
10}
禁止重复声明如果在一个代码块中定义了一个标识符,那么在这个代码块中使用相同的标识符来声明一个字母将导致一个错误被抛出。例如:
1varcount = 30
2letcount = 40//未捕获语法错误:标识符“计数”已经声明
在这个例子中,count变量声明了两次:一次使用var,一次使用let。因为let不能在同一范围内重复声明现有的标识符,所以这里的let声明将引发错误。但是,如果在嵌套范围内使用let声明了同名的新变量,则不会引发错误。
1varcount = 30
2//不会抛出任何错误
3if(条件){
4letcount = 40
5//其他代码
6}
循环中的绑定块作用域的妙用开发人员可能最希望实现for循环的块级范围,因为声明的计数器变量可能仅限于循环。例如,在JS中经常会看到以下代码:
1<。按钮>测试1
2<。按钮>测试2
3<。按钮>测试3
4<。type="text/java ">。
5 varbtns = document . getelementsbytagname(' button ')
6 for(vari = 0;i <。btns.lengthi++) {
7 BTN[I]。onclick = function() {
8console.log('第一个'+(i+1)+'第一个')
9}
10}
11<。/>。
我们需要实现这样一个需求:点击一个按钮,提示“点击第n个按钮”。我们不首先考虑事件代理。万万没想到,点击任何一个按钮都会在后台弹出“第四”,因为我是全局变量。单击事件时,I的值为3。怎么修改,最简单的就是用字母来声明我
1 for(leti = 0;i <。btns.lengthi++) {
2 BTN[I]。onclick = function() {
3 console . log(' the first '+(I+1)+' the first ')
4}
5}
范围链1。什么是自由变量
首先要知道什么是自由变量。在下面的代码中,console.log(a)想要获取一个变量,但是在当前作用域(compare b)中没有定义a。当前范围内没有定义的变量,成为自由变量。如何获取自由变量的值——寻找父作用域(注意:这个语句不严格,下面会详细说明)。
1vara = 100
2functionfn() {
3varb = 200
4console.log(a) // A这里是一个自由变量
5console.log(b)
6}
7fn()
2.什么是范围链
没有父母怎么办?一层一层往上看,直到找到全局范围,然后放弃。这种逐层关系就是范围链。
1vara = 100
2功能F1() {
3varb = 200
4功能F2() {
5varc = 300
6console.log(a) //自由变量,沿着作用域链寻找父作用域
7console.log(b) //自由变量,沿着作用域链寻找父作用域
8console.log(c) //此范围的变量
9}
10F2()
11}
12F1()
3.关于自由变量的值
至于自由变量的值,提到要从父范围取。其实有时候这样的解读会导致歧义。
1varx = 10
2functionfn() {
3console.log(x)
4}
5功能显示(f) {
6varx = 20
7( function() {
8f() //10,不是20
9})()
10}
11show(fn)
在fn函数中,取自由变量x的值时,应该取哪个范围?-从创建fn函数的范围中获取,无论fn函数将在哪里被调用。
所以,不要用它来形容。相比之下,用这句话来描述更合适:这个函数要被创建的域。范围,这里强调的是“创造”,而不是“调用”,切记切记——事实上,这就是所谓的“静态范围”
1vara = 10
2functionfn() {
3varb = 20
4functionbar() {
5console.log(a + b) //30
6}
7returnbar
8}
9varx = fn(),
10b = 200
11x() //bar()
Fn()返回bar函数,赋给x. Execute x(),即执行bar函数代码。取b的值,直接在fn范围内取出。取A的值的时候,我尝试在fn范围内取,但是拿不到。我不得不转向创建fn的范围并找到它,所以最终结果是30
范围和执行上下文
许多开发人员经常混淆范围和执行上下文的概念,并将它们误认为是同一个概念,但事实并非如此。
我们知道Java是一种解释性语言,Java的执行分为解释和执行两个阶段,做不同的事情:
解读阶段:
词法分析语法分析作用域规则确定执行阶段:
创建执行上下文执行函数代码垃圾回收作用域规则会在Java解释阶段确定,所以作用域是在定义函数的时候确定的,而不是在调用函数的时候,而是在函数执行之前创建执行上下文。执行上下文最明显的一点是,这个的方向是在执行过程中确定的。作用域访问的变量由编写的代码的结构决定。
范围和执行上下文最大的区别在于执行上下文是在运行时确定的,随时都可能发生变化;范围在定义时确定,不会改变。
一个范围可能包含几个上下文。有可能从来没有上下文(函数从来没有被调用过);调用函数后,上下文环境可能已被破坏。同时可能有一个或多个(闭包)。在相同的作用域下,不同的调用会产生不同的执行上下文,进而产生不同的变量值。
参考文章和书籍
深入理解java原型和闭包系列Web 前端面试指南与高频考题解析深入理解JS中声明提升、作用域(链)和`this`关键字Java 开发进阶:理解 Java 作用域和作用域链Java 作用域和作用域链深入理解ES6[本文作者]
浪中泛舟:硕士,专注前端。个人微信官方账号:“前端工匠”,致力于打造一系列适合初、中级工程师快速吸收的高品质文章
1.《作用域 深入理解 JS 作用域和作用域链》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《作用域 深入理解 JS 作用域和作用域链》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/guoji/1059350.html