前端知识点(面试可看) —— JS

2024-01-09 11:45:04

摘要

马上就要毕业啦,没有参加2023年的秋招,准备在最近开始找全职或者实习工作,然后也马上过年了,总结和理一下自己的知识要点,参加2024年的春招。

1. JS的执行流程

  1. 浏览器的V8引擎收到到执行的JS代码
  2. V8结构化这段代码,生成AST(抽象语法树),同时生成相关作用域
  3. 生成字节码(介于AST和机器码之间)
  4. 解释器,按照顺序执行字节码,并输出执行结果

JS代码流程:先编译后执行

2. 基本数据类型

  • undefined
  • null
  • Boolean
  • String
  • Number
  • Symbol
  • BigInt
  • Object:只有他存在堆里面

3. 判断数据类型的方式(TTIC)

  1. typeof:判断基本数据类型,判断null时,会返回object
  2. Object.prototype.toString.call(xx):返回一个[object 数据类型]
  3. instanceof:A instanceof B :判断A和B是否有血缘关系,不仅仅根据父子关系。
  4. constructor

4. ES6的新特性

  1. constlet
  2. 解构赋值
  3. 模板字符串
  4. 函数的扩展:默认值、rest参数、函头函数
  5. 数组的拓展:Array.from()将类数组转换为数组、find()、findIndex()、entries()、keys()、values(),includes()
  6. 对象扩展:属性名可以用表达式、Object.assign()、Object.keys()、Object.values()、Object.entries()
  7. Symbol
  8. Set、Map
  9. Promise
  10. Iterator和for…of
  11. Generator 和 async await

箭头函数和普通函数

  1. 语法更加简洁清晰
  2. 箭头函数没有 prototype、所以箭头函数没有this
  3. 箭头函数不会创建自己的this,会自动继承最外层的this
  4. call | apply | bind 无法改变箭头指向
  5. 箭头函数不能作为构造函数
  6. 箭头函数不绑定arguments,用rest(…)取代,来访问箭头函数列表
  7. 箭头函数不用用作Generator,不能使用yield关键字

5. Promise 和 async/await

Promise

Promise对象为了解决回调地狱而提出,不是新的语法功能,而是一种新的写法,允许将回调函数嵌套,改成链式调用

流程:

  1. Promise 接受一个executor(),在new的时候立刻执行
  2. executor()内部的异步任务被放入宏\微任务,等待执行
  3. then,收集\失败回调
  4. executor的异步任务被执行,触发resolve\reject,从失败和成功队列中取出依次执行。

其实是设计模式中的一种观察者模式

  1. 收集依赖 (then收集)
  2. 触发通知 (触发resolve)
  3. 取出依赖执行 ( resolve执行)

async await

是对Generator(生成器)的封装,是个语法糖

*/yieldasync/await的区别:

  1. async/await自带执行器,不需要next()就能下一步
  2. async 返回的是Promise对象,而Generator返回的是生成器对象
  3. await能够返回Promise的 reject/resolve的值

注意:无论await后面跟的什么,后面的代码都会被阻塞

和Promise区别

Promise解决了回调地狱,但是他的语法问题纵向发展形成了一个回调链,遇到复杂的业务场景,这样的语法显然是不美观的。
async await看起来简洁一些,看起来像同步代码,其实它出现的意义就是提供异步的效果
两者其实都是非阻塞

6. Generator

核心: 保存上下文,函数在真正意义中没有被挂起,每一次yield,其实都执行了一边生成器函数、在这个过程中用了一个context存储上下文,每次执行生成器函数的时候,都可以从上一个执行结果开始执行

用babel编译后生成regeneratorRuntime

  1. mark()方法为生成器函数绑定了一系列原型
  2. wrap() 相当于给Generator增加一个_invoke 方法

7. 迭代器

模式: 可以把有些结构称为可迭代对象,它们正式实现了Iterable接口,可以通过迭代器消费,是按需创建的一次性对象,每个迭代器都会关联一个可迭代对象

协议: 可迭代协议需要具备两种能力

  1. 支持迭代的自我识别能力
  2. 创建实现Iterator接口的对象的能力

一次性使用对象,用于迭代与其关联的可迭代对象

迭代器api提供一个next()方法,这个方法会返回两个属性:

  1. done:是否还能取得下个值
  2. value:可迭代对象下一个值

默认实现了Iterable的类型

  1. String
  2. Array
  3. Map
  4. Set
  5. arguments
  6. NodeList等Dom集合类型

接受可迭代对象的原生语言特性包括

  1. for of
  2. 数组结构
  3. 扩展操作符
  4. Array.from()
  5. Set
  6. Map
  7. Promise.all()
  8. Promise.race()
  9. yield

8. 设计模式

三类:(C5S7B11)

  1. 创建型模式:五种: 工厂方法、抽象工厂、单例、建造则、原型
  2. **结构型模式:七种:**适配器、装饰器、代理、外观、桥接、组合、享元
  3. **行为型模式:十一种:**策略、模板方法、观察者(发布订阅)、迭代子、责任链、命令、备忘录、状态、访问者、中介者、模板方法、解释器

9. WebGL

Canvas 是画布一般可以获取2D上下文3D上下文3D上下文一般就是WebGL,当然WebGL也能用于2D绘制,并且WebGL提供硬件渲染加速,性能更好。但是支持性不是特别好,在不支持的情况下最好还是用Canvas,可以用一些兜底的库,如threehs、PIXI

10. CommonJS和ES6 Module

首先,CommonJS同步加载ES6异步加载,前者主要用于Node.js服务端编程,模块文件一般已经存在于本地硬盘,所以记载比较快。而后者是异步加载所以适用于浏览器,不会造成堵塞。

其次,CommonJS模块输出的是一个值的拷贝,也就是说一旦输出一个值,模块内部变化就影响不到这个值ES6模块输出的是值的引用,V8引擎在对JS进行静态分析的时候,遇到import命令,会等到脚本真正执行时,才回到被加载模块中取值。

最后,前者是运行时加载,后者是编译时输出接口

注意:CommonJS不适用于浏览器

11. 声明变量的方式

ES5

  • var
  • function

ES6

  • let
  • const
  • import
  • class

12. 函数声明

  • function fn(…args) {}
  • var fn = function(…args){}
  • new Function(‘x’,‘y’,‘return x+y’)

13. Object/Map/WeakMap区别

MapES6提供的一种新特性,是一种键不局限于字符串的一种键值对集合,对于Object来说他的键只能使用字符串。

WeakMap结构和Map有点类似,但是有两点区别:

  1. 他只接受对象为键(null除外)
  2. 键名所指向的对象,不计入垃圾回收机制

总之,WeakMap专用场合就是,它的键所对应的对象,可能会在将来消失,WeakMap结构有助于防止内存泄露

14. JS深浅复制

浅复制

  1. 扩展运算符
  2. Object.assign()
  3. Object,getOwnPropertyDescriptors()+Object.defineProperties()

深复制

  1. 通过嵌套扩展运算符
  2. 使用JSON
  3. 手动实现(FHT)
    1. 利用for-in对对象的属性进行遍历(自身属性+继承属性)
    2. source.hasOwnProperty(i)判断是否非继承可枚举类型
    3. typoeof source[i]==='object判断值的类型,如果是对象,递归处理

15. 闭包

闭包就是在JS种,根据语法作用域规则,内部函数总是可以访问到其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了。但是内部函数引用外部函数的变量依然保存在内存中,就把这些变量的集合成为闭包。

16. Event Loop

也就是事件循环,会不听从微/宏任务队列中取出对应任务的循环函数。在某种情况中,你可以把它称为一个永动机。这些它取出的任务并将其推送到调用栈种执行,主要有四个主要步骤:

  1. 执行Scirpt:用同步的方式执行script,直到调用栈空才停下来。(还会执行预编译操作。如:变量提升)
  2. 执行一个宏任务:挑选最老的到调用栈中执行,直到调用栈为空
  3. 执行所有微任务:挑选最老的到调用栈中执行,直到调用栈为空。(但是,继续从微任务队列种获取最老的执行。重复)
  4. UI渲染:然后跳到第二步,挑选宏任务执行。

Event Loop 单次迭代过程被称为tick。

宏任务 (Task Queue)

也被称为{回调队列| Callback queue},调用栈适用于跟踪正在被执行函数,宏任务则是跟踪将要被执行函数。
事件循环不停的运行着,并且按照一定规则从宏任务队列中不停的去除对象。

宏任务是一个FIFO,存储的宏任务会被事件循环探查到。记得!这些人物都是阻塞的。

微任务(Microtask Queue)

和宏任务一样,他也是FIFO,同样会被探查到。不同的地方是,微任务是在一个宏任务完成后,在UI渲染之前被触发。它是专门处理ES6中Promise回调的。

17. GC

也就是垃圾回收机制

如何实现(算法)

  1. 通过GC Root 标记空间中活动对象非活动对象
    1. V8 采用可访问性算法,来判断对象是否为活动对象
    2. 是将一些GC Root 作为初始存活的对象的集合
    3. GC Root出发然后遍历所有对象
    4. 通过遍历所得到的对象,认为该对象是否为活动对象
    5. 通过遍历所得到的对象,认为该对象是否为非活动对象
    6. 浏览器环境中,GC Root包括1.全局的window对象 2. 文档DOM树 3.存放栈上变量
  2. 回收非活动对象所占据的内存
  3. 内存整理

代际假说

是GC领域中的一个重要术语

两个特点:

  1. 大部分对象都是朝生夕死的
  2. 不死的对象,会获得很久

堆空间

V8会把分为两个部分

  1. 新生代
    • 存放的是生存时间短的
    • 1~8M容量
    • 副垃圾回收器,负责新生代的垃圾回收
  2. 老生代
    • 存放时间久的对象
    • 主垃圾回收器,负责老生代的垃圾回收

副垃圾回收器

Scavenge算法来处理,九十八新生代区域分为两个区域:对象区域空闲区域
当对象区域要被写满的时候,就做一次垃圾清理操作:

  1. 首先对对象区域的垃圾做标记
  2. 然后把这些对象复制一份然后,有序的丢到空闲区域
  3. 然后复制结束后,两者角色互换

副垃圾回收器采用对象晋升策略:移动那些经过两次垃圾回收依然还活着的对象到老生代中

主垃圾回收器

负责老生代的垃圾回收,除了新生代晋升的,大的对象也会直接被存到老生代中,有两个特点:

  1. 活得久
  2. 大得很

标记|清除 算法

  1. 标记过程:从根元素遍历,能够达到的为活动对象,没有达到的为非活动对象
  2. 垃圾清理过程:主回收器直接把非活动对象清除

标记 - 整理

  1. 标记可回收
  2. 清除:不是对可回收对象清理,二十让所有的活动对象向一端移动,清理掉除了这里以外的内存。

18. 内存问题

内存泄漏: 不需要的内存数据但是依然被其他对象引用着。

常见的: 函数中声明变量时没有用var、let、const导致this指向window,直接赋值到window对象中。

19. 作用域

3大类:

  1. 声明式:函数作用域、module作用域
  2. 对象
  3. 全局

声明式作用域

通过var/const/let/class/module/import/function生成,常说的ES6块级作用域和函数作用域都属于函数式作用域

全局作用域

全局对象使用两个ER管理:

  1. 对象ER:将变量存储在全局对象中,顶层作用域下,varfunction声明的变量被绑定在对象ER中(浏览器为window
  2. 声明式ER:使用内部对象来存储,顶层作用于下,const/let/class声明的变量被绑定在声明ER中

两者存在相同名的变量时,声明式ER优先级更高。

20. this

this是和上下文绑定的,主要分为三种:全局执行、函数执行、eval执行

全局

一般指向window对象,这是this和作用域链唯一焦点,作用域低端包含window对象

函数

默认情况调用一个函数,他的上下文也是指向window

通过call/bind/apply设置上下文:

let bar = {
  myName : " mango ",
  test1 : 1
}
function foo(){
  this.myName = " test "
}
foo.call(bar)
console.log(bar) // mango
console.log(myName)  // not defined

this的设计缺陷

嵌套函数中,内部函数的this不会从外层函数中继承
解决方法:

  1. 将this转化为作用域体系。
  2. 使用ES6中的this函数,他不会创建自己的上下文,所以取决于他的外部函数。

普通函数中的 this 默认指向全局对象 window

可以设置严格模式,函数上下文都是undefined

21. 图片懒加载

通过HTML提供的data-属性来嵌入自定义数据。
然后利用offsetTop计算位置。当图片出现在可视区域的时候,加载图片,也就是给img标签设置src

22. 数组常用方法

改变原数组

  1. push
  2. pop
  3. shift
  4. unshift
  5. reverse
  6. sort
  7. splice

不会改变

  1. concat
  2. join
  3. slice
  4. filter
  5. reduce
  6. find
  7. findIndex

文章来源:https://blog.csdn.net/weixin_44694641/article/details/135375159
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。