笔记打算记录,自己整理好的面试题,
争取保持准确性和专业性,然后用直白的语言描述出来,
希望能做成一个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],通常用于查询动态属性(需要通过编译运行后才知道)
如何能获取对象的所有属性,包括
enumerable为false的属性
- 使用
Object.getOwnPropertyNames(对象) - 使用
Reflect.ownKeys(对象) - 传统的
for...in和Object.keys无法访问到对象中不可枚举的属性
for.of 和 for.in 的理解
for...in 常用于遍历对象,但仅能展示(可枚举)的属性
如果需要遍历全部属性,包括可枚举和不可枚举的,可使用以下两个方法
Object.getOwnPropertyNames(对象)Reflect.ownKeys(对象)
for...of 常用于循环具有 iterator 的变量,常见为数组和类数组。
如何判断变量是否可以使用 for.of,获取变量的[symbol.iterator],如果返回的是一个函数function,则表示可使用。
闭包的理解
有一个函数可以访问到其外部函数的作用域,这样的组合称之为闭包。闭包有什么优点:
- 使用闭包可以创建私有变量
- 可以延长变量的生命周期(未被引用也不会被垃圾回收)
try...catch 的理解
- 捕获不到
promise.reject和setTimeout的错误回调(只能捕获同步代码) - 将异步代码
promise,调整为async/await的模式,可以成功捕获到错误回调,因为await会阻塞代码。(await会自动将后面的代码转换为then回调)
深拷贝的理解
因为在js中,对象类型的变量是引用的机制,所以对于这种变量的复制,会导致新变量修改属性时,影响到旧的变量。深拷贝就是用于解决这种情况,复制变量并且断开变量之间的引用关联。
常用的深拷贝方法有2种:
JSON.parse
使用JSON.parse(JSON.stringify(变量))进行深拷贝时,针对复制变量中的部分value属性会有特殊的转换。
Symbol类型会丢失function函数会丢失undefined会丢失Date变量会自动转成字符串(iso字符串:指的是0时区)正则表达式会变成空的对象
手写的 deepClone方法
- 多种不同类型的变量识别
- 当变量嵌套的比较深时,如何进行循环赋值
(代码可查看【常见手写代码题】中的手写深拷贝)
map 和 weakmap 的区别
- map
map的键值对,key支持任何的类型变量
- weakmap
可以理解为特殊的map,但是 weakmap有自己的特点:
key值只能是对象类型的变量或者非全局定义的symbol- 不支持全局遍历的API方法。例如:
forEach、entries,只有传统的增删改查 - 不会阻止作为
key值对象的垃圾回收,当对象没有引用时,允许被回收
箭头函数 和 普通函数的区别
- 箭头函数的 this 在创建时就被确定,后续无法被 call/apply 修改
- 不可以作为构建函数(不能使用new构建)
- 没有 prototype/argument 参数
事件循环的理解
JS中事件循环分为:同步任务和异步任务。
异步任务分为宏任务和微任务,微任务会在当前的事件循环周期执行,下一个周期执行宏任务。微任务优先级比宏任务高,(如果在执行多个宏任务时,宏任务内部有出现新的微任务,微任务会插队,插在下一个宏任务前优先执行)
- 宏任务:
setTimeout/setInterval/setImmediate(node环境使用) - 微任务:
promise/mutation Observer/process.nextTick/async+await
优先级:
- 同时出现时,setTimeout 优先级高于 setImmediate
- process.nextTick(node环境使用) 优先级高于 promise
async/await:await下面的代码,会自动被封装成一个微任务执行
promise 的理解
异步编程的一种解决方案,Promise是一个构造函数,提供了统一的API方法
promise的实例有三个状态:pending / resolved(fulfilled) / rejected。当状态从 pending 切换到其他状态,就不会再改变。(状态改变只能是通过内部驱动的,外部无法改变)
- 缺点:
- 无法中途取消 promise
- 当 promise 处于
pending状态时,外部无法知道其当前的进度
衍生:
promise.resolve和promise.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
- push
- pop
- shift
- unshift
- sort
- splice
- reverse
新版本的JavaScript,提供了几个新的API,既能实现效果又不影响原数组(使用的方法和原来的API一样)
- toSpliced
- toSorted
- toReversed
JS为什么是单线程
(等待完善)