IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> JavaScript学习_基础_09_作用域与预解析 -> 正文阅读

[JavaScript知识库]JavaScript学习_基础_09_作用域与预解析

作用域


一、基本概念

作用域: 作用域说的是变量起作用的区域或者范围。

作用: 变量在各自的作用域当中起作用。

二、局部变量与全局变量、局部作用域与全局作用域

局部变量全局变量,没有任何关系,两者是相互独立的个体。唯一的区别在于:全局变量在哪都可以使用,而局部变量只能在自己局部作用域当中使用。

案例引入

var a = 10;
var b = 20;

function f1()
{
    a = 20;
    b = 10;
    console.log(a,b)//20,10
}

f1();

console.log(a,b);//20,10

我们知道,当我们在定义变量的时候,都会给前面加一个var,但是也有不加var的情况。而这种情况又根据环境不同分为以下两者情况:

  1. 如果是在全局:没有定义过的变量,不加var,那么必须给这个变量赋值;而且赋值相当于给这个变量加var,赋值完后它是一个地地道道的全局变量
  2. 如果是在局部(如函数内部):没有定义过的变量,而且所处函数也没有一个参数是它,此时定义这个变量不加var,首先要看函数外部全局是否定义过这个变量。如果全局定义过,那么这个变量就不是在初始化,而是在操作全局变量。如果没有定义过,相当于在全局var定义了这个变量。

所以,这就是最终结果a = 20 , b = 10的原因。

案例分析

先说结论:JS的作用域,跟调用位置无关,而是跟定义位置有关。

var num = 10;

function fun() 
{
    var num = 20;
    fun2();
}

function fun2() 
{
    console.log(num); //10
}

fun();
// fun与fun2的作用域并非嵌套关系,所谓作用域嵌套关系指的是在定义时确定的嵌套关系,并非调用的嵌套。根据结论,函数fun与函数fun2并不是在一个作用域内,而是都有着自己单独的作用域。

举例说明

  1. 例一
    var num = 0;
    
    function f1()
    {
        var num = 1;
        function f2()
        {
            var num = 2;
            function f3()
            {
                var num = 3;
                function f4()
                {
                    var num = 4;
                    console.log(num);
                }
                f4();
            }
            f3();
        }
        f2();
    }
    
    
    
    f1();
    // 结果为4
    

    函数f4内使用var定义了一个变量num,这个num作用域就是f4函数

  2. 例二
    var num = 0;
    
    function f1()
    {
        var num = 1;
        function f2()
        {
            var num = 2;
            function f3()
            {
                var num = 3;
                function f4()
                {
                    // var num = 4;  删掉这一行
                    console.log(num);
                }
                f4();
            }
            f3();
        }
        f2();
    }
    
    
    
    f1();
    // 结果为3
    

    删掉f4内的num变量,使用console.log进行输出时,发现输出的是函数f3内的同名num变量。

  3. 例三
    var num = 0;
    
    function f1()
    {
        function f2()
        {
            function f3()
            {
                function f4()
                {
                    console.log(num);
                }
                f4();
            }
            f3();
        }
        f2();
    }
    
    
    
    f1();
    // 结果为0
    

    删除函数f1f2f3f4内的num变量,而保留 全局环境中的num变量,发现此时输出的是全局num变量的值。

  4. 例四
    // var num = 0;  删掉全局变量 num
    function f1()
    {
        function f2()
        {
            function f3()
            {
                function f4()
                {
                    console.log(num);
                }
                f4();
            }
            f3();
        }
        f2();
    }
    
    
    
    f1();
    // 报错:找不到 num
    

    连同全局下的num变量一并删掉,再次进行输出,发现程序报错找不到 num这个变量。

  5. 例五
    function f1()
    {
        function f2()
        {
            function f3()
            {
                function f4()
                {
                    num = 4;
                    console.log(num); // 4
                }
                f4();
            }
            f3();
        }
        f2();
    }
    
    
    
    f1();
    console.log(num); // 4
    

    不使用var,在f4内定义num变量,结果正如我们所说,如果全局没有定义过这个变量,那么这一步相当于在全局var定义了这个变量。

分析结论:

由以上的五个例子,结合我们所说的“函数作用域的嵌套关系是由定义位置决定的”,我们可以得到一个简单结论:

嵌套作用域里的变量在查找的时候,首先从自己的作用域当中去查找。

如果自己的作用域内没有,则会往上一级作用域当中去查找。一直查找到函数外部的全局作用域。

只要找到,立即停止往上找,直接输出。如果没有找到,会一直往上找,如果最后查到函数外部的全局还没有的话,则报错。

这个查找的顺序过程,我们称其为“作用域链”。

案例说明
var num = 10;

function fun() 
{
    var num = 20;
	function fun2() 
	{
	    console.log(num);//20
	}
    fun2();
}

fun();

最重要的一条规则: 作用域的嵌套关系是由定义位置决定的,作用域链是在定义时就随即出现的,而不是由调用位置决定的。

三、图解全局环境与局部环境作用过程(内存是怎么表现的)

var a = 10;
			
function f1()
{
    var b = 11;
};

f1();

预解析

预解析,又被称为声明提升、变量提升。

两个基本原则:

  1. 变量

    预解析只会解析带var的变量,如果var,则进行预解析。

  2. 函数
    • function f1(){} (字面量定义)

      如果是这种写法,函数整体会提升。
      整体提升后,要当成完全体看待,则可看成var f1 = function(){}整体提升。

    • var f1 = function(){}(函数表达式定义)

      如果是这种写法,只会提升var f1,不会提升函数的表达式。

预解析有什么效果

  1. 变量

    全局当中所有带var的变量,以及使用字面量定义的函数,都会提升到全局的最上方。而最重要的是:变量的声明会被提升到当前作用域的最上面,但变量的赋值不会提升。

  2. 函数

    函数当中所有带var的变量,以及使用字面量定义的函数,都会提升到这个函数的局部环境的最上方。而最重要的是:函数的声明会被提升到当前作用域的最上面,但是不会调用函数。

通过案例来理解预解析的效果

  1. alert(a);
    a = 0;
    
    // 报错
    
  2. alert(a);
    var a = 0;
    alert(a);
    
    // undefined
    // 0
    

    根据变量的声明会被提升到当前作用域的最上面,但变量的赋值不会提升的原则,其实它的代码在预解析之后是这个样子:

    var a 
    console.log(a)
    a = 0
    console.log(a)
    

    所以是先输出undefined,后输出0

  3. alert(a);    
    var a = '我是变量';
    function a(){ alert('我是函数'); }
    alert(a);
    
    // function a(){ alert('我是函数'); }
    // 我是变量
    

    解析过程:

    1. 开始解析,全局扫描,提升位置。

    2. 提升位置后的新代码相当于:

      var a;
      function a(){ alert('我是函数'); }
      alert(a);
      a = '我是变量';
      alert(a);
      
    3. 又因为,function a(){ alert('我是函数'); }的完全形式为var a =function(){ alert('我是函数'); },所以会把上面的var a; 给覆盖掉。

  4. alert(a); 
    a++;
    alert(a);    
    var a = '我是变量';
    function a(){ alert('我是函数'); }
    alert(a);
    
    // function a(){ alert('我是函数'); }
    // NAN
    // 我是变量
    

    解析后的新代码相当于:

    var a;
    function a(){ alert('我是函数'); }
    alert(a);
    a++;
    alert(a);
    a = '我是变量';
    alert(a);
    
  5. alert(a);   
    var a = 0;
    alert(a);   
    function fn()
    {
        alert(a);    
        var a = 1;
        alert(a);    
    }
    fn();
    alert(a);
    
    // undefined
    // 0
    // undefined
    // 1
    // 0
    
  6. alert(a);
    var a = 0;
    alert(a);
    function fn()
    {
        alert(a);
        a = 1; // 直接改变的是全局变量a的值
        alert(a);
    }
    fn();
    alert(a);
    
    // undefined
    // 0
    // 0
    // 1
    // 1
    

预解析的优先级(判断预解析的终极方法)

先去解析函数,函数如果有同名会发生覆盖。

再去解析var的变量,如果变量跟函数有同名,真正的情况是忽略。(下面赋值的时候才管你)

案例
alert(a);    
var a = '我是变量';
function a(){ alert('我是函数') }
alert(a); 
function a(){ alert('我是函数2')}
var a = '哈哈';
alert(a);

分析:

function a(){ alert('我是函数') }
function a(){ alert('我是函数2')}
var a; // 因为变量重名,所以忽略;
var a; // 因为变量重名,所以忽略;
alert(a);    // 忽略var a; 所以分析结果是“function a(){ alert('我是函数2')}”
a = '我是变量';
alert(a);  // 分析结果是“我是变量”
a = '哈哈';
alert(a);  // 分析结果是“哈哈”

结果正如分析。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-10-03 17:00:20  更:2021-10-03 17:00:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 21:37:43-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码