四句话总结JavaScript作用域

上一篇文章中简单介绍了一下JS作用域,本篇将作进一步探究和总结。

前言:JavaScript的作用域一直以来都是前端开发中比较难以理解的知识点,JavaScript6中新引入了 let 关键字,用于指定变量属于块级作用域,本次先忽略这个点。

第一句话:JavaScript以函数作为作用域(忽略let)

  很多语言如c#,java都是以代码块作为作用域即大括号也是一个作用域,JavaScript却是以函数作为作用域,如果你对python比较了解,理解起来应该很easy。

在c#中下面的代码将直接报错:

 1 public void Func(){
 2     if(1==1){
 3         string name = 'Java';
 4                             
 5     }
 6     console.writeline(name);
 7                         
 8 }
 9 Func()
10  // 报错    

在JavaScript语言中无块级作用域:

1 function Main(){
2     if(1==1){
3         var name = 'seven';
4     }
5     console.log(name);
6 }
7 // 输出: seven

我们先来回忆一下python的作用域(如果不会python可以跳过,不影响后面阅读):

 1 # 情况一:
 2 def func():
 3     if 1 == 1:
 4         name = 'alex'
 5     print(name)
 6 func()
 7 # 成功
 8 
 9 # 情况二:
10 def func():
11     if 1 == 1:
12         name = 'alex'
13     print(name)
14     
15 func()
16 print(name)
17 # // 报错

再来看看JavaScript采用函数作用域:

1 function Main(){
2     var innerValue = 'seven';
3 }
4  
5 Main();
6  
7 console.log(innerValue);
8  
9 // 报错:Uncaught ReferenceError: innerValue is not defined

  在JavaScript中每个函数作为一个作用域,在外部无法访问内部作用域中的变量。console.log(innerValue)访问函数Main()中的变量肯定是不行的。

第二句话:JavaScript函数的作用域在函数未被调用之前,已经创建

  在JavaScript中如果不创建(声明)变量,直接去使用,则报错:

1 console.log(x);
2 VM199:1 Uncaught ReferenceError: x is not defined(…)

  JavaScript中如果创建值而不赋值,则该值为 undefined,如:

1 var num;
2 console.log(num);
3 
4 //undefined

     了解了这个看看下面这个函数:

1 function func(){
2     if(1==1){
3         var name = 'alex';
4     }
5     console.log(name);
6 }

  在浏览器中直接输入这个函数会得到undefined,而不是报错,就说明变量name在函数调用之前就创建(声明)了,但没有被赋值。

第三句话:函数的作用域存在作用域链,并且也是在被调用之前创建

 1  1 <script>
 2  2     x = "alex";
 3  3     function func() {
 4  4         var x = "eric";
 5  5         function inner() {
 6  6             var x = "tony";
 7  7             console.log(x);
 8  8         }
 9  9         inner();
10 10     }
11 11     func();
12 12 </script>

输出结果是tony。

如上述代码则出现三个作用域组成的作用域链,如果出现作用域链后,那么寻找变量时候就会出现顺序,对于上述实例:

当执行console.log(xo)时,其寻找顺序为根据作用域链从内到外的优先级寻找,如果内层没有就逐步向上找,直到没找到抛出异常。

 练习题1:

 1 x = "alex";
 2 function func() {
 3     var x = "eric";
 4     function inner() {
 5         console.log(x);
 6     }
 7     return inner;
 8 }
 9 var res = func();
10 res();

结果:eric

上述代码,在函数被调用之前作用域链已经存在:

  • 全局作用域 -> func函数作用域 -> inner函数作用域

当执行【ret();】时,由于其代指的是inner函数,此函数的作用域链在执行之前已经被定义为:全局作用域 -> Func函数作用域 -> inner函数作用域,所以,在执行【ret();】时,会根据已经存在的作用域链去寻找变量。

练习题2:

 1 x = "alex";
 2 function func() {
 3     var x = "eric";
 4     function inner() {
 5         console.log(x);
 6     }
 7     var x = 'tony';
 8     return inner;
 9 }
10 var res = func();
11 res();

结果:tony

第四句话: 函数内局部变量 声明提前

1 function func(){
2     console.log(xo);
3     var xo = 'alex';
4 } 
5       
6 func();
7 // undefined

上述代码,不报错而是输出 undefined,其原因是:JavaScript的函数在被执行之前,会将其中的变量全部声明,而不赋值。所以,相当于上述实例中,函数在“预编译”时,已经执行了var xo;所以上述代码中输出的是undefined。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏全沾开发(huā)

数组的遍历你都会用了,那Promise版本的呢

31440
来自专栏IMWeb前端团队

JSON Schema 参考书

本文作者:IMWeb 孙世吉 原文出处:IMWeb社区 未经同意,禁止转载 1 简介 JSON 模式(JSON Schema)是一种基于 JSON 格...

463100
来自专栏算法与数据结构

栈与递归 实现 十进制转二进制

6-4 十进制转换二进制(15 分) 本题要求实现一个函数,将正整数n转换为二进制后输出。 函数接口定义: void dectobin( int n ); 函数...

35650
来自专栏Hongten

java中静态变量和实例变量到底有什么区别_看了就知道啦

==================================================

9830
来自专栏LhWorld哥陪你聊算法

python 使用小结

使用Python 已经一段时间了 ,现将python 中可能用到的技巧和一些知识点分享如下。

12110
来自专栏编程

Python3:复杂数据结构的排序

排序是非常常见的一个场景,相比于Python2,Python3中的排序有不少优化,今天谈一谈Python3中常见排序场景~~更多细节可参考Ref中的Python...

378100
来自专栏余林丰

5.比较排序之归并排序(非递归)

  在上一节中讲解了归并排序的递归版《4.比较排序之归并排序(递归)》,通常来讲,递归版的归并排序要更为常用,本节简单介绍下非递归版的归并排序。思路和递归版相...

31790
来自专栏个人随笔

反射反射 程序员的快乐

java反射操作其实就是主要围绕Class,Field,Methon,Constructor等几个类来操作其中的方法

44940
来自专栏C/C++基础

C++11继承构造函数

子类为完成基类初始化,在C++11之前,需要在初始化列表调用基类的构造函数,从而完成构造函数的传递。如果基类拥有多个构造函数,那么子类也需要实现多个与基类构造函...

19020
来自专栏烂笔头

Python标准库笔记(2) — re模块

目录[-] re模块提供了一系列功能强大的正则表达式(regular expression)工具,它们允许你快速检查给定字符串是否与给定的模式匹配(matc...

38740

扫码关注云+社区

领取腾讯云代金券