第三章介绍了JS语言的基础,包括定义变量、常见的数据类型、操作符。
第四章则深入介绍JS中变量的底层机制。
原始值、引用值的区别
第三章中,介绍了JS中的数据类型,笼统分为:简单数据类型(原始值)、复杂数据类型(引用值)。
- 原始值存在栈空间,保存原始值的变量按值访问;引用值是对象,保存在堆空间,包含引用值的变量实际只包含指向堆中相应对象的指针,即栈中存放的是引用(对象在堆中的地址)。
- 通过变量复制时,原始值是拷贝一个一样的值,是创建该值的第二个副本;引用值是将其栈中存放的指针拷贝给变量(复制后两个变量指向堆中的同一个对象)。
PS:JavaScript中规定,函数的传参是按值传递的,即实参是以类似于变量复制的形式复制给形参接收的。实参变量保存是原始值时,形参接收到的是实参原始值的复制;实参变量保存引用值时(实际存的是引用值在堆中的地址),形参变量接收到的也是引用值在堆中的地址,那么函数中对形参属性的修改会反映到实参变量。
function addTen(num) {
//相当于有一句:let num = count
num += 10
return num
}
let count = 20
let res = addTen(count)
console.log(count) //20
console.log(res) //30
function setName(obj) {
// let obj = p
obj.name = 'ly'
//obj = new Object()
//obj.name = 'yy'
}
let p = {
name: 'cy'
}
setName(p)
console.log(p.name) // ly
变量类型确定
typeof 操作符适合用于确定一个变量是否是原始类型,即判断一个变量是否为字符串、数值、布尔、undefined。
instanceof 操作符用于确定一个变量的引用类型。
通用类型确定方法:
function getType(varible){
return Object.prototype.toString.call(varible).replace(/^\[object (\S+)\]$/,'$1')
}
执行上下文(作用域)与作用域链
任何变量(不管包含的是原始值还是引用值)都存在于某个执行上下文中(也称作用域),其决定了变量的生命周期。
上下文分为:
- 全局上下文(全局作用域)
- 函数上下文(函数作用域)
- 块级上下文(块级作用域)
作用域链:
变量标识符的查找机制,先在当前局部上下文中搜索,局部上下文中找到,则搜索停止;如果没有找到,则沿着作用域链,去上一级查找,直到找到或者到了全局上下文。
依赖的是子作用域可以访问父作用域,父作用域不能访问子作用域。
垃圾回收机制
JS中自动回收内存,不需要手动管理。基本思路就是:确定那个变量不会再使用,然后释放它占用的内存。 垃圾回收程序周期性进行。
标记清理法
将用不上的变量打上标记,在垃圾回收期间被删除。
引用计数法
每个变量记录被应用的次数,当引用次数为0时,就会被回收;但是存在循环引用的问题。
内存泄漏
JS中内存泄漏大部分都是由不合理的引用导致的。
- 函数内部意外声明的全局变量
name = 'bob是最常见、也是最容易修复的内存泄漏的问题:只需要通过var、let、const关键字声明即可。 - 定时器可能导致内存泄漏--定时器回调函数内部引用的外部的变量只要在定时器未被清除之前都会存在,不会被垃圾回收。
//只要定时器不被清除,定时器内部引用的name变量就不会被垃圾回收,可能会造成内存泄漏
let name = 'bob'
setTimeInterval( () => {
console.log(name)
} , 100)
- 使用js闭包很容易造成内存泄漏
//下面的闭包代码会导致分配给name的内存被泄露
let outer = function(){
let name = 'bob'
return function(){
return name
}
}