目录
1. 数据类型
JS的数据类型分
- 基本类型(String、Number、Boolean、Null、Undefined、Symbol【ES6中新加入类型】)
- 引用类型
属性 | 基本类型 | 引用类型 |
---|---|---|
定义 | 简单的数据段 | 可能有多个值构成的对象 |
值 | 可以操作保存在内存中的实际值 | 保存在内存中的对象,按引用访问 |
动态属性 | undefined | 可以添加属性/方法(people.name) |
2. 变量的创建
变量:将值赋给某个东西,这个东西就叫~
- 静态类型(强制类型):声明一个变量存放指定类型的值。eg:java
- 动态类型(弱类型):允许一个变量在任何时刻修改成任何类型的值。eg:javascript
TypeScript 相对于 JavaScript ,提供了静态类型,提高程序正确性。(但笔者认为,如果不出现类型错误的情况下,动态类型在任何时刻反而更加灵活)
大概说说JS引擎进行编译的步骤
- 分词/词法分析(eg:var a =1会分解成var、a、=、2,这些代码块称词法单元)
- 解析/语法分析(将词法单元流【数组】转换成一个由元素逐级嵌套所组成的代表了程序语法的结构树即 “抽象语法树”【AST】)
- 代码生成(将AST转换成可执行代码的过程)
a. 引擎每一次遇到声明,它就把声明传到作用域创建一个绑定。并且对每个声明的变量进行分配内存,默认值为undefined。
// 声明一个名字a,值为1
let a = 1
创建变量过程
- 创建了一个名字a,用来保存后面的值
- 设置变量a的作用范围(作用域)
- 在预处理阶段会被提到函数顶部,此时a为undefined【声明提升–变量都是undefined,函数则是该方法,其他会报错XX is not defined】
- 最后区分不同数据类型进行赋值:
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时,则说明没有办法再访问这个值,将其所占的内存空间给收回来。