每日五道前端面试题--day7


以下题目来自掘金等其它博客,但是问题的答案都是根据笔者自己的理解做出的。如果你最近想要换工作或者巩固一下自己的前端知识基础,不妨和我一起参与到每日刷题的过程中来,如何?

第七天要刷的面试题如下:

  1. 什么是DOM和BOM?
  2. 什么是尾调用,使用尾调用有什么好处?
  3. use strict是什么?使用与否有什么区别?
  4. for…in和for…of的区别?
  5. 如何使用for…of遍历对象?

下面是我自己的理解:

1. 什么是DOM和BOM?

  • DOM是文档对象模型,英文名为document object model,表示将文档作为一个对像对待,而这个对象上的属性和方法主要是为了用来操作网页内容。
  • BOM是浏览器对象模型,英文名为browser object model,表示将浏览器作为一个对像对待,而这个对象上的属性和方法主要是为了用来和浏览器进行交互以及调用浏览器提供的api。
  • BOM的核心是window对象;window本身具有双重角色:
    • js访问浏览器窗口的一个接口;
    • window本身是一个Global对象;
    • 在网页中定义的任何对象,变量,函数都作为全局对象的一个属性或者方法存在;
    • window上重要的属性有:location navigation screen document等。

2. 什么是尾调用,使用尾调用有什么好处?

  • 尾调用(Tail Call)的含义就是在函数A的最后一步调用函数B
  • 注意一定是最后一步,这是可以通过合理的安排函数A的结构体实现的!
  • 原理:代码执行时基于执行栈的,所以在一个函数中调用另一个函数的时候,会保留调用者A的执行上下文保留直到函数A执行完毕!),然后将被调用的函数B的执行上下文加入到执行栈
  • 显而易见的是,如果函数之间的相互调用层数过深,就会导致很多的函数执行上下文都被加入到执行栈中,导致栈的溢出!
  • 采用尾调用优化之后的函数,根据定义,函数A只有可能在最后一步才调用函数B,既然是最后一步那么js引擎就无需将A的执行上下文保存在执行栈中了!
  • 于是节省了内存
  • 需要注意的是:尾调用优化只有在严格模式下才会生效

不使用尾调用优化的斐波那契数列

function factorial(n) {
  if (n <= 1) {
    return 1;
  }
  
  // 非尾调用:递归调用后仍需乘以 n
  return n * factorial(n - 1);
}

使用尾调用优化的斐波那契数列

function factorialTail(n, acc = 1) {
  if (n <= 1) {
    return acc;
  }
  
  // 尾调用优化:传递累积的结果而不进行额外的乘法操作
  return factorialTail(n - 1, n * acc);
}

3. use strict是什么?使用与否有什么区别?

  • 本质上是一段字符串,直接写在作用域顶部
  • 如果是在ES5之下的js版本中使用,值会被当成是一个普通的字符串常量处理,并不会触发严格模式
  • 是ECMS5引入的一种运行模式,又称为严格模式
  • 作用:
    • 消除js中语法的不合理、不严谨的地方,减少怪异的行为
    • 提升了效率(优化变量接卸,禁止意外的全局变量的创建)
    • 消除了js中的不安全的行为
    • 为新版本js的出现做铺垫
  • 区别在于:
    • 禁止了with语句的使用
    • 禁止this默认指向全局对象的特性
    • 对象不能有重名的属性
  • 作用域:
      1. 函数作用域
      1. 全局作用域

4. for…in和for…of的区别?

从以下对象、顺序、机制、迭代值四个角度阐述其区别是比较合适和全面的

  • 迭代的对象类型:for…in 用于迭代对象的可枚举属性,而 for…of 用于迭代可迭代对象(比如数组、字符串、Set、Map 等)的元素。
  • 迭代顺序:for…in 循环以任意顺序迭代对象的属性,对于数组或字符串,它会遍历索引或字符。而 for…of 循环按照对象的迭代协议依次迭代元素,通常按照添加顺序进行循环。
  • 遍历机制:for…in 会遍历对象及其原型链上的所有可枚举属性(包括继承的属性),需要使用 hasOwnProperty() 方法来排除继承的属性。而 for…of 只能遍历可迭代对象自身的元素,不会访问对象的属性或方法。
  • 迭代变量:for…in 循环提供一个变量来表示每个属性的键或索引。而 for…of 循环提供一个变量来表示每个元素的值

for…in和for…of是JavaScript中用于遍历数据结构的两种不同循环结构,它们在迭代对象、遍历顺序以及适用性等方面存在区别。以下是具体分析:

  1. 迭代对象

    • for…in:适用于遍历对象的可枚举属性(包括原型链上的属性)。例如,当用于数组时,它会迭代数组的索引(作为字符串)以及所有可枚举的原型属性。
    • for…of:适用于遍历实现了迭代器协议的对象,如数组、Map、Set、String等。它直接迭代元素值,不包含原型链上的属性。
  2. 遍历顺序

    • for…in:遍历顺序通常是任意的,特别是对于对象的属性名。对于数组,虽然通常按数字顺序遍历,但并不保证这一点。
    • for…of:总是按照元素的插入顺序进行遍历,这在处理有序集合时非常有用。
  3. 适用性

    • for…in:由于其遍历的是键名,因此更适合于需要操作对象属性名称的场景。然而,由于它也会遍历原型链上的属性,使用时需谨慎以避免意外行为。
    • for…of:由于直接迭代元素值,更适合于需要直接访问元素内容的场景,如数组或字符串的字符遍历。
  4. 性能考量

    • for…in:可能会因为遍历原型链上的属性而影响性能,尤其是在大型对象或频繁操作时更为明显。
    • for…of:通常提供更好的性能,特别是在处理大量数据时,因为它只关注实际的元素值。

总的来说,for…in和for…of各有优势和适用场景。选择哪种方法取决于具体的使用需求和上下文环境。理解它们之间的区别有助于编写更高效、更清晰的代码。

for...infor...of 都是 JavaScript 中的循环结构,但它们有一些区别:

  1. 遍历的对象不同:for...in 用于遍历对象的可枚举属性(包括原型链上的属性),而 for...of 用于遍历可迭代对象(如数组、字符串、Map、Set 等)的元素。

  2. 循环变量的值不同:for...in 中,循环变量的值为对象的键名(或索引),而 for...of 中,循环变量的值为可迭代对象的元素值。

举例说明:

// 使用 for...in 遍历对象
const obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
  console.log(key); // 输出:a、b、c
}

// 使用 for...of 遍历数组
const arr = [1, 2, 3];
for (let value of arr) {
  console.log(value); // 输出:1、2、3
}

5. 如何使用for…of遍历对象?

有两种方式,对于类数组对象,将其转换成真正的数组之后使用for.of遍历其中的元素;另外一个是实现[Symbol.iterator]接口:

使用方式一

const obj = {
    0: 'one',
    1: 'two',
    length : 2
}
for(let i of Array.from(obj)){}

使用方式二

const obj = {
    _count: 0,
    [Symbol.iterator]: function(){
        return {
            next(){
                return {
                    value: obj._count++,
                    done: false,
                }
            }
        }
    }
}

下面是可迭代对象实现的[Symbol.iterator]接口的格式:

interface Iterable<T> {
  [Symbol.iterator](): Iterator<T>;
}

interface Iterator<T> {
  next(value?: any): IteratorResult<T>;
}

interface IteratorResult<T> {
  value: T;
  done: boolean;
}

以下题目来自掘金等其它博客,但是问题的答案都是根据笔者自己的理解做出的。如果你最近想要换工作或者巩固一下自己的前端知识基础,不妨和我一起参与到每日刷题的过程中来,如何?

第八天要刷的面试题如下:

  1. 判断对象a和类A之间的关系
  2. 数组中和遍历相关的方法
  3. 对比forEach和map
  4. 对比call和apply
  5. js中实现异步的方式

下面是我自己的理解:

1. 判断对象a和类A之间的关系

  • a instanceof A
  • a.constructor?.name === A.name
  • a.constructor === A
  • Object.getPrototypeOf(a) === A.prototype
  • A.prototype.isPrototypeOf(a)

!!Object.prototype.toString.call(a).slice(8,-1) === A.name是不可以的!

2. 数组中和遍历相关的方法

  1. forEach: forEach 方法用于遍历数组中的每个元素,并对每个元素执行提供的回调函数。它没有返回值,仅用于执行操作或副作用
  2. map: map 方法创建一个新数组,其中包含原始数组经过回调函数转换后的每个元素。它会将回调函数的返回值按照相同顺序组成新数组并返回。
  3. filter: filter 方法创建一个新数组,其中包含原始数组中满足给定条件的所有元素。它通过将每个元素传递给回调函数并根据回调函数的返回值(true 或 false)来筛选元素。
  4. for...of: for…of 是一种循环语句,用于遍历可迭代对象(如数组、字符串、Set、Map 等)的每个元素。它提供了一种简单且直观的方式来迭代和访问元素。
  5. every: every 方法用于检查数组中的所有元素是否都满足给定的条件。它会对数组中的每个元素应用回调函数,只有当所有元素的回调函数都返回 true 时,every 方法才返回 true;否则返回 false。
  6. some: some 方法用于检查数组中是否存在至少一个元素满足给定的条件。它会对数组中的每个元素应用回调函数,只要有一个元素的回调函数返回 true,some 方法就返回 true;否则返回 false。
  7. find: find 方法返回数组中满足给定条件的第一个元素。它会对数组中的每个元素应用回调函数,直到找到使回调函数返回 true 的元素,然后立即返回该元素。
  8. findIndex: findIndex 方法返回数组中满足给定条件的第一个元素的索引。它会对数组中的每个元素应用回调函数,直到找到使回调函数返回 true 的元素,然后返回该元素的索引。
  9. reduce: reduce 方法通过将数组中的每个元素依次应用回调函数来将数组减少为单个值。回调函数接收累加器和当前元素作为参数,并返回累积结果。最终的返回值是回调函数的最后一次调用结果。
  10. reduceRight: reduceRight 方法与 reduce 方法类似,不同之处在于它从数组的末尾开始迭代元素,而不是从头部开始。其他方面的行为与 reduce 相同。

3. 对比forEach和map

  • forEach: forEach 方法用于对数组中的每个元素执行一个提供的回调函数。它会遍历数组的每个元素,并为每个元素执行回调函数,但它没有返回值。forEach 通常用于执行副作用,例如修改原始数组、打印输出或其他与每个元素相关的操作;
  • map: map 方法创建一个新数组,该数组包含原始数组经过回调函数转换后的每个元素。它会遍历数组的每个元素,并将每个元素传递给回调函数进行处理,最后返回一个新数组,其中包含了每次回调函数的返回值。
  • 对于空值的处理的差异:forEach和map对空白元素可以都可以看成循环中的continue,但map返回的数组依然保留空白元素位置!
const arr = [1, null, 3, undefined];
delete arr[0];
arr.forEach(v=>console.log(v)); // null 3 undefined
arr.map(v=>String(v)); // [空白, 'null', '3', 'undefined']

4. 对比call和apply

两者的作用一摸一样,使用ES6实现这两个函数的时候,其差异仅仅体现在形参的第二个参数前面有没有展开运算符

  • apply接受两个参数,第一个指定了函数体中this的指向对象;第二个参数可以是数组或者类数组,apply会将第二个参数中的元素作为参数传递给内部函数。
  • call接受多个参数,第一个指定了函数体中this的指向对象;第二个参数之后的所有参数会作为新的参数序列传递给内部函数。

5. js中实现异步的方式

有四种方式:回调函数、Promise、Generator、async异步函数:

  • 回调函数:存在回调地狱出现的风险
  • Promise: 比回调函数嵌套要好,但是层数过深的时候仍然语义不是很明确
  • generator: 通过转移函数的执行权来实现异步功能,基于协程实现
  • 异步函数: 是generator和promise实现的一个自动执行的语法糖,所谓自动执行指的就是:如果异步函数内部执行到一个await语句的时候,这个语句返回一个promise对象,那么异步函数就会等待promise对象的状态resolve之后再继续向下执行,实现了异步函数内部异步过程的同步书写的形式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@阿猫阿狗~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值