小笔记(js 篇)

71 阅读7分钟

笔记打算记录,自己整理好的面试题,
争取保持准确性和专业性,然后用直白的语言描述出来,
希望能做成一个API文档,让我日后翻到能直接对着复习就好,
如果也能帮助到你就最好了(我会不定时的更新的)

JS中基本数据类型常见的有哪些

  • string
  • number
  • boolean
  • undefined
  • null
  • symbol

衍生:对symbol的理解

  • 每一个普通的symbol变量都不相同。例如定义2个变量Symbol('A'),这2个变量是不相等的
  • 如果想要不同的symbol变量相同,使用Symbol.for('AA')进行定义
  • symbol有一个API方法:Symbol.keyFor(symbol变量),会返回变量定义的key值。但这个API只能返回通过Symbol.for(参数)定义的变量,普通的变量返回undefined

常见的数据类型获取方法

  • typeof
  • instanceof
  • Object.prototype.toString.call(变量),返回[object, 变量类型信息],配合手动截取判断

异步加载JS的理解

  • defer

文档识别到script标签中,针对defer关键字的资源文件,会默默下载不阻塞页面加载,等到文档流的最后统一解析(按照顺序进行加载)

  • async

文档识别到script标签中带有async关键字的资源文件,同样也是异步加载(即文档和文件并行下载)。但是当文件成功下载后,会即刻运行文件(运行时存在阻塞的风险,而且多个async文件加载的顺序不可测)

对象变量访问的理解

点操作符和中括号操作符有什么区别

  • 点操作符:访问对象变量.a,通常用于查询静态属性(已存在)
  • 中括号操作符:访问对象变量[变量A],通常用于查询动态属性(需要通过编译运行后才知道)

如何能获取对象的所有属性,包括enumerablefalse的属性

  • 使用Object.getOwnPropertyNames(对象)
  • 使用Reflect.ownKeys(对象)
  • 传统的for...inObject.keys无法访问到对象中不可枚举的属性

for.of 和 for.in 的理解

for...in 常用于遍历对象,但仅能展示(可枚举)的属性

如果需要遍历全部属性,包括可枚举不可枚举的,可使用以下两个方法

  • Object.getOwnPropertyNames(对象)
  • Reflect.ownKeys(对象)

for...of 常用于循环具有 iterator 的变量,常见为数组和类数组。

如何判断变量是否可以使用 for.of,获取变量的[symbol.iterator],如果返回的是一个函数function,则表示可使用。

闭包的理解

有一个函数可以访问到其外部函数的作用域,这样的组合称之为闭包。闭包有什么优点:

  1. 使用闭包可以创建私有变量
  2. 可以延长变量的生命周期(未被引用也不会被垃圾回收)

try...catch 的理解

  1. 捕获不到promise.rejectsetTimeout 的错误回调(只能捕获同步代码)
  2. 将异步代码promise,调整为async/await的模式,可以成功捕获到错误回调,因为await会阻塞代码。(await会自动将后面的代码转换为then回调)

深拷贝的理解

因为在js中,对象类型的变量是引用的机制,所以对于这种变量的复制,会导致新变量修改属性时,影响到旧的变量。深拷贝就是用于解决这种情况,复制变量并且断开变量之间的引用关联。

常用的深拷贝方法有2种:

JSON.parse

使用JSON.parse(JSON.stringify(变量))进行深拷贝时,针对复制变量中的部分value属性会有特殊的转换。

  1. Symbol类型会丢失
  2. function函数会丢失
  3. undefined会丢失
  4. Date变量会自动转成字符串(iso字符串:指的是0时区)
  5. 正则表达式会变成空的对象

手写的 deepClone方法

  1. 多种不同类型的变量识别
  2. 当变量嵌套的比较深时,如何进行循环赋值

(代码可查看【常见手写代码题】中的手写深拷贝)

map 和 weakmap 的区别

  • map

map的键值对,key支持任何的类型变量

  • weakmap

可以理解为特殊的map,但是 weakmap有自己的特点:

  1. key值只能是对象类型的变量或者非全局定义的symbol
  2. 不支持全局遍历的API方法。例如:forEachentries,只有传统的增删改查
  3. 不会阻止作为key值对象的垃圾回收,当对象没有引用时,允许被回收

箭头函数 和 普通函数的区别

  1. 箭头函数的 this 在创建时就被确定,后续无法被 call/apply 修改
  2. 不可以作为构建函数(不能使用new构建)
  3. 没有 prototype/argument 参数

事件循环的理解

JS中事件循环分为:同步任务和异步任务。

异步任务分为宏任务微任务,微任务会在当前的事件循环周期执行,下一个周期执行宏任务。微任务优先级比宏任务高,(如果在执行多个宏任务时,宏任务内部有出现新的微任务,微任务会插队,插在下一个宏任务前优先执行)

  • 宏任务:setTimeout / setInterval / setImmediate(node环境使用)
  • 微任务:promise / mutation Observer / process.nextTick / async+await

优先级

  • 同时出现时,setTimeout 优先级高于 setImmediate
  • process.nextTick(node环境使用) 优先级高于 promise

async/awaitawait下面的代码,会自动被封装成一个微任务执行

promise 的理解

异步编程的一种解决方案,Promise是一个构造函数,提供了统一的API方法

promise的实例有三个状态:pending / resolved(fulfilled) / rejected。当状态从 pending 切换到其他状态,就不会再改变。(状态改变只能是通过内部驱动的,外部无法改变)

  • 缺点:
  1. 无法中途取消 promise
  2. 当 promise 处于 pending 状态时,外部无法知道其当前的进度

衍生:promise.resolvepromise.reject的理解

这2个promise的API,本质是返回一个promise对象,所以当我们执行promise.resolve(参数),其实是执行一个常规的promise方法。

  • 当参数是一个reject函数,例如promise.resolve(promise.reject(参数)),这样会被拦截到catch里被捕获。
  • 反之promise.reject(参数),无论参数是什么,都会被拦截到catch

衍生:promise.all(参数) 传入参数的理解

大部分API提到,promise.all的入参是一个 promise[](数组),但是不够全面,只要入参是一个iterator可遍历循环的迭代器,数组或者类数组就可以。

  • 使用promise.all时,参数中只要出现reject,会马上中断整个流程,并跳转至catch。如果想使用all的特性又不想出现中断的情况,可以使用promise.allSettled

衍生:如何判断变量是否为 iterator迭代器

获取变量的[symbol.iterator],如果返回的是一个函数function,则表示当前变量是一个可循环的迭代器。

mutation observer 的理解

//new实例时,接受一个回调,参数返回一个数组,可以判断观察元素是否变化
//当有JS DOM操作结束后,会触发回调

--- 等待完善

判断当前变量是否为数组

常见的几种方法

var arr = [];

//使用instanceof
(arr instanceof Array); //true

//使用数组原型方法
Array.isArray(arr)

//原型链
arr.constructor === Array //true

//字符串答应
Object.prototype.toString.call(arr) // [object Array]

常见的几种数组去重方法

let arr = [1,2,3,4,5,4,3,5,1,2];

//利用set
new Set(arr);

//使用filter
arr.filter((item, index) => arr.indexOf(item) === index)

//写循环函数(可多种发散)
let newArr = [];
arr.forEach((item) => {
  if (newArr.includes(item) === false) {
    newArr.push(item);
  }
})

数组常见API的理解

  • 自动过滤无效值
let arr = [1, 2, false, null, 0, "", undefined, NaN, " "]
//使用filter,参数是Boolean (自动过滤掉空值类型的变量)
let out = arr.filter(Boolean) // [1, 2, " "]
  • 空的数组调用 every some API
let arr = [];

//使用every 返回true
arr.every(i => i > 1);

//使用some 返回false
arr.some(i => i > 1);
  • 数组 slice 方法的理解:
//slice方法接收 2个 参数,slice(start, end)
//截取的逻辑是包括 start的索引,不包括end的索引 -> [start, end)的这种逻辑
//使用slice不会影响到原数组(但数组内部的数据是对象,简单slice后,修改数据会影响)

//数组slice方法
let arr = [1,2,3,4];

arr.slice(0, 1) // [1]
arr.slice(0) // [1,2,3,4]
//负数就是从尾巴往前数
arr.slice(0, -1) // [1,2,3]
  • 常用会改变原数组的API
  1. push
  2. pop
  3. shift
  4. unshift
  5. sort
  6. splice
  7. reverse

新版本的JavaScript,提供了几个新的API,既能实现效果又不影响原数组(使用的方法和原来的API一样)

  1. toSpliced
  2. toSorted
  3. toReversed

JS为什么是单线程

(等待完善)