JavaScript
JavaScript基础
怎么减少重排重绘?他们的区别?
减少
- 避免频繁修改DOM
- 使用flex布局
- 避免频繁的获取布局信息,如offsetTop、offsetWidth等
区别
- 触发条件: 重排通常由DOM操作、文本内容改变等触发;而重绘主要由元素样式的改变触发,如颜色
- 性能影响:重排的性能影响通常比重绘更大,因为重排会影响整个布局,而重绘只需重新绘制受影响的元素
- 关联关系: 重排肯定会触发重绘,重绘不一定触发重排
什么是浅拷贝和深拷贝?
- 深拷贝是指拷贝对象的具体内容,二者的内存地址是自主分配的,拷贝之后俩个对象存的值是一样的,但是内存地址不一样,俩个对象页互不影响。浅拷贝是指对内存地址的复制,让目标对象指针和源对象指向同一片内存空间。
什么是防抖与节流?说下什么场景下该使用哪一种方式?
- 防抖:防抖是指在一个频繁的操作中,让前面的操作都无效化,只执行最后一次的操作
- 节流:节流是指在某个事件持续触发时,降低事件的触发频率
- 两者区别是:防抖注重结果,而节流注重过程
应用场景
防抖: 登录、发短信等按钮避免用户点击太快,以至于发送多次请求,需要防抖
节流:
- 鼠标连续不断地触发某事件(如点击),单位时间内只触发一次
- 监听滚动事件,比如是否滑到底部自动加载更多
let、const、var的区别?
- 块级作用域:let、const具有块级作用域,var不存在块级作用域
- 变量提升:var存在变量提升,let和const不存在变量提升
- 重复声明:var声明变量时可以重复声明,后声明的变量会覆盖前面的;而let和const则不允许重复声明
- 初始值设置:var和let不用设置初始值,而const需要
call()、apply()和bind()的区别?
- 三者都是改变this指向的
- 三种第一个参数都是this要指向的对象,如果没有这个参数或为null\undefined则指向全局
- 三种都可以传参,call是参数列表、apply是数组、call和apply是一次性传入参数;bind可以分多次传入
- call()和apply()是直接调用通过调用的方法来返回信息
- bind()则返回的一个函数
原型和原型链?
原型
- 原型是指prototype,也是一个对象,因此原型也称原型对象,用来存放公共属性和方法以及构造函数所用到的构造器。
原型链
- 原型链是JS的特有机制,是用来查找属性和方法的,如果在当前对象中没有对应的属性或方法,就会通过__proto__隐式原型找到父类查询,如果父类也没有则会继续往上查找,如果找到顶级对象Object还没有,则会返回undefined。
promise的then第二个回调和catch有什么区别吗?
区别就是.then如果返回异常,.catch可以捕获,但是.then的第二个参数不能捕获
new做了什么事情?
- 创建一个空对象
- 将构造函数的作用域赋给新对象(改变this指向)
- 返回实例化对象
ES6常用新特性?
- 块级作用域
- 箭头函数
- 模板字符串
- Promise
普通函数和箭头函数?
- 箭头函数和普通函数更加简洁
- 箭头函数没有自己的this指向
- 箭头函数继承来的this指向永远不会改变
- call()、apply()、bind()等方法是无法改变箭头函数的this指向
- 箭头函数没有prototype
- 箭头函数不能作为构造函数使用
setInterval和setTimeout的区别?
- setTimeout设置后隔指定时间只会执行一次
- setInterval设置后每隔指定时间都会执行一次,是无限执行,直到取消
ESMAScript 定义哪些数据类型?
- 简单数据类型:Number、string、Boolean、Null、Undefined
- 复杂数据类型:对象、数组、函数
区别
- 简单数据类型是存储在栈中的,因为占据空间小、大小固定,属于频繁使用的数据,所以存在栈中
- 复杂数据类型是存储在堆中的,因为占据空间大,大小不固定,如果存在栈中,就会影响运行的性能;复杂数据类型在栈中存储的是指针,该指针指向堆中的地址。当需要寻找对应的值时,就会检索其在栈中的地址,拿到地址后从堆中拿到具体的数据
null和undefined的区别?
- null和undefined都是基本数据类型
- undefined代表变量已经声明,但没有值;null代表空
typeof null 的结果是什么,为什么?
- typeof null 的结果是Object。
- 这是因为JavaScript历史原因,在javaScript最初的实现中,js的值是由表示类型的标签和实际数值表示的,而恰好object的类型标签是0,null又代表空指针,在大多数平台下解析到的类型标签也是0,所以typeof的值也就变成了object;
块级作用域是什么?
- 块级作用域是指变量或函数在一个代码块内有效,在代码块外无效的作用域。常见的代码块如if语句、for循环等。在 JavaScript 中,使用大括号 {} 定义块级作用域。
什么是闭包?
- 是指能访问其他函数作用域中变量的函数
怎样形成闭包?
- 每当一个函数在另一个函数中被创建时,就会产生闭包
闭包的作用?
- 让局部变量持久化
- 防止变量被垃圾回收机制回收
- 结果缓存
- 防抖节流
闭包的缺点?
- 比普通函数更占用内存,会导致网页性能变差,在IE下容易造成内存泄露。
- 什么是内存泄漏
每个浏览器会有自己的一套回收机制,当分配出去的内存不使用的时候便会回收;内存泄露的根本原因就是你的代码中分配了一些‘顽固的’内存,浏览器无法进行回收,如果这些’顽固的’内存还在一直不停地分配就会导致后面所用内存不足,造成泄露。
什么是作用域链?
- 作用域链就是当访问某个变量时,先从当前作用域去找,如果找不到,就会向它的父级作用域去找,直到找到全局作用域还没有,就会返回报错。
事件循环机制?
- JS代码从上往下执行,当遇到异步任务时,将异步任务挂起 => 先执行后面的同步任务 => 等同步任务执行完 => 才去执行挂起的异步任务
什么是宏任务,什么是微任务?
- 宏任务和微任务是在事件循环中执行的两种不同类型的任务。
- 宏任务是相对较大的任务,如setTimeout等
- 微任务是相对较小的任务,如Promise的回调等
- 微任务会比宏任务先执行
判断数组的方法?
- Object.prototype.toString.call()
- instanceof
- 通过原型链做判断
- 通过ES6的Array.isArray()做判断
promise的作用?
- Promise 是异步编程的⼀种解决⽅案,主要是用来解决回调地狱问题的
谈谈对前端模块化的理解?
是什么?
- 是将公有的js代码封装到别的js中,在别的地方可以使用
为什么需要模块化?
- 随着JS业务不断增加,代码量庞大
原因:
- 解决命名冲突
- 解决依赖管理
- 按需加载
- 代码封装
怎么去设计一个组件封装?
- 明确组件的职责,确定组件需要做什么
- 组件内部的功能需要紧密相关
- 设计组件时需要考虑在不同场景的复用性
- 隐藏组件的内部实现细节,只暴露必要的接口或属性;避免外部直接操作组件内部的状态和逻辑。
- 组件应该能够独立于其他组件存在和运行,不应该依赖于外部的全局状态。
- 允许通过属性(props)或插槽(slots)等机制来配置组件的行为和外观。使得组件能够适应不同的使用需求。
- 考虑组件的性能,避免不必要的渲染和计算。
- 保持组件的样式与结构分离,使得样式可以独立于组件结构进行修改。
虚拟dom怎么结合?
- 虚拟DOM (Virtual DOM) 是一种轻量级的抽象,它允许我们在JavaScript和DOM之间保持一致的数据表示。它不是实际的DOM元素,而是一个描述DOM的JavaScript对象。当状态改变时,可以通过比较新旧虚拟DOM树的差异,高效地更新实际的DOM。
虚拟DOM的结合通常涉及以下步骤:
- 创建一个虚拟DOM节点。
- 使用虚拟DOM构建函数创建更多的虚拟DOM节点。
- 比较新旧虚拟DOM的差异。
- 根据差异更新实际的DOM。
promise的使用场景?
异步请求
- 如经常需要进行异步请求,如发送HTTP请求获取数据。使用Promise可以更好地处理这些异步请求,通过Promise的链式调用可以更清晰地表达异步操作之间的依赖关系。
定时器
- Promise可以与定时器结合使用,通过Promise的resolve和reject方法来控制定时操作的执行结果。例如,可以使用Promise封装setTimeout函数,通过resolve方法在定时结束后执行回调函数。
多个异步操作的并行执行
- 有时需要同时执行多个异步操作,如同时发送多个请求并等待它们全部完成后再进行下一步操作。Promise提供了Promise.all方法,可以将多个Promise对象包装成一个新的Promise对象,当所有的Promise对象都完成时,新的Promise对象才会被解析。这样可以方便地实现多个异步操作的并行执行。
异步操作的错误处理
- 在前端开发中,异步操作可能会出现错误,如网络请求失败、数据解析错误等。Promise提供了catch方法,可以捕获并处理异步操作中的错误。通过catch方法,可以更好地处理和管理异步操作的错误情况,并进行相应的错误处理和提示。
promise.all()的使用场景?
- 数据的批量获取:例如从不同 API 下载多个资源,只有当所有资源都下载完成后才执行下一步操作。
- 登录时验证用户名和密码同时是否有效。
- 在多个异步依赖项都满足时初始化应用或模块。
promise.race()的使用场景?
- 适用于多个异步请求之间的竞争场景。如果我们需要同时向多个 API 发送请求,但是只有一个请求的响应时间是关键的,那么我们可以使用promise.race()方法来优化请求时间,提高效率。
js中cookie、localstorage、sessionstorage三种缓存使用场景?
Cookie
- 场景描述:Cookie 是服务器发送到客户端浏览器并保存在客户端的小型文本文件。Cookie 可以被设置为在浏览器关闭后依然存在(持久Cookie),或者在会话结束时被删除(会话Cookie)。
- 使用场景:
-
- 存储会话信息,如用户认证信息(例如JWT令牌)。
- 跟踪用户的会话状态,如购物车中的商品。
- 个性化设置,如用户偏好或主题设置,尽管这些信息更适合存储在LocalStorage中。
LocalStorage
- 场景描述:LocalStorage 提供了一个简单的方式来在用户的浏览器中存储数据,数据没有过期时间,会永久保存直到被清除。
- 使用场景:
-
- 存储用户偏好设置,如界面主题、布局选项。
- 缓存数据以提高性能,如API响应数据,用于无需从服务器重新获取数据的场景。
- 存储需要跨会话访问的数据,如未完成的表单数据。
SessionStorage
-
场景描述:SessionStorage 与 LocalStorage 类似,但它的数据只在浏览器会话期间有效。当用户关闭浏览器窗口或标签页时,存储的数据会被清除。
-
使用场景:
- 存储临时数据,如表单填写过程中的临时数据。
- 管理单个会话中的用户状态,如登录状态或临时通知。
- 存储只在当前页面或标签页中需要的数据。
window有那些子对象?
window有五个子对象:Location对象、Navigator对象、Screen对象、History对象、document
Location对象
- Location 对象包含有关当前 URL(统一资源定位符) 的信息。(Uniform Resource Location)
- Location 对象是 Window 对象的一个部分,可通过 window.location 属性来访问。
- location.hostname 返回当前 URL 的主机名。
- location.pathname 返回当前 URL 的路径部分。
- location.protocol 返回当前 URL 的协议。
- location.href 返回完整的 URL。
Navigator对象
- Navigator 对象包含有关浏览器的信息。
- cookieEnabled 指明浏览器中是否启用 cookie 的布尔值。
- appVersion 返回浏览器的平台和版本信息。
- userAgent-- 用户代理头的字符串表示。
- appName 返回浏览器的名称。
Screen对象
- Screen 对象包含有关客户端显示屏幕的信息。
- height 返回显示屏幕的高度。 width 返回显示器屏幕的宽度。
- availHeight 显示屏幕的可用高度 (除 Windows 任务栏之外)。
- availWidth 显示屏幕的可用宽度 (除 Windows 任务栏之外)。
History对象
- History 对象包含用户(在浏览器窗口中)访问过的 URL。
- back() 加载历史列表中的前一个 URL(如果存在)。
- forward() 加载历史列表中的下一个 URL。
document
js中继承有多少种?
- js中常用的有6种:原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、组合继承继承
原型链继承
- 在原型上添加一个方法,它的子集(实例)都能获取调用,因为它不止在当前对象中寻找,他还会从对象的原型,原型的原型,一层一层的向上寻找
- 优点:写起来容易方便,可读性强容易理解,可复用(意思就是所以链上的对象实例都可以去复用链上的方法)
- 缺点:无法传参,容易无感知覆盖更改导致出问题
构造函数继承
- 就是借用构造函数来继承,是一种通过调用父类构造函数来实现子类继承的方法,通过使用call()、apply() 或 bind() 方法来在子类中继承父类的属性和方法
- 优点:解决了原型链实现继承的不能传参的问题和父类的原型共享的问题。
- 缺点:因为方法都得在构造函数中定义,因此是不能进行函数复用的《《这里的复用不是说不能用父类的方法,父类的方法是可以用的,但是你每次new的时候实例里面会重新生成一个方法,每个实例里面的方法其实是独立的,不能复用的》》 。父类的原型方法也是不能继承的。
组合继承
- 就是将原型链和借用构造函数组合在一起就叫组合继承,将两者的优点都组合在一起。使用原型链对原型属性和方法,通过借用构造函数的实例对象来进行继承。这样,既能进行函数复用,又能保证每个实例都有自己的属性
- 优点:解决了原型链继承和借用构造函数继承的问题
- 缺点:子类实例化时会调用两次父类构造函数
原型式继承
- 就是用原型(prototype)来继承。借用构造函数在一个函数A内部创建一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例。
- 优点:不需要单独创建构造函数,object.create就可以了
- 缺点:跟原型链继承一样,都会共享,子类的实例也不能向父类传参
寄生式继承
- 创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真的是它做了所有工作一样返回对象。相对于原型式继承多了一层封装。
- 优点:简单易用,避免原型链的修改
- 缺点:无法做到函数复用
组合寄生继承
- 组合继承指的是子类调用父类构造函数并改变this指向来继承属性和利用原型链来继承方法这两种方式的组合。
js的内置对象有哪些?
- Object:JavaScript 中所有对象的父对象,包括数组、函数等都继承自 Object 对象。
- Function:函数对象,在 JavaScript 中函数也是对象,可以像其他对象一样被传递、赋值等。
- Array:数组对象,用于存储多个值的有序集合。
- String:字符串对象,用于表示文本数据。
- Number:数字对象,用于表示数值。
- Boolean:布尔对象,用于表示 true 或 false。
- Date:日期对象,用于处理日期和时间。
- Math:数学对象,提供了许多数学计算相关的方法,如三角函数、对数函数等。
- RegExp:正则表达式对象,用于处理和操作字符串的模式匹配。
- Error:错误对象,用于表示错误信息的对象。
- JSON:用于解析 JSON 格式数据的对象,提供了 JSON 数据的序列化和反序列化方法。
- Global:全局对象,提供了一些全局性的方法和属性,比如 parseInt、setTimeout 等。
什么是面向对象?
- 面向对象是一种编程思想,以对象为中心,将属性和方法放在一起形成一个整体(这个整体就是对象),然后调用其方法来解决业务逻辑。
在js中this指向,this该怎么指向?
- 看是普通函数还是箭头函数
- 如果是普通函数,谁调用这个this,那this就指向谁
- 箭头函数是没有this指向的,需要看在那个作用域内,就指向谁
事件委托的理解?
- 给父元素绑定事件,用来监听子元素的冒泡事件,并找到是哪个子元素的事件。
事件委托的好处
- 事件委托技术可以避免对每个字元素添加事件监听器,减少操作DOM节点的次数,从而减少浏览器的重绘和重排,提高代码的性能。使用事件委托,只有父元素与DOM存在交互,其他的操作都是在JS虚拟内存中完成的,这样就大大提高了性能。
什么时候用事件委托
- 当子元素有很多,需要对子元素的时间进行监听的时候