JavaScript面试总结

1. 基本数据类型

  • Number
  • String
  • Boolean
  • Undefined
  • null
  • BigInt
  • Symbol

2. 引用数据类型

  1. Object
  2. Array
  3. Function
    函数实际上是对象,每个函数都是Function类型的实例,而Function也有属性和方法,跟其他引用类型一样
  4. 其他引用类型
    除了上述说的三种之外,还包括Date、RegExp、Map、Set

存储差别

基本数据类型和引用数据类型存储在内存中的位置不同:

  • 基本数据类型存储在栈中
  • 引用类型的对象存储在堆中
    当我们把变量赋值给一个变量时,解析器首先要确认的就是这个值是基本类型值还是引用类型值

3. instanceof运算符的实现原理及实现

instanceof运算符适用于检测构造函数的prototype属性是否出现某个实例对象的原型链上

原理:

instanceof运算符的原理是基于原型链的查找。当使用 obj instanceof constructor的时候,js引擎会从obj的原型链上查找constructor.prototype的存在,如果找到返回true,没有找到则继续在原型链上寻找,如果查找到原型链顶端依然没有找到则返回false

  • instanceof适用于引用数据类型,不能用于简单数据类型的检查

4. typeof与instanceof

typeof

typeof操作符返回一个字符串,表示未经计算的操作数的类型

typeof 1;//'number'
typeof '1';//'string'
typeof undefined;//'undefined'
typeof true;//'boolean'
typeof null;//'object'
判断一个变量a是否存在(不要使用if(a) ,因为如果a未声明,会报错)

if(typeof a != ‘undefined’){
// 变量存在
}
如果需要通用检测数据类型,可以采用Object.prototype.toString,调用该方法,统一返回格式"[Object Xxx]"类型的字符串

instanceof

instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上
object instanceof constructor
object为实例对象,constructor为构造函数
构造函数通过new可以实例对象,instanceof能判断这个对象是否是之前那个构造函数生成的对象

区别

typeof和instanceof都是判断数据类型的方法

  • typeof会返回一个运算数的基本类型,但是intanceof返回的是布尔值
  • instanceof可以准确判断引用数据类型但是不能判断简单数据类型。typeof则恰好相反(function除外)

5. 为什么0.1+0.2 !== 0.3.如何让其相等

在计算机计算中,会把数据转换为二进制来进行运算。但是0.1是不能直接用二进制来表示的,它的二进制是无限循环的,所以人们使用了一种采取一定精度使用近似值表示一个小数的方法,就是IEEE 754。十进制浮点数无法完全精确转换为二进制浮点数,所以在计算时会产生误差

解决方案
  1. 将其先转换为整数,在相加之后转回小数。具体做法为先乘10相加后除以10
let x = (0.1*10 + 0.2*10)/10;
console.log(x === 0.3);
  1. 使用number对象的toFixed方法,只保留一位小数点
(n1 + n2).toFixed(2);

6. 判断数组的方法有哪些?

  1. 通过原型链判断
    obj.__proto__ === Array.prototype;
  2. 通过es6的Array.isArray()判断
    Array.isArray(obj);
  3. 通过instanceof做判断
    obj instanceof Array;
  4. 通过Object.prototype.toString.call()做判断
    Object.prototype.toString.call(obj).slice(8,-1)==="Array"

7. 将类数组arguments转换为数组的方法

  1. Array.from(arguments)
  2. Array.prototype.slice.call(arguments)
  3. […arguments]

8. foreach 和 map 方法的区别

两个方法都是用来遍历数组的
foreach会改变原数组,没有返回值
map不会改变原数组,返回一个经过操作之后的新数组

9. == 和 === 的区别

== 操作符

等于操作符在比较中会先进行类型转换,再确定操作数是否相等
遵循以下规则:

  • 如果任意操作数是布尔值,则将其转换为数值再比较是否相等
    let result = (true == 1);// true
  • 如果一个操作数是字符串,另一个操作数是数值,则尝试将字符串转换为数值,再比较是否相等
    let result = ("11" == 11);// true
  • 如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法取得其原始值,再根据前面的规则进行比较
let obj = {
	valueOf:function(){
		return 1;
	}
}
let result = (Object == 1);// true
  • null 与 undefined相等
  • 如果有任一操作数是NaN,则相等操作符返回false
    let result = (NaN == NaN);// false
  • 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回true
let obj1 = {name:"kkkk"};
let obj2 = {name:"kkkk"};
let result = (obj1 == obj2);// false
总结
  • 两个都为简单类型,字符串和布尔值都会转换成数值,在比较
  • 简单类型与引用类型比较,对象转换成其原始类型的值,再比较
  • 两个都为引用类型,则比较它们是否指向同一个对象
  • null和undefined相等
  • 存在NaN则返回false

=== 操作符

只有两个操作数在不转换的前提下相等才返回true。即类型相同,值也需相等
undefined 和null 与自身严格相等

let result1 = (null === undefined);// false
let result2 = (null === null);// true
区别

== 操作符会做类型转换,再进行值的比较,=== 运算符不会做类型转换
但在比较null的情况的时候,我们一般使用相等操作符==

const obj = {};
console.log(obj.x == null);// true

10. let const var的区别

块级作用域解决的问题:

1、内层变量名可能会覆盖外层变量名
2、用来计数的变量可能会泄露为全局变量

变量提升:

let const 不存在变量提升,必须先声明后使用,如果未声明就访问,就会触发ReferenceError,这个阶段称为暂时性死区
var可以重复声明变量,后面的变量会覆盖前面的,let、const不允许重复声明
var let 声明变量后可以不先设置初始值,但是const必须设置

  • const定义的是常量,之后不能重新赋值

11. 箭头函数和普通函数的区别

  • 箭头函数是匿名函数,不能作为构造函数使用new关键字
  • 不能使用argument
  • 箭头函数没有自己的this,会取上下文作为自己的this
  • 箭头函数没有prototype
  • 箭头函数不能使用yield关键字

12. js常用数据结构

  1. 数组
  2. 对象
  3. 队列
  4. 链表

13. DOM

14. BOM:

浏览器对象模型,提供了独立于内容与浏览器窗口进行交互的对象,其作用就是跟浏览器做一些交互效果,比如如何进行页面的前进、后退、刷新、浏览器窗口发生变化,滚动条的滚动,以及获取客户的一些信息如:浏览器品牌版本,屏幕分辨率。

  1. window
    BOM的核心对象,表示一个浏览器实例。在浏览器中,window对象有双重角色,即是浏览器窗口的一个接口,又是全局对象
    因此所有在全局作用域中声明的变量、函数都会变成window对象的属性和方法

  2. location
    属性:
    hash:url中#后面的字符,没有则返回空串
    host:域名+端口号
    hostname:域名
    href:完整url
    pathname:服务器下面的文件路径
    port:url的端口号,没有则返回空
    protocol:协议
    search:url的查询字符串,通常为?后面的内容
    location.reload(),此方法可以重新刷新当前页面

  3. navigator
    navigator对象主要用于获取浏览器的属性,区分浏览器的类型。
    navigator对象接口定义的属性和方法

  4. screen
    保存的纯粹是客户端能力信息,也就是浏览器窗口外面客户端显示器的信息,比如像素宽度和像素高度

  5. history
    history对象主要是操作浏览器url的历史记录,可以通过参数向前,向后,或者向指定url跳转
    history.go()
    history.forward()
    history.back()
    history.length()

15. js原型:

js中每个对象都有一个指向另一个对象的引用,这个对象就被称为原型。它包含对象共享的属性和方法。

js原型链:

基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联关系是一种链状的结构,这种原型对象的链状结构关系称为原型链。
原型链其实是一种查找规则。

实现原型继承:

通过原型来继承构造函数 2、 将constructor指向原来的构造函数

js原型的应用场景

  1. 继承
    通过原型,可以实现对象之间的继承关系。子对象的原型链会指向父对象的原型,从而实现属性和方法的共享。
  2. 原型链
    原型链是 JavaScript 中实现继承的机制,通过原型链,对象可以沿着原型链访问其原型上的属性和方法。
  3. 原型方法
    将方法添加到对象的原型上,可以使得所有通过该原型创建的实例都能够共享这些方法,提高代码的复用性。
    如将全局事件总线挂载到全局时,Vue.prototype.$bus = Bus
  4. 性能优化
    在 JavaScript 中,每个对象通过原型链访问属性和方法,而不是直接在对象上定义,这有助于减少内存占用和提高代码执行效率。
  5. 动态性
    可以在运行时动态地修改原型对象,添加、修改或删除原型上的属性和方法,从而影响通过该原型创建的所有实例。

16. this

定义:函数的this关键字是函数运行时自动生成的一个函数内部对象,只能在函数内部使用,总指向调用它的对象

绑定规则:

  1. 全局上下文中的 this: 当函数在全局上下文中被调用时,this 指向全局对象,在浏览器中通常是 window 对象。
  2. 函数作为对象的方法调用时的 this: 当函数作为对象的方法被调用时,this 指向调用该方法的对象。
  3. 使用 call()、apply() 或 bind() 明确指定 this: 这些方法允许显式地设置函数执行时的 this 值。
  4. 构造函数中的 this: 当一个函数被用作构造函数并使用 new 关键字调用时,this 指向新创建的对象。
  5. 箭头函数中的 this: 箭头函数的 this 是在定义时确定的,指向其父作用域中的 this 值,而不是在调用时确定。

17. 事件模型

JavaScrip中事件模型是一种处理用户交互和其他异步操作的机制。基于事件驱动的编程范式,通过添加事件监听器来响应事件的发生。当事件发生时,相关的事件处理函数会被调用,执行特定的代码逻辑。通过事件模型,可以实现与用户的交互以及页面元素之间的动态交互。
事件模型分为三种:原始事件模型、标准事件模型、IE事件模型

18. 事件与事件流

事件:

事件是指用户在浏览器中执行的动作,如点击、鼠标移动、键盘输入等。每个事件都会触发相应的事件对象,并可以通过事件监听器来捕获和处理事件。

事件流:

事件流描述了事件在页面中传播和触发的顺序。事件流分为冒泡(bubble)阶段和捕获(capture)阶段。在冒泡阶段中,事件从最具体的元素(触发事件的元素)向最不具体的元素(文档根元素)传播。而在捕获阶段中,事件从最不具体的元素向最具体的元素传播。在捕获阶段和冒泡阶段之间存在一个目标阶段,代表事件到达目标元素的阶段。

取消事件冒泡

js:event.stopPropagation()方法

事件冒泡和事件捕获的区别

事件冒泡和事件捕获是两种不同的事件传播方式,默认是冒泡,他们的区别在于事件的传播方式不同

  • 事件冒泡是自下而上,从子元素冒泡到父元素,触发的是父元素上的事件处理
  • 事件捕获是事件从文档的根元素开始,逐级向下传播到较为具体的元素。即从父元素到子元素。

事件代理(事件委托)

事件代理是一种利用事件冒泡机制来简化事件处理的技术。它通过将事件处理程序绑定到一个父元素上,然后利用事件冒泡的特性,让父元素代理子元素的事件处理。这样可以减少事件处理程序的数量,提高性能,并且可以处理动态添加或移除子元素的事件。

  1. 列表/表格项的事件处理
  2. 动态元素的事件处理
  3. 性能优化
  4. 减少内存占用
    在这里插入图片描述

19. 闭包:

一个函数对于周围状态的引用捆绑在一起,这样的组合就是闭包
简单来说就是内层函数+外层函数的变量
使用场景:实现数据的私有,创建全局私有变量

20. map和Object的区别

  1. 键的类型:
    map的键可以是任意数据类型(包括对象,函数,NAN),但是Object的键只能是字符串和Symbol类型
  2. 键值对的顺序
    map中的键值是按照插入的顺序存储的,而对象中的键值没有顺序
  3. 键值对的遍历
    map的键值对可以使用for…of遍历,而Object的键值对需要手动遍历
  4. 继承关系
    map没有继承关系,Object是所有对象的基类

21. 垃圾回收机制

垃圾回收:

js代码运行时,需要分配内存空间来存储变量和值,当变量不在参与运行之后,系统会自动回收占用的内存空间,避免因不及时清理造成系统卡顿、内存溢出。

内存泄露:

程序中分配的内存由于某种原因没有被释放或者无法释放,就叫做内存泄露(意外的全局对象、被遗忘的计时器或回调函数、闭包、脱离DOM的引用)

如何进行垃圾回收:

  1. 标记清除法
    通过标记所有活动对象,然后清除所有未标记(即未被引用)的对象来完成内存回收。
  2. 引用计数法
    这种算法会为每个对象维护一个引用计数,当引用计数为零时表示对象不再被引用,可以被回收。这种算法简单直观,但容易出现循环引用导致内存泄漏的问题。

22. call、apply、bind的区别

都能修改this指向

  • 传参:call、bind都是传入一个对象,apply是传入一个数组
  • 调用函数:call和apply在修改this指向后会立即执行函数,但是bind函数改变this的指向后会返回this被改变后的函数,需要手动调用才会执行

23. new操作符的实现原理

  1. 创建一个空对象
  2. 将空对象的__proto__指向构造函数的原型对象
  3. 将构造函数的this指向空对象
  4. 返回创建的对象

手写new:

function myNew(constructor,...args){
    // 创建一个空对象
    const obj = {};
    // 将空对象的__proto__指向构造函数的原型对象
    obj.__proto__ = constructor.prototype;
    // 利用apply方法,将constructor指向对象,并传入参数
    const result = constructor.apply(obj,args);
    // 如果执行结果有返回值并且是一个对象,返回执行的结果,否则返回新创建的对象
    return result instanceof Object ? result : obj;
}

24. 宏任务与微任务

宏任务和微任务是js事件循环中的两种任务类型,他们在执行顺序和优先级上有所不同

  • 宏任务:宏任务是常规的事件任务类型,他们在事件循环中按照顺序依次执行。
    常规的宏任务包括:
    script:整体代码脚本
    setTimeoutr和setInteral:通过两个函数设置的延时或定时执行的回调函数
    I/O:在nodejs中,来自文件、数据库等的回调也是宏任务
    UI渲染
    postMessage和MessageChannel:通过这两个方法发送和接受消息
    Promise
    async
  • 微任务:微任务是比宏任务更高优先级的任务类型,它们在每次事件循环的末尾执行,即执行完一个宏任务后,会立即执行所有等待的微任务。
    常见的微任务包括:
    Promise.then和Promise.catch中的回调
    浏览器环境:用于监视DOM更改的API,当DOM发生变化时,其回调函数会被添加到微任务队列中
    queueMicrotask
    promise的回调
    await

25. 事件循环

js中的事件循环是一种重要机制,用于处理异步操作和协调任务执行的操作。
核心思想:在单线程中高效管理任务执行,确保任务执行的顺序
事件循环包括三个部分:执行栈,任务队列和事件循环本身。

  1. 执行栈:执行栈是一个用于管理函数调用关系和执行上下文的数据结构。当一个函数被调用的时候,它的执行上下文会被压入栈中。当函数执行完毕后,它的函数上下文会从执行栈中被弹出
  2. 任务队列:任务队列用于存储待执行的任务。这些任务通常是异步任务的回调。当异步操作完成后,它的回调函数会被添加到任务队列中等待执行。任务队列有多个,其中包含宏任务和微任务。
  3. 事件循环:协调执行栈和任务队列之间工作的核心机制。当执行栈为空时,事件循环会从任务队列中取出一个任务(首先检查微任务,然后再检查宏任务),将其推进执行栈中执行。这个过程会不断重复,形成一个循环,因此叫做事件循环
    事件循环工作流程:
  4. 执行同步任务
  5. 处理微任务
  6. 执行宏任务
  7. 重复循环

26. 消息队列

js的消息队列是一种管理和处理异步任务的机制,它保证了异步任务的执行顺序并且遵循事件循环机制。js引擎使用消息队列来存储和管理待执行的异步任务,并在适当的时机将它们从队列中取出并执行。

消息队列的应用

  1. 通过消息队列可以实现异步编程,如定时器、事件监听、网络请求等,避免阻塞主线程。
  2. Promise 对象的异步执行结果处理就是借助了消息队列的机制。

27. defer和async

async

异步加载下载js脚本文件,在加载下载完成之后立马执行js脚本文件,多个async脚本文件执行时执行顺序没有保障,执行js的过程中,会阻塞html的解析和渲染。

defer

异步加载下载js文件,在下载完成后会等到html文件解析完成后再执行defer脚本文件,多个defer脚本文件执行时顺序有保障,会先来后到的执行。执行过程中会阻塞html的解析和渲染。
两者区别

  • 执行的事件不同,async是下载完成后立马执行,而defer需要等html完全的解析之后才会执行
  • 执行顺序。多个defer的执行顺序都是有保障的,而多个async的执行顺序没有保障。
  • 如果想要获取到dom就需要使用defer。

28. css和js的解析顺序

默认情况下,浏览器会在解析完HTML和CSS后再解析JavaScript
当浏览器遇到 script 标签或者外部 JavaScript 文件(通过 script src=“…” 引入)时,会停止解析 HTML,先执行 JavaScript 代码。

29. require和import的区别

  1. 加载方式:require是在运行时加载模块,import是在编译时加载模块
  2. 规范:require遵循commonjs/AMD规范,import遵循es6规范
  3. 使用位置:require可以写在文件的任意位置,但是import只能写在文件的顶部
  4. 导出方式:require导出的是exports对象,import通过exports导出的是指定输出的代码
  5. 性能:由于require是运行时加载,性能较低,而import是编译时加载,性能较高。
  6. 值得改变:通过require引入的模块,其值一旦确定就不能改变,import引入模块的值可以改变

30. js异步

js异步是指在js中,某些操作不会立马执行,而是会延迟一段时间或者等待其他任务完成再执行。
异步操作通常通过promise、async/await等方法实现

31. js处理并发

  1. 使用promise,async/await
  2. 事件驱动:JavaScript 是事件驱动的语言,通过事件机制可以处理并发的用户交互、网络请求等操作。通过监听事件并注册相应的处理函数,可以在不同的事件发生时执行相应的逻辑,实现并发处理。
  3. Web Worker:Web Workers 是 HTML5 提供的一种在后台运行脚本的机制,可以在单独的线程中执行 JavaScript 代码。通过 Web Workers,可以在后台处理计算密集型任务或者网络请求,从而不影响主线程的执行。

js是单线程语言,意味着在 JavaScript 运行时环境中,只有一个主线程用于执行代码和处理事件。

32. js包括哪些部分

  1. ECMAscript:js语言的核心部分,描述了js的语法
  2. 文档对象模型DOM:DOM是js操作网页内容的接口。它将HTML文档表示为一个树形结构,使js可以操作和修改页面的内容样式结构。
  3. 浏览器对象模型BOM:BOM提供了js与浏览器交互的接口。
  4. 事件处理
  5. 异步编程
  6. 模块化
  7. ES6+新特性

33. 数组的方法

不会改变原数组的方法:
map,filter,slice,concat,reduce,every,some

34. 字符串方法

不会改变原始字符串,而是返回一个新的字符串

  1. 字符串的查找和提取
    indexOf lastIndexOf chatAt substring slice
  2. 字符串的拼接 concat
  3. 字符串的转换
    toLowerCase,toUpperCase,trim
  4. 其他操作
    match,replace,split

35. Generator函数

Generator是es6引入的一种新的函数类型,它允许函数在执行过程中被暂停和恢复。其原理主要依赖于函数的执行上下文和迭代器协议

  1. 执行上下文
  2. 迭代器协议

36. ES6的新特性

  1. let和const声明
  2. 箭头函数
  3. 模版字符串
  4. 解构赋值
  5. 默认参数
  6. 展开运算符
  7. 类和继承
  8. 模块化
  9. Promise
  10. Generator

37. 进程和线程

  1. 进程是程序执行的一次过程,是操作系统资源分配的基本单位。每个进程都有独立的内存空间,包含程序的代码、数据和堆栈等。不同进程之间相互独立,通信需要通过进程间通信(IPC)机制
  2. 线程是进程中的实体,是CPU调度的基本单位。线程共享相同的进程地址空间,包含代码段、数据段和堆栈等,可以直接访问进程的共享资源

区别:

  1. 进程和线程的最大区别在于资源分配和执行方式。进程是独立的程序执行过程,拥有独立的地址空间;而线程是进程中的执行实体,共享相同的地址空间。
  2. 进程间通信需要较为复杂的 IPC 机制,而线程之间可以直接通过共享内存等方式进行通信,更加高效便捷。

38. 前端设计模式:

  • MVC模式——将应用程序分为三个组件:模型(数据处理)、视图(UI)和控制器(逻辑层)
  • MVVM模式——将MVC中的控制器分为绑定器和视图模型两部分,使视图与数据绑定更简单
  • 观察者模式——当被观察对象状态改变时通知所有观察者
  • 工厂模式——通过工厂类创建对象,避免使用new关键字直接创建对象。
  • 单例模式——确保只有一个实例存在的设计模式。
  • 装饰器模式——动态地向对象添加新的行为,同时保持与原始类的接口的兼容性。
  • 适配器模式——允许不兼容的接口之间进行通信。

39. Promise

Promise是js中用来处理异步操作的对象。它代表了一个异步操作的最终完成或失败,并且可以获取其结果。避免了回调地狱的问题。

一个Promise实例有两个重要的方法:then()和catch()。

then() 方法用于指定当 Promise 对象状态变为 resolved(完成态)时要执行的回调函数,而 catch() 方法用于指定当 Promise 对象状态变为 rejected(拒绝态)时要执行的回调函数。
Promise.all() 方法接收一个 Promise 对象的数组作为参数,当所有的Promise对象都变成完成态时,它才会变成完成态;
Promise.race() 方法接收一个 Promise 对象的数组作为参数,当其中任意一个Promise对象变成态或者拒绝态时,它就会相应地变成完成态或拒绝态。

40. async和await底层原理

async/await是建立与promise之上的语法糖。它使异步代码能够以同步的方式书写。
其底层原理主要依赖于promise的链式调用和js的事件循环机制。

  • 在底层,async/await通过编译器被转换成了Promise的链式调用。编译器会把await表达式转换成Promise.then()的调用,并处理错误。
  • async/await捕获异常:通过async函数内部使用try/catch捕获异常
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值