34.arguments
- 在JavaScript函数内部,arguments对象是一个类数组对象,它存储了函数被调用时传递的所有参数。
- 动态参数列表:arguments对象可以接受任意数量的参数。这意味着你可以在调用函数时传递任意数量的参数,而不需要在函数定义中明确指定形式参数的个数。
- 通过索引访问参数:可以通过arguments对象的索引来访问函数的参数。例如,arguments[0]表示第一个参数,arguments[1]表示第二个参数,以此类推。
- 类数组对象:虽然arguments对象看起来像一个数组,但它并不是一个真正的数组。它没有数组特有的方法,如push()和pop(),但可以通过length属性获取参数的数量。
35.什么是纯函数
- 返回值取决于参数
- 不依赖于外部状态,也不改变外部状态的函数
36.匿名函数(IIFN)
- 匿名函数,即没有名称的函数
- 如果单独只写一个匿名函数,此时是不符合语法要求的 会报错。需要给 匿名函数包裹一个括号,使之成为表达式。
- 被小括号包裹的内容会被js识别为一个函数表达式
37.什么是闭包函数
- 在函数内部声明一个函数,并且使用了外部函数的变量,被称为闭包函数
- 闭包函数的优点:实现私有属性和方法,减少对外界的污染;
- 闭包函数的缺点:外部函数执行完之后,内部函数不会被销毁,造成变量长期贮存,占用内存空间
- 解决闭包:使用call,apply,bind改变内部函数的this指向;内部函数改用箭头函数
- 使用场景:防抖、节流、缓存、封装私有变量、函数柯里化等
38.函数柯里化
概念:把一个接收多个参数的函数变成接收单一参数 并且返回能够接收新参数的函数
39.什么是递归函数
- 一个函数直接或间接地调用自身,称为递归。
- 常用场景:遍历属性结构、生成斐波那契数列、深拷贝
40.什么是回调函数
- 在javascript中,回调函数指的是一个被作为参数传递给另一个函数的函数;
- 回调函数本身不是异步的,但是可以通过异步机制(例如事件循环、Promises、async/await)来实现异步行为,例如定时器、延时器、axios请求、hooks、事件
41.箭头函数和普通函数的区别
- this指向。箭头函数的this在定义时就已经确定,不会在运行时改变,它继承自外围作用域的this值;普通函数的this在运行时确定,可以通过call、apply、bind方法改变。
- 原型属性。箭头函数没有原型属性;普通函数有原型属性
- 构造函数。箭头函数不能作为构造函数使用,不能通过new关键字调用;普通函数可以作为构造函数使用。
- 语法形式。箭头函数使用=>符号定义,而普通函数使用function关键字定义
- 函数体语法。箭头函数的函数体可以省略括号,但有一定的限制。如果箭头函数的函数体只有一行语句,则可以省略大括号和return关键字;如果箭头函数的函数体多于一行语句,则必须使用大括号和return关键字;普通函数的函数体必须使用大括号。
- arguments对象。箭头函数不绑定arguments对象,但可以通过剩余参数代替;普通函数有arguments对象,用于存储所有传递的参数
- Generator函数。箭头函数不能作为Generator函数使用,不能使用yield关键字;普通函数可以作为Generator函数。
42.Generator函数
- Generator函数是一种特殊类型的函数,它可以在执行过程中暂停并在需要时恢复执行。当Generator函数被调用时,它并不会立即执行,而是返回一个遍历器对象(Iterator),这个对象可以逐步遍历Generator函数的内部状态。
- Generator函数使用步骤
- Generator 函数使用
function*
声明,通过yield
关键字产生一个值,并挂起函数的执行。 - 可以通过调用
iterator.next()
方法逐一遍历迭代器对象,也可以为其传参,执行后标记为未完成。 - 当运行到
iterator.return()
语句时,迭代器对象会被标记为“完成”,不再产生新的值。
- Generator 函数使用
- iterator对象的方法:
- next()逐一遍历迭代器对象
- return()返回return的对象同时停止迭代
- throw()中断迭代
- 主要用途包括:解决回调地狱、迭代和生成无限序列、替代回调函数。
function
* generator() {
yield
'one'
;
yield
'two'
;
return
'done'
;
}
const iterator = generator(); //
返回一个遍历器对象
console.log(iterator.next());
//{ value: 'one', done: false }
console.log(iterator.next());
//{ value: 'two', done: false }
console.log(iterator.next());
//{ value: 'done', done: true }
43.防抖和节流
- 防抖(debounce)和节流(throttle)是性能优化中常用的两种技术,用以控制函数执行的频率,以减少计算资源的使用。
- 防抖:指的是在一定时间内,对于频繁触发的事件,重新计算时间,只让其最后一次触发时执行。例如搜索框、页面改变大小。
- 节流:指的是在一定时间内,对于频繁触发的事件,只让其执行一次,例如:触底加载、页鼠标不断点击时。
44.引用类型和值类型的区别
值类型 | 基本数据类型 | |
---|---|---|
存储位置 | 栈(stack) | 指针存放在 栈(stack)、实体存放在 堆(heap) |
数据大小 | 固定 | 不固定 |
占用空间 | 小 | 大 |
存取速度 | 块 | 慢 |
数据类型 | 简单 | 复杂、可嵌套 |
访问方式 | 按值访问 | 按指针访问 |
45.什么是深拷贝和浅拷贝
- 深拷贝。只复制对象内容,不复制对象的指针,拷贝出来的对象是一个全新的对象,修改这个新对象的时候,不会影响源对象。
- 浅拷贝。只复制对象的指针,不复制对象的内容,拷贝出来的新对象指针还是指向原来的对象。修改这个新对象时,原来的对象也会被修改。引用类型的数据,默认都是浅拷贝。
46.深拷贝的方法
- JSON.stringfy(JSON.parse())
- 取不到值为 undefined 的 key;
- 如果对象里有函数,函数无法被拷贝下来;
- 无法拷贝copyObj对象原型链上的属性和方法;
- 展开运算符:只能进行浅层的拷贝
- Object.assign :只能进行浅层的拷贝
- 递归实现深拷贝
- 在递归过程中,需要记录已经遍历过的对象。
- 需要处理不同数据类型(如数组和对象)。
- 使用插件实现
- lodash的cloneDeep函数
- Immutable.js生成不可变数组或对象(Map不可变对象 List不可变数组)
47.什么是同步任务和异步任务
- js是一门单线程语言,单线程意味着所有任务都需要排队,只有前一个任务执行完之后,才能执行下一个任务。这种情况显然是不合理的,比如说我挂起一个30秒的延时器,那么后面的任务就会被阻塞,导致cpu空闲且无法执行其他任务。针对这一情况,javascript把这些阻塞进程的任务统一放在了异步任务队列中等待执行,任务队列通知主线程某个异步任务可以执行了,这个异步任务就会被放在主线程中按顺序执行。
- 同步任务(synchronous)就是主线程中的任务,按照顺序一个个的执行,比如:普通函数、new Promise()、console.log()等等
- 异步任务 (asynchronous) 就是任务队列中的、会阻塞到进程的任务,只有“任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。比如:DOM 事件回调、定时器回调、网络请求的回调、promise.then()、promise.catch()的回调。异步任务又分为宏任务和微任务:
- 宏列队 macrotask queue:用来保存待执行的宏任务(回调),比如:DOM 事件回调、定时器回调、网络请求的回调
- 微列队 microtask queue:用来保存待执行的微任务(回调),优先级高于宏任务,比如:promise.then()、promise.catch()、MutationObserver 、queueMiscrotask()
48.宏任务和微任务的区别
- 执行优先级不同。宏任务的优先级相对较低,而微任务的优先级较高。
- 执行时机不同。宏任务会被添加到事件队列中,在每个事件循环中执行一次;微任务会在当前宏任务执行完毕后立即执行,而不会添加到事件队列中,微任务的执行时机是在当前宏任务的末尾,在下一个宏任务之前。
- 用途不同。宏任务通常包括定时器任务(如setTimeout、setInterval)、网络请求、用户交互事件(如点击、滚动等),这些是相对较大的任务,需要等待一定时间或特定的触发条件才会执行;微任务通常包括Promise回调、DOM变动观察器等,这些是相对较小的任务,适合在当前宏任务执行完毕后立即执行。
- 总结:宏任务是事件循环中的较大任务,通常处理用户交互、渲染等任务;微任务是较小的任务,通常用于处理异步操作的结果,由于微任务具有较高的优先级,它们可以在用户交互之前或渲染之前得到及时处理。
49.Promise的作用
- Promise的主要作用是处理异步操作。它允许你以更同步的方式编写异步代码,提高了代码的可读性和可维护性。
- 处理异步任务结果。Promise区分成功(resolve)和失败(reject)的状态,使得错误处理更加明确和直接。
- 避免回调地狱。Promise对象可以链式调用,减少嵌套回调的使用,避免代码过于复杂和难以管理。
- 支持并发操作。例如,使用Promise.all()可以并行执行多个操作,等待所有操作完成后执行某些逻辑。
- 便于代码的模块化和复用。通过将异步操作封装成Promise对象,可以提高代码的复用性和模块化程度。
50.promise的三种状态?怎么改变promise的状态
- 等待中(Pending)。这是Promise的初始状态,表示异步操作尚未开始或结果尚未确定。
- 已完成(Fulfilled)。当异步操作成功完成并返回结果时,Promise的状态变为Fulfilled。
- 已拒绝(Rejected)。当异步操作执行失败时,Promise的状态变为Rejected。
51.Promise.then和Promise.catch
- Promise函数有两个参数resolve和reject,当我们调用resolve()时,Promise函数会从等待状态变为成功状态,当我们调用reject()时,Promise函数会从等待状态变为失败状态。
.then():在promise函数中调用
resolve()后执行的函数,用于处理执行成功的结果。可以通过resolve(参数)获得参数。.catch():在promise函数中调用
reject()后执行的函数,用于处理执行失败的结果。可以通过reject(参数)获得参数。- .then和.catch都属于异步任务中的微任务。
52.promise.all和promise.race的区别
Promise.all
和Promise.race
都是用于处理多个Promise实例的方法,但它们的行为和用途有所不同。Promise.all
。接收一个Promise数组作为参数。只有所有结果都执行成功或失败的时候,才会变为成功状态或者失败状态,否则仍然会处于等待状态。如果所有Promise都执行成功,则返回所有执行成功的结果。如果有一个执行失败,就会返回第一个执行失败的结果。Promise.race
。接收一个Promise数组作为参数,返回的第一个执行成功的Promise结果。romise.all
适用于需要等待所有异步操作完成再进行下一步处理的场景,例如并行下载多个文件并将它们合并为一个文件;Promise.race
适用于需要在多个异步操作中获取最先完成的结果的场景,例如设置超时机制或不知道哪个接口响应更快的情形。
53.proimse是同步还是异步
Promise本身是同步的。Promise是一个用于异步编程的对象,它允许你以同步的方式编写异步代码,但Promise对象本身在创建时立即执行,不会造成主线程的阻塞。然而,Promise的回调函数,如then()和catch(),是异步执行的,它们会在当前脚本的所有同步任务执行完毕后调用。
54.promise.then的交替执行
如果是单个promise实例,即使有多个then,仍然会按照顺序执行。如果是多个promise实例同时调用.then,then会出现交替执行的情况。这个是编译器做的优化,主要是为了避免某一个promise占用的时间太长。
55.怎么解决回调地狱
- promise实现链式调用
- generator函数
- async/await