• 一篇文章带你了解JavaScript中的变量,作用域和内存问题


    file

    作者 | Jeskson

    来源 | 达达前端小酒馆

    1

    在JavaScript中的变量分别区分为两种:

    一种为基本类型值,一种为引用类型值。

    基本类型值指的是简单的数据段

    引用类型值为可能由多个值组成的对象

    file

    引用类型的值是保存在内存中的对象,JavaScript不允许直接操作对象的内存空间,实际上操作对象的引用而不是实际对象。

    file

    var dada = new Object();
    undefined
    dada.name = "dada";
    "dada"
    console.log(dada.name);
    VM158:1 dada
    undefined
    

    file

    var da1 = "da1";
    undefined
    da1.age = 12;
    12
    console.log(da1.age);
    VM272:1 undefined
    undefined
    

    基本类型的值添加属性,是不管用的,只能给引用类型的值动态地添加属性,才是有用的。

    2

    复制变量值

    就是从一个变量向另一个变量复制 基本类型值 和 引用类型值

    var da1 = 12;
    var da2 = da1;
    

    file

    da1中保存的值是12,使用da1的值来初始化da2时,da2中也保存了值12,但是d2中的值12和da1中的值12是完全独立的。这两个变量可以参与任何操作互不影响。

    从一个变量向另一个变量复制引用类型的值:

    引用类型的值实际上是一个指针,是指向存储在堆中的一个对象,引用类型的复制,是将指向引用同一个对象,所以改变其中一个变量,另一哥变量也会受到影响。

    var da3 = new Object();
    var da4 = da3;
    
    da3.name = "dada";
    console.log(da4.name);
    

    file

    da3和da4指向同一个对象,da3添加name属性后,da4来访问这个属性,因为两个变量指向同一个对象,所以输出结果为dada。

    3

    参数传递:

    在JavaScript中所有函数的参数都是按值传递的,参数按值传递的意思,和复制一样的,把函数外的值传递到函数内部。

    function addNum(num){
     num = num + 1;
     return num;
    }
    var da5 = 12;
    var result = addNum(da5);
    console.log(da5);
    console.log(result);
    

    file

    函数addNum有一个参数num,这个参数实际为函数的局部变量。调用这个函数,变量da5作为参数被传递给了这个函数,这个变量的值为12,所以参数num为12在这个addNum()函数中使用。

    function setName(obj) {
     obj.name = "dada";
    }
    var da6 = new Object();
    setName(da6);
    console.log(da6.name);
    

    file

    检测类型:

    typeof操作符是用来检测一个变量是否是基本数据类型,如果变量的值为一个对象或null,那么这个typeof操作符下返回的就是object。

    var da7 = "dada";
    var da8 = 12;
    var da9;
    var da10 = null;
    var da11 = new Object();
    
    console.log(typeof da7);
    console.log(typeof da8);
    console.log(typeof da9);
    console.log(typeof da10);
    console.log(typeof da11);
    

    file

    instanceof操作符,是用来干什么的呢?判断是什么类型的对象。

    // 前提先定义person
    console.log(person instanceof Object);
    console.log(person instanceof Array);
    console.log(person instanceof RegExp);
    

    注意,所有的引用类型的值都是Object的实例,所以检测引用类型的值和Object构造函数时,instanceof操作符都是返回true。instanceof操作符检测基本类型的值,返回的则都是false。因为instanceof检测的都是什么类型的对象。

    4

    作用域:

    当代码在一个环境中执行时,会创建变量对象的一个作用域链,这个作用域链的用途是 保证对执行环境有权访问的多有变量和函数的有序访问。全局执行环境的变量对象都是作用域链中的最后一个对象。

    标识符解析是沿着作用域链一级一级地搜索标识符的过程。

    var da12 = "dada"
    
    function changeDa(){
     if(da12 === "dada"){
      da12 = "da";
     }else{
      da12 = "da1";
     }
    }
    
    changeDa();
    
    console.log(da12);
    

    file

    函数changeDa()的作用域链包含两个对象:

    它自己的变量对象,和,全局环境的 变量对象。

    它自己的 定义着的arguments对象

    var da12 = "dada"
    
    function changeDa(){
     var anotherDa = "dadada";
     
     function daDa(){
      var tempDa = anotherDa;
      anotherDa = da12;
      da12 = tempDa;
      // 可以访问 tempDa, anotherDa,da12
     }
     // 这里只能访问 da12,anotherDa
     daDa();
    }
    // 这里只能访问da12;
    changeDa();
    

    file

    分析执行环境,有3个,一个为全局环境,一个为changeDa()的局部环境,一个为daDa()的局部环境。

    全局环境中有一个变量da12,和一个函数changeDa()。

    changeDa()的局部环境中有什么?

    一个变量anotherDa,一个名为daDa()的函数。这个函数可以访问全局变量中的da12。

    daDa()的局部环境中有什么?

    一个变量tempDa,该变量只能在这个环境中访问。

    无论是全局环境还是changeDa()的局部环境都无法访问tempDa。

    file

    file

    file

    为什么内部的daDa()可以访问其他两个环境中的所有变量呢?

    因为那是它们两个的环境是它的父执行环境。

    内部环境可以通过作用域链访问所有的外部环境,但是外部环境不能访问内部环境中的任何变量和函数,内部环境都可以向上搜索作用域链,查变量和函数名,不能向下搜索作用域链进入另一个环境。

    对于daDa()函数,其中作用域链包含3个对象:

    daDa()的变量对象,changeDa()的变量对象,全局变量对象。

    过程:

    daDa()函数的局部环境,会先开始搜索自己的变量对象中的变量和函数名,如果找不到,会向上搜索上一级的作用域链。

    对于changDa()中的环境:

    它包含两个对象::一为它自己的变量对象,二为全局变量对象。

    即它不能访问daDa()函数的局部环境。

    5

    执行环境分两种:

    一种为全局作用域,一种为局部作用域。

    如何理解 try catch 延长了作用域链?

    with语句和 try catch 都可以延长作用域链

    with比较好理解,而且一般有性能问题,也不推荐用

    try catch 是捕获Error对象的时候 会新开一个作用域吗?

    还是说 catch的大括号内就是一个能访问到error对象的块级作用域?

    try中的代码捕获到错误以后,会把异常对象推入一个可变对象并置于用域的头部,在catch代码块内部,函数的所有局部变量将会被放在第二个作用域对象中,catch中的代码执行完,会立即销毁当前作用域。

    什么叫延长作用域链#

    执行环境(变量对象可谓是它的衍生物)、作用域、作用域链

    file

    作用域:函数当前执行环境。

    作用域链:执行环境产生的变量对象构成。 作用域链是保证函数在执行时能够正确访问需要的变量和函数。

    作用域链最外层就是全局作用域

    file

    var i = 0;
    function dada(){
        console.log(i);
    }
    undefined
    dada();
    VM656:3 0
    undefine
    

    在函数中是没有存在i的,但是在调用这个函数时会返回为0,这是为什么呢?这就是函数作用域链的作用。

    延长一: try catch

    file

    file

    (function(window){
       try{
           throw Error("出错误了");
       }catch(e){
          alert(e);  //alert("Error: 出错误了")
       }
       console.log(e);  //undefind
    })(window);
    

    在执行catch语句块时,JavaScript自动把其执行环境添加作用域链中,但是该语句块执行完后又自动把该执行环境(变量对象)移除。

    alert(e) ==  alert("Error:出现错误");
    console.log(e)  ==  undefined;
    

    IE结果:

    alert(e)  =>  alert("Error: 出错误了");   
    
    console.log(e) =>   
    object Error: 
    出错误了{description: "出错误了",message: "出错误了",name: "Error"}
    

    延长二:with

    file

    function da(){
        console.log(location.href); 
    } 
    
    function da(){ 
        with(location){ 
            console.log(href); 
        } 
    }
    

    两种方式是等价的:

    前提是非严格模式下, 因为严格模式下不支持 with这种方式。

    file

    延长作用域的表现

    file

    什么是作用域链?

    我的理解就是,根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问。

    想要知道js怎么链式查找,就得先了解js的执行环境。

    每个函数运行时都会产生一个执行环境,而这个执行环境怎么表示呢?

    js为每一个执行环境关联了一个变量对象。环境中定义的所有变量和函数都保存在这个对象中。

    没有块级作用域:

    if(true) {
     var da = "dada";
    }
    console.log(da);
    

    file

    file

    function add(num1,num2){
        var num = num1 + num2;
        return num;
    }
    undefined
    var result = add(1,2);
    undefined
    console.log(result);
    VM962:1 3
    undefined
    

    file

    向上查询:

    file

    var da = "dada";
    
    function getDa(){
     var da = "dadada";
     return da;
    }
    
    console.log(getDa());
    

    JavaScript中最常用的垃圾收集方式是标记清除,另一种不太常见的垃圾策略叫做引用计数。

    基本类型值和引用类型值:

    基本类型值在内存中占据固定的空间,保存在栈内存中,从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本,引用类型的值为对象,保存在堆内存中。

    包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向对象的指针。

    typeof操作符判断的是一个值是哪种基本类型,instanceof操作符判断的是一个值是哪种引用类型。

    执行环境分:

    全局执行环境,函数执行环境。

    每次进入一个新的执行环境时,都会创建一个用于搜索变量和函数的作用域链。

    ❤️ 不要忘记留下你学习的脚印 [点赞 + 收藏 + 评论]

    作者Info:

    【作者】:Jeskson
    【原创公众号】:达达前端小酒馆。
    【福利】:公众号回复 “资料” 送自学资料大礼包(进群分享,想要啥就说哈,看我有没有)!
    【转载说明】:转载请说明出处,谢谢合作!~

    大前端开发,定位前端开发技术栈博客,PHP后台知识点,web全栈技术领域,数据结构与算法、网络原理等通俗易懂的呈现给小伙伴。谢谢支持,承蒙厚爱!!!


    若本号内容有做得不到位的地方(比如:涉及版权或其他问题),请及时联系我们进行整改即可,会在第一时间进行处理。


    请点赞!因为你们的赞同/鼓励是我写作的最大动力!

    欢迎关注达达的CSDN!

    这是一个有质量,有态度的博客

    前端技术栈

  • 相关阅读:
    开始学习C#
    关于串口数据读取的几个问题
    Joel测试
    VC查找内存泄漏技巧【转】
    思考题一
    自我介绍
    2020面向对象程序设计寒假作业1 题解
    思考题二
    题解 洛谷P2158 【[SDOI2008]仪仗队】
    深入浅出InfoPath系列
  • 原文地址:https://www.cnblogs.com/dashucoding/p/12099908.html
Copyright © 2020-2023  润新知