易忘,但常问的面试题(二)

11、 map、forEach 和 for 的区别

map方法

map 方法用于对数组中的每个元素进行操作,并返回一个新的数组,该数组包含原数组中每个元素操作后的结果。它不会改变原数组,而是返回一个新的数组。

forEach方法

forEach 方法用于对数组中的每个元素执行一个函数,但不会返回任何值。它会遍历数组中的每个元素,并对其执行提供的函数。与 map 方法不同,forEach 方法不会创建新的数组,而是直接在原数组上进行操作。

for 循环

for 循环是一种通用的迭代结构,它可以用于迭代任何可迭代对象(如数组、字符串等)。它通过指定迭代的起始索引、结束索引和迭代步骤来遍历可迭代对象中的每个元素。

三者的性能比较:for > forEach > map

for:for循环没有额外的函数调用栈和上下文,所以它的实现最为简单。
forEach:对于forEach来说,它的函数签名中包含了参数和上下文,所以性能会低于 for 循环。
mapmap 最慢的原因是因为 map 会返回一个新的数组,数组的创建和赋值会导致分配内存空间,因此会带来较大的性能开销。

for循环是js提出时就有的循环方法。forEachMap是ES5提出的,挂载在可迭代对象原型上的方法。

12、ES6模块化和CommonJS的区别

  1. 语法差异:
  • ES6 模块化: 使用 importexport 语法。
js 复制代码
// 导入
import { myFunction } from "./myModule";

// 导出
export const pi = 3.14;
export function square(x) {
  return x * x;
}
  • CommonJS: 使用 requiremodule.exports 语法。
js 复制代码
// 导入
const myModule = require("./myModule");

// 导出
exports.pi = 3.14;
exports.square = function(x) {
  return x * x;
};
  1. 加载时机:

    • ES6 模块化: 在编译阶段静态分析,加载时可以进行优化。
    • CommonJS: 在运行时加载,是同步加载模块的方式。
  2. 模块值的拷贝:

    • ES6 模块化: 模块值是动态映射关系,只读视图,通过实时的映射关系来获取值。
    • CommonJS: 是值的拷贝,一旦加载,被加载模块的值变化不会影响导入模块。
  3. 导出方式:

    • ES6 模块化: 导出的是值的引用,修改导入值会影响到导出值。
    • CommonJS: 导出的是值的拷贝,修改导入值不会影响到导出值。
  4. 适用场景:

    • ES6 模块化: 主要用于浏览器端和现代前端开发,支持异步加载。
    • CommonJS: 主要用于服务器端,是同步加载模块的一种较为简单的方式。
  5. ES6 模块化的静态特性:

    • ES6 模块化在编译时可以静态分析模块之间的依赖关系,这使得一些工具(如 tree shaking)能够更好地优化代码,去除未使用的部分。

总的来说,ES6 模块化在语法上更加优雅,支持静态分析,异步加载等特性,适用于现代前端开发;而 CommonJS 更适用于服务器端的模块化开发。

13、 深浅拷贝的区别

浅拷贝复制对象的引用,深拷贝创建一个新对象并递归复制所有嵌套的对象

浅拷贝:

  • 浅拷贝仅复制对象或数组的第一层结构,对于嵌套的对象或数组,仅复制引用而不是实际的对象或数组。
  • 常见的浅拷贝方法有 Object.assign()、扩展运算符(...)、Array.slice() 等。
js 复制代码
   const originalArray = [1, 2, { a: 3 }];
   const shallowCopy = [...originalArray];
   
   originalArray[0] = 100;
   originalArray[2].a = 99;
   
   console.log(originalArray); // [100, 2, { a: 99 }]
   console.log(shallowCopy); // [1, 2, { a: 99 }]

上面代码中,说明shallowCopy仅复制originalArray数组的第一层结构,其他深层次结构则复制的是引用,对shallowCopy中数据进行修改时,shallowCopy 数组中除第一层结构以外的数据,都会被修改(通俗的说,就是除开第一层结构是自己的,其他层都是别人的,别人想改就改)。

深拷贝:

  • 深拷贝会递归地复制对象或数组及其所有嵌套的对象或数组,生成一份完全独立的副本。
  • 深拷贝能够解决浅拷贝中引用传递的问题,确保复制的对象和原对象互不影响。
  • 常见的深拷贝方法有使用递归、JSON.parse(JSON.stringify())、第三方库如 lodash 的 _.cloneDeep() 等。
js 复制代码
   const originalArray = [1, 2, { a: 3 }];
   const deepCopy = JSON.parse(JSON.stringify(originalArray));

   originalArray[2].a = 99;

   console.log(deepCopy); // [1, 2, { a: 3 }]

深拷贝开辟了一个新的空间,将数据复制进去,进行处理操作时,与原数据互不影响。

浅拷贝实现方式:

  • Object.assign()

    js 复制代码
    const shallowCopy = Object.assign({}, originalObject);
  • 扩展运算符(...)

    js 复制代码
    const shallowCopy = [...originalArray];
  • Array.slice()

    js 复制代码
    const shallowCopy = originalArray.slice();

深拷贝实现方式:

  • 递归实现(递归实现深拷贝可能会因为循环引用而陷入死循环)

    js 复制代码
    function deepClone(obj) {
      if (obj === null || typeof obj !== 'object') {
        return obj;
      }
    
      const clone = Array.isArray(obj) ? [] : {};
    
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          clone[key] = deepClone(obj[key]);
        }
      }
    
      return clone;
    }
  • JSON.parse(JSON.stringify())(不能复制函数、正则表达式等特殊对象)

    js 复制代码
    const deepCopy = JSON.parse(JSON.stringify(originalObject));
  • 第三方库,如 lodash

    js 复制代码
    const deepCopy = _.cloneDeep(originalObject);

14、 var、let、const的区别

varletconst 是 JavaScript 中用来声明变量的关键字,它们的区别如下:

  1. 作用域:
  • var:函数作用域,在函数内部声明的 var 变量可以在整个函数内部使用。
  • let:块级作用域,在代码块(花括号)内部声明的 let 变量只能在该代码块内部使用。
  • const:块级作用域,与 let 类似,但声明的变量的值不能被修改。
  1. 变量提升:
  • var:存在变量提升,即在声明变量之前就可以使用该变量,但是值为 undefined
  • letconst:不存在变量提升,必须先声明再使用,否则会抛出 ReferenceError 异常。
  1. 可修改性:
  • varlet:声明的变量的值可以被修改。
  • const:声明的变量的值不能被修改,必须在声明时或在构造函数中进行初始化。
  1. 暂时性死区(Temporal Dead Zone,TDZ):
  • letconst:在它们所声明的块级作用域内,存在暂时性死区。在 TDZ 中,不能访问该变量,也不能修改该变量的值。
  1. 全局对象属性:
  • 使用 var 声明的变量,会成为全局对象的属性,前提是没有在函数内部或块级作用域中声明该变量。
  • 使用 let 声明的变量不会成为全局对象的属性。
  • 使用 const 声明的变量不会成为全局对象的属性,但如果声明同时进行初始化,会在全局对象上创建一个只读属性。

15、 介绍下 BFC 及其应用

BFC(Blcok formatting context) 直译为"块级格式化上下文"。他是一个独立的渲染区域,只有块级元素参与,它规定了内部块级元素的布局,并且与这个区域外部毫不相关,外部元素也不会影响这个渲染区域的元素。

简单说:BFC 就是页面上的一个隔离的独立渲染区域,区域里边的子元素不会影响到外面的元素。外边的元素也不会影响到区域里面的子元素。

以下是一些常见的创建 BFC 的方法:

  1. 浮动元素:将一个元素设置为浮动(float: leftfloat: right)会创建一个 BFC。
  2. 绝对定位元素:将一个元素设置为绝对定位(position: absolute)会创建一个 BFC。
  3. 固定定位元素:将一个元素设置为固定定位(position: fixed)会创建一个 BFC。
  4. 具有 overflow 属性的元素:将一个元素的 overflow 属性设置为非 visible 的值(例如 overflow: hiddenoverflow: autooverflow: scroll)会创建一个 BFC。
  5. 具有 display 属性为 inline-blocktable-celltable-captionflex 的元素:这些元素会自动创建一个 BFC。
  6. 根元素(html 元素):根元素始终是一个 BFC。

BFC 可以解决那些问题

  1. 避免垂直方向的margin合并。问题:垂直方向上的,两个元素margin相遇,两元素间的距离并不等于两个margin之和。而是等于最大的margin。小的margin会被大的margin吞并。
  2. 清除浮动元素的影响。问题:如果父元素包含一个浮动元素,那么其他元素可能会受到浮动元素的影响,导致布局混乱。但是给父元素变成BFC,浮动元素对其他元素的布局不再产生影响。
  3. 防止高度塌陷。问题:父元素不写高度时,子元素浮动后,导致父元素会发生高度塌陷(造成父元素高度为0)。但是将父元素变成BFC,就不会造成高度塌陷,最简单的方法是,给父元素设置overflow: hidden属性。
  4. 垂直布局。问题:父元素包含多个子元素,并且这些子元素的高度不同,那么在没有创建 BFC 的情况下,这些子元素可能会在垂直方向上重叠。但是将父元素变成BFC,子元素在垂直方向上就能正确排列。
相关推荐
掘金者阿豪8 分钟前
把业务数据变成共享仪表盘:Metabase可视化与远程访问实践
前端·后端
kyriewen29 分钟前
折腾了半年 AI 编程工作流,最后发现效率瓶颈是桌上那块屏幕
前端·javascript·ai编程
蜗牛前端1 小时前
codex 全流程开发上线的高颜值礼簿小程序
前端·微信小程序
大龄秃头程序员2 小时前
我在图文流 App 里落地双层缓存、弱网降级与 OOM 治理
前端
老王以为2 小时前
React Renderer 分离的多平台架构
前端·react native·react.js
hunterandroid2 小时前
Kotlin Coroutines 与 Flow:让异步任务更清晰
前端
Bigger2 小时前
从零搭建 AI 代码审查服务:一份前端也能看懂的 Python 学习笔记
前端·ci/cd·ai编程
lichenyang4533 小时前
JSAPI、NAPI、Biz、Imp:ASCF Demo 如何真正调用系统能力和 C++ 能力
前端
自由路飞3 小时前
RAG 混合检索深挖:BM25 和向量分数为什么不能直接相加?
面试
lichenyang4533 小时前
IPC、JSVM、UIThread、libuv:ASCF 架构图里最容易混的几个词
前端