几年之后再看JavaScript--变量的诞生到结束

1. 数据类型

JS的数据类型分

  • 基本类型(String、Number、Boolean、Null、Undefined、Symbol【ES6中新加入类型】)
  • 引用类型
属性基本类型引用类型
定义简单的数据段可能有多个值构成的对象
可以操作保存在内存中的实际值保存在内存中的对象,按引用访问
动态属性undefined可以添加属性/方法(people.name)

2. 变量的创建

变量:将值赋给某个东西,这个东西就叫~

  • 静态类型(强制类型):声明一个变量存放指定类型的值。eg:java
  • 动态类型(弱类型):允许一个变量在任何时刻修改成任何类型的值。eg:javascript

TypeScript 相对于 JavaScript ,提供了静态类型,提高程序正确性。(但笔者认为,如果不出现类型错误的情况下,动态类型在任何时刻反而更加灵活)

大概说说JS引擎进行编译的步骤

  1. 分词/词法分析(eg:var a =1会分解成var、a、=、2,这些代码块称词法单元)
  2. 解析/语法分析(将词法单元流【数组】转换成一个由元素逐级嵌套所组成的代表了程序语法的结构树即 “抽象语法树”【AST】
  3. 代码生成(将AST转换成可执行代码的过程)
    a. 引擎每一次遇到声明,它就把声明传到作用域创建一个绑定。并且对每个声明的变量进行分配内存,默认值为undefined。
  // 声明一个名字a,值为1
  let a = 1 

创建变量过程

  1. 创建了一个名字a,用来保存后面的值
  2. 设置变量a的作用范围(作用域)
  3. 在预处理阶段会被提到函数顶部,此时a为undefined【声明提升–变量都是undefined,函数则是该方法,其他会报错XX is not defined】
  4. 最后区分不同数据类型进行赋值:
    a. 基本类型把值直接保存在变量中
    b. 引用类型把对象保存在计算机内存中,同时创建访问内存的地址即引用,把引用保存到变量中

总结:变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如 果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能够找到就会对 它赋值。

3. 不同类型变量复制原理

基本类型变量复制原理

对于 基本类型的变量来说,两个变量之间可以 参与任何操作而不相互影响
存在栈中。

  let a = 6
  let b = a
  b = 8 
  console.log(b) // 8
  console.log(a) // 6

在这里插入图片描述

引用类型变量复制原理

对于引用类型变量,复制实际上是指针指向存储在堆中的一个对象,改变其中一个变量,就会影响另一个变量。

  let person1 = {
    name: '小明'
  }
  let person2 = person1
  person2.name = '小红'
  console.log(person1.name) // 小红
  console.log(person2.name) // 小红

在这里插入图片描述

ECMAScript中所有函数的参数都是按值传递的。【参数会复制给一个局部变量(arguments对象的一个元素),在参数传递引用类型的时候,会把这个值在内存中的地方复制给这个局部变量,因此这个局部变量的改变也会影响函数外部】

4. 检测变量类型

typeof 操作符是检测类型最佳的操作,但是对Object对象不友善。
采用 instanceof 操作符可以区分检测object类型。

两者结合才是最好验证类型的方法

 // typeof
  let genre;
  console.log(typeof genre) // undefined
  genre = 'haha'
  console.log(typeof genre) // string
  genre = 33
  console.log(typeof genre) // number
  genre = NaN
  console.log(typeof genre) // number
  genre = true
  console.log(typeof genre) // boolean
  genre = undefined
  console.log(typeof genre) // undefined
  genre = null
  console.log(typeof genre) // object
  genre = { name: 'cere', gender: 'man' }
  console.log(typeof genre) // object
  genre = [1, 2, 3]
  console.log(typeof genre) // object
  genre = function () { console.log(1) }
  console.log(typeof genre) // function

  // instanceof
  genre = null
  console.log(genre instanceof Object) // false
  genre = { name: 'cere', gender: 'man' }
  console.log(genre instanceof Object) // true
  genre = [1, 2, 3]
  console.log(genre instanceof Array) // true

注意几点

  • typeof null返回的是object,这个应该算JS的bug,但似乎不会修改(太多代码依赖这个bug,修改导致大量问题)

    使用符合条件检测null值的类型
    var a = null;
    if (!a && typeof a === ‘object’)

  • NAN 是number类型

null与undefined区别可以参考链接:阮一峰谈undefined与null的区别

5. 垃圾收集

为什么要有垃圾收集机制

之前我们提到过创建变量,或者复制给其他变量等操作的时候都会分配一份内存。如果代码越来越壮大,程序运行起来会申请大量的内存空间,如果不及时清理,内存会使用完(内存溢出),导致程序崩溃

JS的垃圾收集机制

JavaScript不用手工跟踪内存情况,它的自动垃圾收集机制,可以实现自动管理无用内存。垃圾收集器会按照固定的时间间隔,周期性的执行,找到不再继续使用的变量,然后释放占用的内存。

JS垃圾收集策略

垃圾收集器跟踪变量并且判断有没有用,如果没用则在标签打上标记,以备垃将来收回占用的空间。
垃圾收集的策略有很多种,比如标记清除,引用计数,循环引用,分代回收等。浏览器中的实现基本上都是标记清除(或类似策略),只不过垃圾收集的间隔互不相同。

标记清除(mark-and-sweep)

原理:当变量进入环境(比如函数中声明一个变量),将这个变量标记(可以用任何方式标记)为“进入环境”,此时内存占有。而当变量离开环境时候,标记为“离开环境”,然后等待垃圾收集器下次完成内存清楚工作,销毁带标记的值并回收它们所战友的内存空间。

引用计数(reference counting)

引用计数的含义是跟踪记录每个值被引用的次数。当赋给其他值或其他操作的时候,引用次数不断加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。当这个引用次数变成0时,则说明没有办法再访问这个值,将其所占的内存空间给收回来。

阮老师JavaScript 内存泄漏教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值