当前位置: 首页 > article >正文

2024前端面试训练计划-高频题-JavaScript基础篇

具体内容结构(可作为回答思路)为:简略回答,详细回答

1、JavaScript有几种数据类型?

  • 简略回答

    JavaScript共有八种数据类型,分别是 Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt。

  • 详细回答

    具体来说,分为两种类型:原始数据类型和引用数据类型:

  1. 原始数据类型:
    1. string
    2. number
    3. boolean
    4. null
    5. undefined
    6. symbol
      1. 表示创建后独一无二且不可变的数据类型,它主要是为了解决可能出现的全局变量冲突的问题。
    7. bigInt
      1. 一种数字类型的数据,它可以表示任意精度格式的整数,使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。
  2. 引用数据类型:
    1. object
    2. array
    3. function

原始数据类型和引用数据类型的区别:

  1. 区别在于存储位置的不同
    1. 原始数据类型 直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;
    2. 引用数据类型 存储在堆(heap)中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
  2. 堆和栈的概念存在于数据结构和操作系统内存中
    1. 在数据结构中
      1. 栈中数据的存取方式为先进后出。
      2. 堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大小来规定。
    2. 在操作系统中,
      1. 内存被分为栈区和堆区
      2. 栈区内存由编译器自动分配释放,
        1. 存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈
      3. 堆区内存一般由开发者分配释放,
        1. 若开发者不释放,程序结束时可能由垃圾回收机制回收。

2、JavaScript中如何判断数据类型

  • 简略回答
  1. 使用typeof操作符,可以返回一个值的数据类型,它适用于除了 null 以外的所有值。
  2. 使用 instanceof 操作符,可以判断一个对象是否是某个构造函数的实例。
  3. 使用 Object.prototype.toString 方法,可以返回一个值的内部类型,它适用于所有值,包括 null 和 undefined。
  4. 还有一些其他的判断类型的方式,如 Array.isArray 判断数组类型、isNaN 判断是否为 NaN、Number.isInteger 判断是否为整数等。
  • 详细回答

3、typeof null 为什么是object

  • 简略回答

typeof null 的结果为 “object”,这是 JavaScript 语言的一个历史遗留问题。

  • 详细回答

在 JavaScript 最初的版本中,使用 32 位的值表示一个变量,其中前 3 位用于表示值的类型。

在这种表示法下,null 被解释为一个全零的指针,000 表示对象,也就是说它被认为是一个空对象引用,因此 typeof null 的结果就是 “object”。


4、深拷贝和浅拷贝的原理

  • 简略回答

浅拷贝:

  1. 浅拷贝指的是将一个对象的属性值复制到另一个对象,
  2. 如果有的属性的值为引用类型的话,那么会将这个引用的地址复制给对象,因此两个对象会有同一个引用类型的引用。
  3. 浅拷贝可以使用 Object.assign 和展开运算符来实现。

深拷贝:

  1. 深拷贝相对浅拷贝而言,如果遇到属性值为引用类型的时候,它新建一个引用类型并将对应的值复制给它,
  2. 因此对象获得的一个新的引用类型而不是一个原有类型的引用。
  3. 深拷贝对于一些对象可以使用 JSON 的两个函数来实现,但是由于 JSON 的对象格式比 js 的对象格式更加严格,所以如果属性值里边出现函数或者 Symbol 类型的值时,会转换失败
  • 详细回答

5、变量提升可能带来什么问题

  • 简略回答

1. 未经意的变量访问:

由于变量提升,变量的声明会被提升到当前作用域的顶部。这意味着在变量声明之前,变量已经存在,但其值为undefined。如果在变量声明之前使用变量,它将被视为存在但未定义,可能导致意外的行为或错误。

2. 变量覆盖:

由于变量提升,同一个作用域内的多个变量声明可能会相互覆盖。这可能导致变量值的意外改变或冲突。

3. 难以追踪变量声明:

由于变量提升,变量的声明可能分散在作用域的不同位置。这使得代码阅读和理解变得困难,因为变量的声明点与其实际使用点不一致。这可能导致代码的可读性和维护性下降。

  • 详细回答

6、暂时性死区的问题

  • 简略回答

let,const 定义变量时存在暂时性死区。在变量声明之前的区域内,变量是不可访问的。

  • 详细回答

临时性死区的意义在于引入了更严格的变量声明和使用规则, 规避变量提升带来的结构不稳定性,提高代码的可靠性和可维护性

1. 错误捕获

    1. TDZ 的存在可以帮助开发者在开发过程中捕获潜在的错误。
    2. 在变量声明之前 访问变量会在静态检查阶段引发 ReferenceError,从而提醒开发者注意变量的声明和初始化顺序。
    3. 这有助于避免使用未声明或未初始化的变量,减少代码中的潜在错误。

2. 可读性和维护性

    1. TDZ 要求在使用变量之前显式声明和初始化它们。这使得代码更易于阅读和理解,因为变量的使用点和定义点更加清晰明确。开发者可以更轻松地追踪变量的生命周期,并更好地组织和维护代码。

3. 避免变量提升问题

    1. 在ES6之前,使用var声明的变量存在变量提升的问题,可能导致变量在使用之前被意外地访问。TDZ的引入消除了变量提升,强制开发者在使用变量之前进行显式声明和初始化,从而减少了因变量提升而产生的潜在问题。

4. 规范一致性:

    1. TDZ的规则和行为在不同的JavaScript环境中是一致的,如浏览器和Node.js。这提供了跨平台和跨环境的一致性,使开发者可以编写可移植的代码,并减少由于环境差异而引起的错误。

7、用const声明的变量是否可以被改变

  • 简略回答
  1. substring() 方法接收两个参数,起始位置和结束位置。它截取从起始位置到结束位置之间的字符
  2. substr() 方法接收两个参数,起始位置和截取的字符数。它从起始位置开始截取指定数量的字符
  • 详细回答

8、JavaScript有几种数据类型?

  • 简略回答
  1. splice() 方法可以在数组中添加、删除或替换元素,并返回被删除的元素,它会改变原数组。

  2. slice() 方法是从原数组中返回指定开始和结束位置的元素组成的新数组,它不会改变原数组。

  • 详细回答

9、promise.all 和 promise.allsettled区别

  • 简略回答

promise.all 和 promise.allsettled都是promise的静态方法,用于处理多个promise对象的并发执行。

  • 详细回答

区别

1. promise.all 方法在需要所有 Promise 对象 都成功的情况下使用。

Promise.all() 方法返回的 Promise 对象在所有的 Promise 对象都 resolve 之后,才会 resolve 并返回一个由所有 Promise 返回值组成的数组。如果其中有一个 Promise 被 reject,则会立即 reject 并返回相应的错误信息。简单来说,只有所有 Promise 都成功了才算成功,有一个失败了就算失败。

2. Promise.allSettled() 方法则适用于需要知道每个 Promise 的执行结果的情况下使用。

Promise.allSettled() 方法返回的 Promise 对象在所有的 Promise 对象都 resolve 或 reject 之后,才会 resolve 并返回一个由所有 Promise 状态对象组成的数组,每个状态对象包含一个 status 字段表示 Promise 状态,和一个 value 或 reason 字段表示 Promise 的返回值或错误信息。简单来说,它会等到所有 Promise 都执行完毕,无论成功还是失败,都会把每个 Promise 的状态信息收集到一个数组里面返回。


10、new 的理解

  • 简略回答

new运算符用于创建新的对象并调用构造函数来初始化该对象

  • 详细回答

实现步骤

  1. 创建一个空对象
  2. 通过Object.create()方法将新对象的原型设置为构造函数的原型
  3. 调用构造函数,使用apply方法将this指向新对象,并传入参数
  4. 如果构造函数返回一个对象,则返回这个对象,否则返回新创建的对象

11、let全局声明变量,window能取到吗

  • 简略回答

使用 let 声明的变量不会挂在全局对象 window 上,因此无法通过 window.variableName 的方式访问。

  • 详细回答

使用 var 声明的变量不同,var 声明的变量会被挂载在全局对象上,因此可以通过 window.variableName 的方式访问。


12、javascript连续多个bind,最后this指向是什么

  • 简略回答

指向第一个绑定的对象

  • 详细回答
  1. 原因是每次bind都会返回一个新的函数,这个新函数的this值被固定为bind调用时传入的第一个参数。
  2. 但是,后续的bind调用并不会改变已经被第一个bind固定的this值。
  3. 所以,连续多个bind时,最终的this指向第一个bind方法所绑定的对象。

13、javascript 异步加载如何实现

  • 简略回答
  1. 使用asyncdefer属性

当给<script>标签添加async属性时,浏览器会在下载脚本的同时继续解析 HTML 文档。脚本下载完成后,会立即停止 HTML 解析,执行脚本,脚本执行完毕后再继续解析 HTML。

  1. 动态创建 script 标签,并设置其 src 属性为需要加载的脚本 URL。

这种方式可以通过设置 onload 或 onreadystatechange 事件来检测脚本是否加载完成。

  1. 使用 XMLHttpRequest 对象或 Fetch API 发送异步请求,并在请求成功后将响应文本解析为 JavaScript 代码,然后使用 eval() 函数或 Function() 构造函数来执行脚本。
  • 详细回答

异步加载和同步加载的区别

  1. 异步加载可以提高页面的加载速度和响应性能,避免因 JavaScript 阻塞而造成页面卡顿的情况。
  2. 异步加载可以避免因加载脚本而造成的阻塞情况,使页面的其他资源可以更快地加载和呈现。
  3. 异步加载可以更灵活地控制脚本的加载顺序和执行时间,可以根据页面需要动态加载和卸载脚本,提高页面的可维护性和可扩展性。

14、JavaScript有几种数据类型?

  • 简略回答

作用域 是指变量和函数的可访问范围。它可以控制变量的可见性和生命周期,避免命名冲突和意外修改变量值等问题。

  • 详细回答

作用域类型

  1. 全局作用域

在全局作用域中声明的变量可以在任何地方访问;

  1. 函数作用域

函数作用域中声明的变量只能在函数内部访问;

  1. 块级作用域

ES6引入了块级作用域,块级作用域指在代码块中定义的变量,使用let和const关键字声明的变量只能在块级作用域中访问。

作用域链 当程序执行到一个作用域时,它会先搜索该作用域中的变量,如果找到了就直接使用,否则就向上一级作用域继续搜索,直到找到全局作用域为止。这个 搜索过程 形成了作用域链,它保证了变量在正确的作用域中被访问和使用。


15、事件循环

  • 简略回答

浏览器的事件循环(Event Loop)可以使单线程的JavaScript代码在 等待某些操作完成时 继续执行其他任务,从而处理多个任务,实现异步编程。

  • 详细回答

事件循环是一种机制,它会不断地轮询任务队列(Task Queue),并将队列中的任务依次执行。

  • 任务可以分为两类:宏任务(Macro Task)和 微任务(Micro Task)。
  1. 宏任务通常包括一些需要花费较长时间的操作,例如定时器、事件回调等等。

当一个宏任务执行完毕后,JavaScript 引擎会检查是否存在未执行的微任务,如果存在,则立即执行这些微任务。在所有微任务执行完毕后,JavaScript 引擎会再次开始执行宏任务。

  1. 微任务通常包括一些需要尽快执行的操作,例如 Promise 的回调函数、MutationObserver 的回调函数等等。

微任务可以使用 Promise 对象的 所有方法 或者 MutationObserver 的 observe() 方法注册。

执行步骤

  1. 执行当前执行当前宏任务中的同步代码,直到遇到第一个宏任务或微任务。
  2. 如果遇到微任务,则将它添加到微任务队列中,继续执行下一个同步代码。
  3. 如果遇到宏任务,则将它添加到宏任务队列中,继续执行下一个同步代码。
  4. 当前微任务或者宏任务执行完毕后,JavaScript 引擎会检查微任务或宏任务队列是否为空。如果不为空,则执行队列中的第一个任务,重复执行该步骤直到任务队列为空。
  5. 当前事件轮询结束,等待下一次事件的触发。

16、事件流

  • 简略回答

javascript 事件流 是浏览器处理事件的方式。事件流描述了从页面中接收事件的顺序。顺序是从父节点到子节点,再从子节点到父节点。

  • 详细回答

事件流分为三个阶段:捕获阶段、目标阶段和冒泡阶段。

  1. 捕获阶段:是指事件从最外层的节点开始,逐层向下传递到目标节点。
  2. 目标阶段:是指事件到达目标节点后,触发目标节点的事件处理程序。
  3. 冒泡阶段:是指事件从目标节点开始,逐层向上传递到最外层节点。

事件传播过程停止和取消方法

  1. 事件的传播过程被停止的方法是调用事件对象的stopPropagation()方法。
  2. 取消事件的默认行为的方法是调用事件对象的preventDefault()方法。

17、instanceof 的理解

  • 简略回答
  1. instanceof 运算符可以用于判断一个对象是否是某个构造函数的实例
  2. 原理是基于原型链的检查,如果对象的原型链比较深,那么检查的效率会比较低,影响性能。
  • 详细回答

实现步骤

  1. 检查是否为基本数据类型,如果是,直接返回false
  2. 通过Object.getPrototypeOf()方法获取对象的原型
  3. 循环检查原型链,直到找到constructor.prototype构造函数上的原型 或者 原型链为null为止

18、for-in 和 for-of 的区别

  • 简略回答

for…in 循环用于遍历对象的可枚举属性

  • 它会将对象的每个属性名称(或键名)作为迭代变量来遍历,包括自有属性和继承属性。
  • 因此,它并不适用于遍历数组和类数组对象。

for…of 循环用于遍历可迭代对象的元素

  • 它会将对象的每个元素作为迭代变量来遍历,

  • 只能遍历实现了迭代器接口(Iterator)的对象

  • 详细回答


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

  • 简略回答
  1. 箭头函数没有自己的 this 上下文,它的 this 上下文继承自外部作用域,因此不能使用 call()、apply() 或 bind() 方法改变 this 上下文。
  2. 箭头函数没有自己的 arguments 对象,如果需要获取函数参数,可以使用 rest 参数或者展开运算符。
  3. 箭头函数不能作为构造函数使用,不能使用 new 关键字创建对象。
  • 详细回答

20、怎么删除数组最后一个元素

  • 简略回答
  1. 使用pop() 方法,删除并返回数组的最后一个元素,即原数组值的最后一个元素被删除;
  2. 使用 splice() 方法,删除并返回数组,数组中为被删除的最后一个元素,即原数组值的最后一个元素被删除;
  3. 使用 slice() 方法截取指定索引元素,创建一个新的数组,包含除了最后一个元素以外的所有元素;
  • 详细回答

21、原型和原型链

  • 简略回答

原型(Prototype)
定义:在 JavaScript 中,原型是一个对象,它是函数所特有的属性。当使用函数创建对象(通过new关键字)时,新创建的对象会从这个函数的原型对象继承属性和方法。

原型链(Prototype Chain)
定义:原型链是 JavaScript 中对象之间的一种链式查找机制。当访问一个对象的属性或方法时,如果在该对象本身不存在这个属性或方法,JavaScript 会沿着这个对象的原型(__proto__属性,它指向对象的原型对象)向上查找,这个查找路径形成的链条就是原型链。

  • 详细回答

原型链作用

  1. 实现javascript的继承。
  2. 使用场景使用
    a. instanceof操作符在原型链上查找并判断某个对象是否是某个构造函数的实例。
    特点 是查询原型链上的属性方法时性能差,试图访问不存在的属性时会遍历整个原型链。

原型对象获取方式

  1. 通过__proto__属性获取
  2. 使用Object.getPrototypeOf()方法获取

22、数组foreach能否提前结束循环

  • 简略回答

1、数组的 forEach 方法默认不支持提前结束循环,即无法使用类似于 break 或 return 的语法来跳出循环。

2、但是可以使用抛出异常的方式来达到提前结束循环的效果。

  • 详细回答

23、事件池

  • 简略回答

事件池 是指浏览器在事件处理中,为了节省内存和提高性能,会对事件对象进行重复利用。

  • 详细回答

实现方式

事件池机制会让浏览器,先检查是否存在空闲的事件对象,如果有,则直接从池中取出事件对象进行使用,避免了重复创建对象的开销,提高了性能。

特点

  1. 事件池机制只针对同一类型的事件。
    a. 例如,当有多个鼠标点击事件同时发生时,浏览器会在内部为每个鼠标点击事件维护一个事件池,而不是把不同类型的事件混合在同一个事件池中进行管理。
  2. 由于事件池机制,在事件处理函数中不能异步获取事件对象,因为事件对象会在事件处理函数执行完成后被返回池中,如果此时异步代码仍然在使用事件对象,就会出现不可预料的后果。

24、如何判断一个对象是不是空对象

  • 简略回答
  1. 使用Object.keys()方法,看返回的数组长度是否为0。
  2. 使用JSON.stringfy()方法,看返回的值是否为“{}”字符串大挎号。
  3. 使用for-in循环方法,看是否执行循环遍历,没有执行则为空对象
  • 详细回答

25、如何合并对象

  • 简略回答
  1. 使用Object.assign()方法
  2. 使用… 扩展运算符
  • 详细回答

26、requestAnimationFrame和requestIdleCallback

  • 简略回答

  • requestAnimationFrame 是浏览器提供的一种动画帧请求机制,作用是会在浏览器下一次绘制之前执行指定的回调函数。

  • requestIdleCallback 作用是在浏览器空闲时执行指定的回调函数。这个 API 的目的是让开发者能够在浏览器空闲时,进行一些比较耗时的任务,例如计算和渲染。

  • 详细回答


27、Symbol 基础数据类型

  • 简略回答
  1. Symbol 是在 ES6 中新增的基础数据类型。
  2. 它的主要作用是创建一个唯一的标识符,用于对象属性名的命名、常量的定义等场景。
  • 详细回答
    不会出现在 for…in、for…of、Object.keys()、Object.getOwnPropertyNames() 等遍历对象属性的方法中,因此可以用来定义一些不希望被遍历到的属性,例如一些内部实现细节或隐藏属性

28、闭包

  • 简略回答

闭包是指有权访问另一个函数作用域中变量的函数

  1. 优点是私有化数据,在私有化数据的基础上保持数据,
  2. 缺点使用不恰当会导致内存泄漏,在不需要用到的时候及时把变量置为null
  • 详细回答
  1. 闭包的作用 可以将变量和函数私有化,从而避免命名冲突和变量污染。
  2. 闭包的原理 是在内存中创建一个包含函数和变量的环境,当函数返回后,该环境仍然存在于内存中,因此可以被其他函数访问和使用。闭包中的变量和函数可以被多次调用和修改,从而在私有化数据上保持数据。
  3. 常见的使用场景
    • 保存变量状态和私有化变量和函数。
    • 用于事件处理和回调函数。
    • 用于解决循环中异步问题。

哪些方式可以防止闭包引起的内存泄露

  1. 避免创建不必要的闭包:

如果闭包中包含的变量在函数执行完后不再需要使用,可以避免创建闭包,从而避免内存泄漏的问题

  1. 及时释放闭包:

在使用闭包时,需要在不需要时及时释放闭包,可以使用变量赋值为 null 或者手动解除对闭包的引用等方式来释放闭包。

  1. 使用模块模式:

在模块模式中,可以使用立即执行函数(IIFE)来创建一个私有作用域,从而避免闭包中的变量被外部访问,避免了内存泄漏的问题

  1. 避免循环引用:

如果闭包中引用了 DOM 元素或其他对象,需要确保在不需要时及时释放这些对象,避免循环引用造成内存泄漏的问题。


29、0.1加0.2为什么不等于0.3

  • 简略回答
  1. 原因是 在js中,浮点数采用二进制形式表示实数。而二进制无法精确地表示某些十进制小数,比如0.1和0.2,在二进制下是无限循环小数。

  2. 所以js中执行0.1加0.2的计算时,由于无法精确表示这两个数字,所以他们会被转换成最接近的可表示二进制数,再进行计算。

  3. 这会导致微小的舍入误差,使结果不等于0.3。

解决方法

  1. 使用toFixed() 方法将结果四舍五入到指定小数位数。再进行类型转换。
  2. 行整数运算。
  3. 使用第三方高精度库。
  • 详细回答

30、== 和 === 的区别

  • 简略回答
  1. ==非严格运算符进行比较时,会进行类型转换,然后在比较两个值是否相等;
  2. ===严格比较运算符进行比较时,不会进行类型转换,直接比较两个值,值和类型相等时才会返回true;
  • 详细回答

类型转换规则

  1. 如果两个值类型相同,则直接比较他们的值。
  2. 如果一个值是null,另一个值是undefined,则它们相等。
  3. 如果一个值是数字,另一个值是字符串,则将字符串转换为数字后再比较。
  4. 如果一个值是布尔值,另一个值是非布尔值,则将布尔值转换为数字或再比较。
  5. 如果一个值是对象,另一个值是数字、字符串或布尔值,则将对象转换为原始值后再比较。

31、ES6新特性

  • 简略回答
  1. 块级作用域
    a. 通过 let 和 const 声明的变量只在当前块级作用域中有效。
  2. 箭头函数
    a. 使用 => 符号定义的函数,具有简化的语法和自动绑定 this 上下文的特点。
  3. 模板字符串
    a. 使用反引号 `` 和 ${} 操作符,可以方便地拼接字符串和变量。
  4. 解构赋值
    a. 可以将数组或对象的值解构赋给变量。
  5. 类和继承
    a. 引入了 class 和 extends 关键字,使得 JavaScript 支持面向对象编程。
  6. Promise 和 async、await,用于处理异步函数。
  • 详细回答

PS.未完待续,文中答案有误也欢迎评论指出!

另外作者也在找工作,欢迎公司有HC的同学内推,base地:上海、北京或杭州。


http://www.kler.cn/a/375223.html

相关文章:

  • Gin-vue-admin(1):环境配置和安装
  • 维克日记:私密写作新选择,轻松记录生活点滴
  • 【信号滤波 (上)】傅里叶变换和滤波算法去除ADC采样中的噪声(Matlab/C++)
  • git命令恢复/还原某个文件、删除远程仓库中的文件
  • XMLHttpRequest的基础知识
  • pm面试题
  • ubuntu禁止自动更新设置
  • 新浪新闻探索大会|赵世奇:文心智能体解锁AI浪潮中的商业新范式
  • 《别傻等外卖了!Java 中的 CompletableFuture 比 Future 香十倍!》
  • computed拦截v-model
  • 「Mac畅玩鸿蒙与硬件10」鸿蒙开发环境配置篇10 - 项目实战:计数器应用
  • k8s集群 ceph rbd 存储动态扩容
  • Java项目实战II基于Java+Spring Boot+MySQL的植物健康系统(开发文档+数据库+源码)
  • 网络搜索引擎Shodan(5)
  • Lucene数据写入流程
  • 【qt qtcreator使用】【正点原子】嵌入式Qt5 C++开发视频
  • Python爬虫:在1688上“侦探游戏”获取店铺详情
  • 如何利用斗篷cloak技术做F牌独立站?
  • mysql 中临时表
  • 如何提高FPGA的逻辑利用率与资源效率!!!
  • 软件测试工程师面试整理 —— 编程与自动化!
  • 竞赛管理新未来:Spring Boot大学生竞赛平台
  • C++的filesystem的时间与Qt的QDateTime时间为什么相差204年?
  • git入门教程15:git扩展
  • Vue全栈开发旅游网项目(3)-Vue路由配置
  • 基于SpringBoot和PostGIS的世界各国邻国可视化实践