深入了解 JS | 青训营笔记

176 阅读5分钟

一. 基本介绍

JavaScript(缩写:JS)是一门完备的动态编程语言。当应用于HTML文档时,可为网站提供动态交互特性。由布兰登·艾克(Brendan Eich,Mozilla 项目、Mozilla 基金会和 Mozilla 公司的联合创始人)发明。

基本数据类型(引用课程内部图片)

image.png

注意: 基本数据类型在上面,复制数据类型在上面

二. 如何执行

引用青训营课程的图片: image.png

  • 词法环境

代码执行过程中存储变量、函数和对象等标识符与其对应值的数据结构 , 可以用来存储函数声明和变量(let和const)绑定。

  • 变量环境

存储当前上下文中定义的变量和函数等标识符及其对应值的数据结构,可以用来存储 var 变量绑定。

  • Outer

指向外部变量环境的一个指针

a. 作用域

变量的可访问性和可见性静态作用域,通过它就能够预测代码在执行过程中如何查找标识符

  • 全局作用域
var a = 6
console.log(a)
  • 函数作用域
var a = 6

function showA(){
    a = 7
    console.log(a) // 7
}
  • 块级作用域
{
    let a = 7
    console.log(a) // 7
}

b. 变量提升

  • var有变量提升
  • let、const没有变量提升(不是绝对的),提前访问会报错
  • function函数可以先调用再定义
  • 赋值给变量的函数无法提前调用

c. 执行上下文

  • 全局执行上下文

代码开始执行时就会创建,将他压执行栈的栈底,每个生命周期内只有一份

  • 函数执行上下文

当执行一个函数时,这个函数内的代码会被编译,生成变量环境、词法环境等,当函数执行结束的时候该执行环境从栈顶弹出

  • Eval执行上下文

创建一个被称为“eval 上下文”的特殊执行上下文,并在此上下文中执行指定的代码。这个上下文包含一些默认变量和函数,例如当前作用域链、this 值等,同时还可以访问其外围的变量和函数,但不能访问被封闭的函数变量或绑定到当前作用域的 let 和 const 变量

三. 进阶

a. 值传递和引用传递

不得不提到深拷贝和浅拷贝

深拷贝(值传递): 拷贝目标的值 (常出现在变量的值赋值中)

let a = 100;

let b = a;

console.log(a,b) // 100 100

浅拷贝(引用传递): 拷贝目标地址,将自身指到目标的的堆内存中的地址 (常出现在对象(包括数组,数组的本质也是对象)的赋值中)

let a = {
    name:'xiaomin',
    age:26
};
// 将 a 的地址复制到了 b ,两个变量指向同一个地址
let b = a;

b.name = 'xiaohua';
console.log(a.name,b.name) // xiaohua xiaohua

b. 闭包

  • 简介

内部函数可以访问其外部函数中定义的变量和参数,即使在外部函数调用已经结束之后,这些变量和参数也依然存在于内存中。

我们知道,根据 JS 的垃圾回收机制, JS 引擎在代码从当前执行上下文切换下一个执行上下文时会清除掉当前执行上下文的所有变量,函数,以及对象等占用的内存空间

  • 例子(制作一个简易的计数器)
function funA(){
    let count = 0;
    return function(){
        // 访问了其它的执行上下文中的变量,形成闭包
        count++;
        console.log(count);
    }
}

const fun = funA()

fun() // 1
fun() // 2
  • 总结

优点:

  1. 避免与全局变量命名冲突
  2. 实现回调和事件处理,通过闭包将当前的环境保存下来,通过回调函数轻松访问到
  3. 实现私有方法和属性,保证只能在当前函数下访问,保证代码的安全性和可维护性

缺点:

  1. 可能会造成内存泄漏
  2. 作用域难理解,造成一定的难维护性

c. this 指向

  • 普通函数的 this 指向 window (全局)
function fun(){
    console.log(this)
}
  • 对象调用则指向对象

先赋值再调用,看调用的地方

const person = {
    name:'xiaowmin',
    function fun(){
        console.log(this.name)
    }
}

const people = person.fun;

person.fun() // 指向 person
people() // 指向 people
  • 构造函数中的指向
function funA(){
    this.name = 'xiaomin';
    console.log(this.name); // xiaomin
}

1.创建临时对象
2.this指向临时对象
const fun = new funA();

3.执行构造函数
4.返回临时对象
console.log(fun.name); // xiaomin

d. 垃圾回收

1. 栈的回收

通过 ESP(记录当前状态)指针

当前执行上下文切换到下一个时,ESP 指针也会指向下一个,并且会清除覆盖掉当前的执行上下文,达到垃圾回收的目的

2. 堆的回收

将 JS 的执行分成两个区域 对象区域空闲区域对象区域是存放所有变量、函数的区域

  • 垃圾标记

Js 自动寻找存在对象区域中不再使用的变量,并标记这些变量作为待回收对象

  • 对象复制

对象区域中的数据复制到空闲区域

  • 区域反转

将两个区域进行调换, 对象区域变成空闲区域空闲区域变成对象区域,最后将之前的对象区域清空

e. 事件循环

事件循环在每次轮询时,最先处理的就是当前调度队列中的宏任务。完成后,会立刻检查是否有可用的微任务,如果存在则依次执行所有微任务。随后再开始下一个宏任务,如此循环下去。

微任务先于宏任务执行,微任务相对于宏任务,具有更高的优先级

  • 宏任务

setTimeout、setlnterval、DOM 事件等

  • 微任务

Promise 回调