深入理解js | 青训营笔记

98 阅读6分钟

js基本概念

  1. 单线程: GUI线程和JS线程不能同时执行,互斥。

  2. 动态,弱类型 定义变量不需要指定类型,而在其他语言中,比如C,C++都需要指定类型

image.png

  1. 面向对象,函数式

  2. 解释类语言 JIT

JS是一种解释型语言,程序在运行时边编译边运行,因此需要用到解释器才可执行代码,且执行效率天生比编译型语言低

JIT:即时编译,是一种编译技术,它可以在运行时将代码编译为机器语言,以提高程序的执行效率。

  1. 安全,性能差

JavaScript是一种基于对象和事件驱动并具有相对安全性的客户端脚本语言。它不允许访问本地的硬盘,不能将数据存入服务器,不允许对网络文档进行修改和删除。边解释,边执行,所以性能会相对差,

数据类型

分为基本数据类型,和引用数据类型(对象) 与cpp,Java不同,js的字符串也属于基本数据类型,其他都一样

作用域

规定了变量分为可访问性和可见性,作用域分为动态作用域和静态作用域。

js是静态作用域,通过作用域可以知道代码如何查找标识符。

js中的作用域分为三种,全局作用域,函数作用域,块作用域。如下:

image.png

变量提升

变量提升(hoisting)是JavaScript中的一个特性,它指的是在执行代码之前,变量和函数声明会被移动到它们所在作用域的顶部。

//let const定义的变量没有变量提升,所以不能在定义前使用
console.log('company', company);//err
console.log('dep', dep);//err

// var定义的变量有变量提升,相当于在使用前声明这个变量并且被初始化为 undefined
console.log('myname', myname);//undefined

// 函数也有变量提升
// 在声明前使用该函数,发生函数提升,把函数的定义提到前面并且被初始化为 undefined
showInfo();// 正常输出

// var show = undefined 提升
show();// err  show不是一个函数

const company = "Bytedance";
let dep ="边缘云";
var myname = "unkown";

function showInfo(){
    console.log(company, dep, mynane);
    console.log(myname);
}

var show = function () {
    console.log(myname);
}

js是怎么执行的

image.png

执行上下文

词法环境中存储了当前执行上下文中所有的本地变量、函数声明、形参等信息,以及对外部词法环境的引用。 (感觉类似于os的中断处理)

当JS 引擎解析到可执行代码片段(通常是函数调用) 的时候,就会先做一些执行前的准备工作,这个准备工 作,就叫做“执行上下文( execution context简 称EC ) " ,也叫执行环境

调用栈: (图中 func应该放在词法环境中)

image.png

在调用栈中有一个ESP指针,指向栈顶空间,即当前运行的函数

词法环境、变量环境、outer

  • 词法环境: 存放let const 定义的变量以及函数
  • 变量环境:存放var 定义的变量。
  • outer:指向外部函数的一个指针。

(js可以在函数里面定义函数)

image.png

堆和栈

基本类型:采用的是值传递

引用类型:则是地址传递

引用类型的数据的地址指针是存储于栈中的,将存放在栈内存中的地址赋值给接收的变量。当我们想要访问引用类型的值的时候,需要先从栈中获得对象的地址指针,然后,在通过地址指针找到堆中的所需要的数据(保存在堆内存中,包含引用类型的变量实际上保存的不是变量本身,而是指向该对象的指针)。

js进阶知识点

this

在 JavaScript 中,this 关键字的指向取决于函数的调用方式。下面是一些常见的情况:

  1. 在全局作用域中,this 指向全局对象。在浏览器中,全局对象是 window 对象。直接输出this时,值为 global

  2. 其他情况类似java和cpp ,另外可以使用 callapply 或 bind 方法显式地设置 this 指向的值。

var value='I am global';

function func() {
  console.log(this.value);
}

let obj = { value: 'I am object' };

func.call(obj); // 输出 'I am object'   让this指向obj ,同样apply,bind都可以

垃圾回收

v8对GC(回收垃圾的)的优化

分代式垃圾回收:v8 的垃圾回收策略主要基于分代式垃圾回收机制,V8 中将堆内存分为新生代和老生代两区域。

image.png

新生代垃圾回收:新生代空间比较小,内存分为对象区域和空闲区域。 1.垃圾标记 ,将数据标记出来:哪些是垃圾哪些是活跃数据。

2.对象复制 ,把对象(即活跃的数据)复制到空闲区。

3.区域反转 ,对象区和空闲区反转,然后清除空闲区,原来的对象现在是在对象区。

新生代回收使用并行回收提高效率并行回收依然会阻塞主线程

老生代垃圾回收

对象占用空间过大,新生代区存放不下时就存放在老生代区,另外新生代区中经过两次没有被清除掉的数据会放到老生代区

老生代垃圾回收采用标记整理算法,标记出垃圾数据并清理,然后整理内存,空出连续的内存

垃圾清理时js停止执行,以防在清理时引用到垃圾数据。

另外采用增量标记、惰性清理,并发回收提高效率

增量标记是指将一次垃圾回收的过程分为很多个小部分,每次执行完一小块,就让应用程序执行一会,交替执行。增量标记采用三色标记法(暂停与恢复)与写屏障(增量中修改引用)

惰性清理指的是让js脚本先执行,也无需一次性清理完所有非对象内存。

并发回收:主线程在执行 JavaScript 的过程中,辅助线程能够在后台完成执行垃圾回收的操作,辅助线程在执行垃圾回收的时候,主线程也可以自由执行而不会被挂起

事件循环

js代码主要分为两大类: 同步代码、异步代码

  1. 异步代码又分为:微任务与宏任务

  2. 事件循环Event Loop执行机制

    • 1.进入到script标签,就进入到了第一次事件循环.

    • 2.遇到同步代码,立即执行

    • 3.遇到宏任务,放入到宏任务队列里.

    • 4.遇到微任务,放入到微任务队列里.

    • 5.执行完所有同步代码

    • 6.执行微任务代码

    • 7.微任务代码执行完毕,本次队列清空 (一次执行完所有微任务)

    • 寻找下一个宏任务,重复步骤1 (每一个宏任务执行完都检查是否有新增微任务,有就先把这些新增的微任务全执行完)

      以此反复直到清空所有宏任务,这种不断重复的执行机制,就叫做事件循环

    宏任务主要包含:script( 整体代码)、setTimeout、setInterval、I/O、UI 交互事件、setImmediate(Node.js 环境)

    微任务主要包含:Promise、MutaionObserver、process.nextTick(Node.js 环境)

写在最后

笔记参考@胡萝卜会飞 大佬的(字节青训营-深入理解js - 掘金 (juejin.cn))

20230105_132931000_iOS.jpg

大家的目光都在烟花上,但是,对不起,我却无法,把目光从这侧脸上移开,心跳的声音实在太吵了,连烟花的声音都听不到了。