关于javascript:一文搞懂JavaScript变量作用域和内存

36次阅读

共计 2895 个字符,预计需要花费 8 分钟才能阅读完成。

ECMAScript 变量能够蕴含两种不同类型的数据:原始值和援用值

原生值和援用值的区别

什么是原始值:
最简略的值 例如:Undefined、Null、Boolean、Number、String、Symbol,BigInt。保留原始值的变量是按值(by value)拜访的,因为咱们操作的就是存储在变量中的理论值
什么是援用值:
由多个值形成的对象 例如:Object。援用值是保留在内存中的对象,保留援用值的变量是按援用(by reference)拜访的,因为 JavaScript 不容许间接拜访内存地位,实际上操作的是对该对象的援用(reference)而非理论的对象自身。

区别:

  • 动静属性:
    原始值:不能有属性
    援用值:能够随时增加、批改和删除其属性和办法
  • 复制值:
    原始值:复制值变量能够独立应用,互不烦扰。

    let num=123;
    let num1=num;
    console.log(num,num1)//123,123
    num=100;
    console.log(num,num1)//100,123

    援用值:复制值,复制的是栈中的指针(堆中对象的地址),指向的是同一个对象

    let obj={name:"lly",sex:"Man"}
    let obj1=obj;
    console.log(obj,obj1)//{name:"lly",sex:"Man"},{name:"lly",sex:"Man"}
    obj.age=20
    console.log(obj,obj1)//{name:"lly",sex:"Man",age:20},{name:"lly",sex:"Man",age:20}
  • 传递参数:
    原始值:跟原始值变量的复制一样

    function add(num) { 
           num += 10; 
           return num; 
          } 
          let count = 20; 
          let result = add(count); 
          console.log(count); // 20,没有变动
          console.log(result); // 30

    援用值:跟援用值变量的复制一样

    function setName(obj) {obj.name = "lly";} 
    let person = new Object(); 
    setName(person); 
    console.log(person.name); // "lly"
  • 对于类型的判断的办法:
    原始类型值:typeof
    援用类型值:instanceof
    比拟全面的判断办法:Object.prototype.toString.call(要判断的类型值);

    执行上下文与作用域

    什么是执行上下文:
    以后 JavaScript 代码被解析和执行时所在环境的抽象概念
    执行上下文的品种
    1. 全局执行上下文
    3. 函数执行上下文
    2. 块级执行上下文
    什么是作用域:
    一个不让变量外泄裸露的独立区域,简略说就是隔离变量的空间,特质:同名变量在不同作用域下互不抵触。
    作用域的品种:

  • 全局作域域
  • 函数作用域
  • 块级作用域

变量申明的品种以及特色

  1. var 函数作用域申明
    存在变量晋升,在同一作用域内能够反复申明同一变量
  2. let 块级作用域申明
    也存在变量晋升只是暂时性死区缘故导致变量申明之前不可用、在同一作用域内不能申明两次
  3. const 常量申明
    申明变量必须同时初始化值,一经申明不能从新赋值(对于初始值是原始值,不可从新赋值,对于初始值是援用值,不能从新赋值其余援用,对援用值得键值没有限度),其余特质和 let 雷同。

    垃圾回收机制

    JavaScript 是应用垃圾回收的语言。执行环境负责在代码执行时治理内存。通过主动内存治理实现内存调配和闲置资源回收。实现思路:确定那个变量不在应用,就开释所占内存。垃圾回收的标记策略次要采纳:标记清理和援用计数。
    标记清理的原理
    来到作用域的值会被主动标记为可回收,而后在垃圾回收期间被删除,先给以后不应用的值加上标记,再回来回收它们的内存
    援用计数的原理(不罕用的)
    对每个值进行援用次数记录,当援用次数为 0 时,垃圾回收程序下次运行的时候就会开释援用数为 0 的值的内存。
    援用次数的规定:
    申明变量并给它赋一个援用值时,这个值的援用数为 1。如果同一个值又被赋给另一个变量,那么援用数加 1。相似地,如果保留对该值援用的变量被其余值给笼罩了,那么援用数减 1
    性能
    垃圾回收程序会周期性运行,频繁的调度垃圾回收程序会重大影响性能,所以浏览器采纳了许多性能优化的计划:
    1. 分代收集:

    对象被分成两组:“新的”和“旧的”。许多对象呈现,实现它们的工作并很快死去,它们能够很快被清理。那些长期存活的对象会变得“老旧”,而且被查看的频次也会缩小

2. 增量收集:

如果有许多对象,并且咱们试图一次遍历并标记整个对象集,则可能须要一些工夫,并在执行过程中带来显著的提早。所以引擎试图将垃圾收集工作分成几局部来做。而后将这几局部会逐个进行解决。这须要它们之间有额定的标记来追踪变动,然而这样会有许多渺小的提早而不是一个大的提早。

3. 闲时收集:

垃圾收集器只会在 CPU 闲暇时尝试运行,以缩小可能对代码执行的影响。

内存治理
1. 通过 const 和 let 申明晋升性能:
因为 const 和 let 以块为作用域不是函数作用域,更早地让垃圾回
收程序染指,尽早回收应该回收的内存。从而改良垃圾回收的过程。
2. 暗藏类和删除操作:
暗藏类:
JavaScript 引擎为了进步性能引入了暗藏类,运行期间,V8 会将创立的对象与暗藏类关联起来,以跟踪它们的属性特色。

function people() {this.name= 'lly';} 
let a1 = new people(); 
let a2 = new people(); 
a2.sex="Man"

解决方案就是防止 JavaScript 的“先创立再补充”式的动静属性赋值,并在构造函数中一次性申明所有属性。

function people(sex) { 
 this.name= 'lly'; 
this.sex=sex
} 
let a1 = new people(); 
let a2 = new people("Man"); 

删除操作:
应用 delete 关键字会导致生成雷同的暗藏类片段,最佳实际是把不想要的属性设置为 null。

function people() { 
 this.name= 'lly'; 
 this.sex="Man"
} 
let a1 = new people(); 
let a2 = new people(); 
delete a1.sex;// 不举荐
a1.sex=null// 最佳实际

3. 导致内存泄露起因以及解决办法:

  • 闭包:手动设置 null,切断援用
  • 意外的全局变量:应用严格模式或者后面加上申明关键字
  • 定时器:不在须要定时器时革除定时器
  • DOM/BOM 对象透露 window.onunload 事件上开释内存

4. 动态调配与对象池:
对象池:
JavaScript 优化的一种策略, 用来治理一组可回收的对象。应用程序能够向这个对象池申请一个对象、设置其属性、应用它,而后在操作实现后再把它还给对象池。因为没产生对象初始化,垃圾回收探测就不会发现有对象更替,因而垃圾回收程序就不会那么频繁地运行
动态调配:
优化的一种极其模式。如果你的应用程序被垃圾回收重大地拖了后腿,能够利用它晋升性能。但这种状况并不多见。大多数状况下,这都属于过早优化,因而不必思考。

正文完
 0