前端基础面试题大全

发布时间:2024年01月20日

一、Vue

文章目录

1、vue 修改数据页面不重新渲染

vue2是用过Object.defineProperty实现数据响应式, 组件初始化时,对 data 中的 item 进行递归遍历,对 item 的每一个属性进行劫持,添加 set , get 方法。我们后来 新加的属性 ,并没有通过Object.defineProperty设置成响应式数据,修改后不会视图更新。

通过数组索引号修改了数组,界面会不会相应更新?为什么?

答:不会。vue 监听不到

vue 为什么没有提供 arr[下标] = val 变成响应式?

尤大:“因为性能问题,性能代价和获得的用户体验收益不成正比”

数组/对象的响应式 ,vue 里面是怎么处理的?

对象:使用了Object.defineProperty中的 get 和 set

如何监测对象中的数据?
通过 setter 实现监视,且要在 new Vue 时就传?要监测的数据

  1. 对象中后追加的属性,Vue 默认不做响应式处理
  2. 如需给后添加的属性做响应式,请使?如下 API:
    Vue.set(target,propertyName/index,value) vm.$set(target,propertyName/index,value)

数组Vue重写了数组的原型,更准确的表达是拦截了数组的原型

如何监测数组中的数据?
通过包裹数组更新元素的?法实现,本质就是做了两件事:

  1. 调?原?对应的?法对数组进?更新

  2. 重新解析模板,进?更新??

  3. 在 Vue 修改数组中的某个元素?定要?如下?法:

    1. 使 ? 这 些 API :
      push() pop() shift() unshift() splice() sort() reverse()
    2. Vue.set()vm.$set()
    3. 覆盖整个数组

为什么对象和数组要分开处理?

对象的属性通常比较少,对每一个属性都劫持set和get,并不会消耗很多性能

数组有可能有成千上万个元素,如果每一个元素都劫持set和get,无疑消耗太多性能了

所以对象通过defineProperty进行正常的劫持set和get

数组则通过修改数组原型上的部分方法,来实现修改数组触发响应式

2、生命周期
Vue 生命周期都有哪些?
生命周期执行时机
beforeCreate在组件实例被创建之初、组件的属性?效之前被调用
created在组件实例已创建完毕。此时属性也已绑定,但真实 DOM 还未?成,$el 还不可?
beforeMount在组件挂载开始之前被调?。相关的 render 函数?次被调?
mounted在 el 被新建的 vm.$el 替换并挂载到实例上之后被调用
beforeUpdate在组件数据更新之前调?。发?在虚拟 DOM 打补丁之前
update在组件数据更新之后被调用
activited在组件被激活时调?(使用了 <keep-alive> 的情况下)
deactivated在组件被销毁时调?(使用了 <keep-alive> 的情况下)
beforeDestory在组件销毁前调?
destoryed在组件销毁后调?
父子组件生命周期执行顺序

加载渲染过程

	->父beforeCreate
  ->父created
  ->父beforeMount
  ->子beforeCreate
  ->子created
  ->子beforeMount
  ->子mounted
  ->父mounted

更新过程

父beforeUpdate->子beforeUpdate->子updated->父updated

销毁过程

父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

keep-alive可以实现组件缓存,当组件切换时不会对当前组件进行卸载

https://juejin.cn/post/6844903641866829838#heading-11

https://juejin.cn/post/7114252241166401573

3、watch 和 computed 的区别

官方文档:对于任何复杂逻辑,你都应当使用计算属性

扩展:vue 中 Computed、Methods、Watch 区别

computed(计算属性)watch(监视属性/侦听器)
根据你所依赖的数据动态显示新的计算结果
不用再 data 中声明,否则报错
data 的数据监听回调,依赖 data 的数据变化
直接使用 data 声明的数据
支持缓存不支持缓存
不支持异步支持异步
有 get 和 set 方法,当数据变化时,调用 set 方法可以深度监视 deep,加载就调用 immediate
监听的函数接收两个函数,newVal 和 oldVla
当需要进行数值计算,并且依赖于其它数据时,用 computed在某个数据变化时做一些事情或需要异步操作时,用 watch
computed 能做的watch 都能做到
4、组件通信(组件间传值)
  • props $emit
  • .sync v-model
  • $parent / $children $parent 获取父组件的实例,任意调用父组件的方法,修改父组件的数据
  • ref 父组件获取子组件实例,任意调用子组件的方法获取子组件的属性
  • provide / injectprpvide 父组件内部提供数据 inject 嵌套的子组件可以注入数据
  • $attrs / $listeners $attrs(没有被 props 接收的所有自定义属性) $listeners(可以获取所有的父组件传递过来的自定义事件)
  • eventBus 定义一个事件总线 使用$on 绑定 $emit 触发
  • vuex
  • 路由传参

https://www.wpgdadatong.com/cn/blog/detail?BID=B3650

https://juejin.cn/post/7110223595359436813

5、$nextTick

NextTick 是什么

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM

// 修改数据
this.message = "修改后的值";
// 此时DOM还没有更新
console.log(this.$el.textContent); // => '原始的值'
this.$nextTick(function () {
  // DOM 更新了
  console.log(this.$el.textContent); // => '修改后的值'
});
6、修饰符

引用:https://juejin.cn/post/7026867875990208543#heading-22

事件修饰符

stop

  • stop修饰符的作用是阻止冒泡

prevent?

  • prevent修饰符的作用是阻止默认事件(例如 a 标签的跳转)

capture

  • 事件流默认冒泡,使用 capture 进行捕获捕获

self

  • self修饰符作用是,只有点击事件绑定的本身才会触发事件

once

  • once修饰符的作用是,事件只执行一次

native?

  • native修饰符是加在自定义组件的事件上,保证事件能执行
  • native是用来是在父组件中给子组件绑定一个原生的事件,就将子组件变成了普通的 HTML 标签看待

passive

  • 当我们在监听元素滚动事件的时候,会一直触发 onscroll 事件,在 pc 端是没啥问题的,但是在移动端,会让我们的网页变卡,因此我们使用这个修饰符的时候,相当于给 onscroll 事件整了一个.lazy 修饰符
v-bind 修饰符

sync?

  • 父组件传值进子组件,子组件想要改变这个值时,可以这么做

  • // 父组件里
    <children :foo.sync="bar"></children>
    // 子组件里
    this.$emit('update:foo', newValue)
    

camel

.camel 修饰符允许在使用 DOM 模板时将 v-bind property 名称驼峰化,例如 SVG 的 viewBox property:

<svg :view-box.camel="viewBox"></svg>
鼠标修饰符

left right middle

  • 这三个修饰符是鼠标的左中右按键触发的事件
表单相关修饰符

trim?

  • trim修饰符的作用类似于 JavaScript 中的trim()方法,作用是把v-model绑定的值的首尾空格给过滤掉。

lazy

  • lazy修饰符作用是,改变输入框的值时 value 不会改变,当光标离开输入框时,v-model绑定的值 value 才会改变

number

  • number修饰符的作用是将值转成数字,但是先输入字符串和先输入数字,是两种情况:

先输入数字的话,只取前面数字部分

先输入字母的话,number修饰符无效

系统修饰符

.ctrl.alt.shift.meta

按键修饰符

配合键盘事件使用: .enter .tab .delete .esc .space .up .down left

7、图片上传 TODO??

FileReader 与 URL.createObjectURL 实现图片、视频上传预览

event.target.files 就是用户上传的图片信息

配合 cropperjs 可以实现图片裁剪功能

// 如果接口要求 Content-Type 是 multipart/form-data
// 则你必须传递 FormData 对象
8、v-for 和 v-if / v-if 和 v-show

为什么 v-if 和 v-for 不能同时使用?

  • v-if 不能和 v-for 一起使用的原因是 v-for 的优先级比 v-if 高,一起使用会造成性能浪费

  • 解决方案有两种,把 v-if 放在 v-for 的外层或者把需要 v-for 的属性先从计算属性中过滤一次

  • v-if 和 v-for 的优先级问题在 vue3 中不需要考虑,vue3 更新了 v-if 和 v-for 的优先级,使 v-if 的优先级高于 v-for

v-if 和 v-show

v-show隐藏则是为该元素添加display:nonev-if是将dom元素整个添加或删除

  • v-showfalse变为true的时候不会触发组件的生命周期
  • v-iffalse变为true的时候,触发组件的beforeCreatecreatebeforeMountmounted钩子,由true变为false的时候触发组件的beforeDestorydestoryed方法

性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗;

如果需要非常频繁地切换,则使用 v-show 较好

如果在运行时条件很少改变,则使用 v-if 较好

9、Vue.use()

Vue.use是用来安装插件的

用法:Vue.use(plugin)

  • 如果插件是一个对象,必须提供 install 方法。
  • 如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入
  • Vue.use(plugin)调用之后,插件的 install 方法就会默认接受到一个参数,这个参数就是 Vue

总结:Vue.use 是官方提供给开发者的一个 api,用来注册、安装类似 Vuex、vue-router、ElementUI 之类的插件的。

10、 跨域
什么是跨域

跨域问题是浏览器的同源策略所导致的

其中,域名、协议、端口号相同,称之为同源,如果不同,称之为跨源或跨域

跨域常见的解决方法:

  • 代理,常用
  • CORS,常用
  • JSONP
跨域解决方法 1-代理

代理适用的场景是:生产环境不发生跨域,但开发环境发生跨域

因此,只需要在开发环境使用代理解决跨域即可,这种代理又称之为开发代理

在实际开发中,只需要对开发服务器稍加配置即可完成

// vue 的开发服务器代理配置
// vue.config.js
module.exports = {
  devServer: {
    // 配置开发服务器
    proxy: {
      // 配置代理
      "/api": {
        // 若请求路径以 /api 开头
        target: "http://dev.taobao.com", // 将其转发到 http://dev.taobao.com
      },
    },
  },
};
跨域解决方法 2-CORS

阮一峰 CORS: https://www.ruanyifeng.com/blog/2016/04/cors.html

CORS是基于http1.1的一种跨域解决方案,它的全称是Cross-Origin Resource Sharing,跨域资源共享

CORS 需要浏览器和后端同时支持。

整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与同源的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信。

浏览器将 CORS 请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)

只要同时满足以下两大条件,就属于简单请求。

凡是不同时满足下面两个条件,就属于非简单请求。

(1) 请求方法是以下三种方法之一:

  • HEAD
  • GET
  • POST

(2)HTTP 的头信息不超出以下几种字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三个值application/x-www-form-urlencodedmultipart/form-datatext/plain
跨域解决方法 3-JSONP

JSONP 的做法是:当需要跨域请求时,不使用 AJAX,转而生成一个 script 元素去请求服务器,由于浏览器并不阻止 script 元素的请求,这样请求可以到达服务器。服务器拿到请求后,响应一段 JS 代码,这段代码实际上是一个函数调用,调用的是客户端预先生成好的函数,并把浏览器需要的数据作为参数传递到函数中,从而间接的把数据传递给客户端

JSONP 优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持 get 方法具有局限性,不安全可能会遭受 XSS 攻击。

11、cookie

什么是 cookie

cookie 是储存在用户本地终端上的数据,是网站为了识别用户跟踪会话而存储在用户本地终端中的文本数据

怎么操作

可以使用js-cookie插件

模块化开发时直接引入import Cookies from 'js-cookie'

js-cookie.js 常用的 API 和方法

设置 cookie

Cookies.set("name", "value", { expires: 7, path: "" }); //7天过期
Cookies.set("name", { foo: "bar" }); //设置一个json

读取 cookie

Cookies.get("name"); //获取cookie
Cookies.get(); //读取所有的cookie

删除 cookie

Cookies.remove("name"); //删除cookie时必须是同一个路径。
12、keep-alive 原理
keep-alive 是什么
  1. vue 自带的组件 >> 主要功能是缓存组件 >> 提升性能
  2. 使用场景:可以少网络请求,如果当前组件数据量比较大,就可以节省网络请求 >> 提升用户体验
  3. 举例:如果详情页面之间进行切换,就可以使用keep-alive进行缓存组件,防止同样的数据重复请求

keep-alive 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们

keep-alive可以设置以下props属性:

  • include - 字符串或正则表达式。只有名称匹配的组件会被缓存
  • exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存
  • max - 数字。最多可以缓存多少组件实例

关于keep-alive的基本用法:

<keep-alive>
  <component :is="view"></component>
</keep-alive>

匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值),匿名组件不能被匹配

设置了 keep-alive 缓存的组件,会多出两个生命周期钩子(activateddeactivated):

  • 首次进入组件时:beforeRouteEnter > beforeCreate > created> mounted > activated > … … > beforeRouteLeave > deactivated
  • 再次进入组件时:beforeRouteEnter >activated > … … > beforeRouteLeave > deactivated
使用场景

使用原则:当我们在某些场景下不需要让页面重新加载时我们可以使用keepalive

举个栗子:

当我们从首页–>列表页–>商详页–>再返回,这时候列表页应该是需要keep-alive

首页–>列表页–>商详页–>返回到列表页(需要缓存)–>返回到首页(需要缓存)–>再次进入列表页(不需要缓存),这时候可以按需来控制页面的keep-alive

在路由中设置keepAlive属性判断是否需要缓存

{
  path: 'list',
  name: 'itemList', // 列表页
  component (resolve) {
    require(['@/pages/item/list'], resolve)
 },
 meta: {
  keepAlive: true,
  title: '列表页'
 }
}

使用<keep-alive>

<div id="app" class='wrapper'>
    <keep-alive>
        <!-- 需要缓存的视图组件 -->
        <router-view v-if="$route.meta.keepAlive"></router-view>
     </keep-alive>
      <!-- 不需要缓存的视图组件 -->
     <router-view v-if="!$route.meta.keepAlive"></router-view>
</div>
缓存后如何获取数据

解决方案可以有以下两种:

  • beforeRouteEnter
  • actived
13、ref

作用:

  • 放到 dom 节点上 >> 获取原生 dom
  • 组件身上 >> 获取组件实例 >> 可以获取组件内部所有的方法和数据
14、scoped 原理是什么?

作用:使样式私有化(模块化),不对全局造成污染

原理:动态的给组件加上一个 hash 值,用属性选择器去匹配

15、$router 和 $route

$route:当前的路由信息对象,获取到路由参数、路径

  1. $route.path: 字符串,对应当前路由的路径,总是解析为绝对路径,如/foo/bar
  2. $route.params: 一个 key/value 对象,包含了 动态片段 和 全匹配片段,如果没有路由参数,就是一个空对象。
  3. ** r o u t e . q u e r y : ? ? 一个 k e y / v a l u e 对象,表示 U R L 查询参数。例如,对于路径 / f o o ? u s e r = 1 ,则有 route.query:** 一个 key/value 对象,表示 URL 查询参数。例如,对于路径 /foo?user=1,则有 route.query??一个key/value对象,表示URL查询参数。例如,对于路径/foo?user=1,则有route.query.user == 1,如果没有查询参数,则是个空对象。
  4. $route.hash: 当前路由的 hash 值 (不带#) ,如果没有 hash 值,则为空字符串。锚点
  5. $route.fullPath: 完成解析后的 URL,包含查询参数和 hash 的完整路径。
  6. $route.matched: 数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。
  7. $route.name: 当前路径名字
  8. $route.meta: 路由元信息

** r o u t e r ? ? :全局路由 v u e R o u t e r 的实例,挂载到 V u e 原型上 router**:全局路由 vueRouter 的实例,挂载到 Vue 原型上 router??:全局路由vueRouter的实例,挂载到Vue原型上router 属性,可以获取到全局路由配置信息,跳转方法

$router.replace({path:‘home’}),//替换路由,没有历史记录

$router.push(‘/login’) ,跳转到指定路由

$router.back()

$router.go()

16、发布订阅模式和观察者模式
发布订阅模式

发布订阅模式中有三个角色,发布者 Publisher ,信息中心 Event Channel ,订阅者 Subscriber

我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern)

通常是通过 on 事件订阅消息,emit 事件发布消息,remove 事件删除订阅

class PubSub {
  constructor() {
    // 事件中心
    // 存储格式: warTask: [], routeTask: []
    // 每种事件(任务)下存放其订阅者的回调函数
    this.events = {};
  }
  // 订阅方法
  subscribe(type, cb) {
    if (!this.events[type]) {
    }
    this.events[type].push(cb);
  }
  // 发布方法
  publish(type, ...args) {
    if (this.events[type]) {
      this.events[type].forEach((cb) => cb(...args));
    }
  }
  // 取消订阅方法
  unsubscribe(type, cb) {
    if (this.events[type]) {
      const cbIndex = this.events[type].findIndex((e) => e === cb);
      if (cbIndex !== -1) {
        this.events[type].splice(cbIndex, 1);
      }
    }
    if (this.events[type].length === 0) {
      delete this.events[type];
    }
  }
  unsubscribeAll(type) {
    if (this.events[type]) {
      delete this.events[type];
    }
  }
}
观察者模式

当对象之间存在一对多的依赖关系时,其中一个对象的状态发生改变,所有依赖它的对象都会收到通知,这就是观察者模式

  • 观察者(订阅者) – Watcher
    • update():当事件发生时,具体要做的事情
  • 目标(发布者) – Dep
    • subs 数组:存储所有的观察者
    • addSub():添加观察者
    • notify():当事件发生,调用所有观察者的 update() 方法
  • 没有事件中心
总结
设计模式观察者模式发布订阅模式
主体Watcher 观察者、Dep 目标对象Publisher 发布者、Event Channel 信息中心、Subscribe 订阅者
主体关系
Dep 中通过 subs 记录 WatcherPublisher 和 Subscribe 不想不知道对方,通过中介联系
优点角色明确,Watcher 和 Dep 要遵循约定的成员方法松散耦合,灵活度高,通常应用在异步编程中
缺点紧耦合当事件类型变多时,会增加维护成本
使用案例双向数据绑定事件总线 EventBus
17、vue 响应式原理(数据劫持)
18、eventBus
19、v-model 与.sync

相同点:都是语法糖,都可以实现父子组件中的数据的双向通信。

//  v-model
<son  v-model="num"/> //父组件使用子组件
model:{
  	prop:'newValue', // 默认为 value 可以使用prop自定义属性名
  	event:"updateValue", // event 修改事件名 默认为input
},
props: { // 子组件接收
	value: {  // 默认为value
		type: Number,
	}
}
// .sync
<son :title.sync="doc.title"></son>   // 父组件
props:{
	title:{
	  type:...
	}
}
this.$emit('update:title', newTitle)  // 子组件

区别点:格式不同: v-model=“num”, :num.sync=“num”

v-model: @input + value
:num.sync: @update:num

另外需要特别注意的是: v-model只能用一次;.sync可以有多个

20、如何做样式穿透

背景:修改当前组件嵌套的子组件内部的样式

问题

  1. 如果不添加 scoped,可能影响全局样式
  2. 如果添加 scoped,嵌套的子组件内部样式不能生效

如何解决:添加/deep/ / ::v-deep

scss: 使用::v-deep

less: 使用 /deep/

21、你对SPA单页面的理解,它的优缺点分别是什么?

SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。

单页应用优缺点

优点:

  • 具有桌面应用的即时性、网站的可移植性和可访问性
  • 用户体验好、快,内容的改变不需要重新加载整个页面,SPA 相对对服务器压力小;
  • 良好的前后端分离,分工更明确

缺点:

  • SEO(搜索引擎)难度较大
  • 首次渲染速度相对较慢
  • 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
22、怎么理解 Vue 的单向数据流

①单向数据流是指数据从父组件传向子组件,子组件没有权限直接修改该数据;
②子组件需要在 data 或者 computed 中重新定义变量来接收父组件传来的值,以便修改;
③子组件可以通过 $emit 的方式通知父组件修改值,再重新传回给子组件;

二、JS

1、手动实现防抖节流

防抖:在限定时间内,总是执行最后一次 ( 类似回城,打断就得重新回 )

节流:在限定时间内,只会执行第一次 ( 类似技能需要冷却时间到了才能用 )

/**
 * debounce 防抖
 * @param fn [function] 需要防抖的函数
 * @param delay [number] 毫秒,防抖期限值
 */
function debounce(fn, delay) {
  let timer = null; //借助闭包
  return function (...arg) {
    if (timer) {
      //进入该分支语句,说明当前正在一个计时过程中,并且又触发了相同事件。
      // 所以要取消当前的计时,重新开始计时
      clearTimeout(timer);
    }
    timer = setTimeout(() => {
      fn.apply(this, arg); // 使用apply将fn函数的this指向修改为return后的function
    }, delay); // 进入该分支说明当前并没有在计时,那么就开始一个计时
  };
}
/**
 * debounce 节流
 * @param fn [function] 需要节流的函数
 * @param delay [number] 毫秒
 */
function throttle(fn, delay) {
  let valid = false; // 节流阀
  return function (...arg) {
    if (valid) {
      //休息时间 暂不接客
      return;
    }
    // 工作时间,执行函数并且在间隔期内把状态位设为无效
    valid = true;
    setTimeout(() => {
      fn.apply(this, arg);
      valid = false;
    }, delay);
  };
}
2、let、const、var 的区别

1.是否存在变量提升?

  • var声明的变量存在变量提升(将变量提升到当前作用域的顶部)。即变量可以在声明之前调用,值为undefined

  • letconst不存在变量提升。即它们所声明的变量一定要在声明后使用,否则报ReferenceError

2.是否存在暂时性死区?

let和const存在暂时性死区。即只要块级作用域内存在 let 命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响

3.是否允许重复声明变量?

  • var允许重复声明变量。

  • letconst在同一作用域不允许重复声明变量。

4.是否存在块级作用域?

  • var 不存在块级作用域。
  • let 和 const 存在块级作用域。
    • 块作用域由{ }包括,if语句和for语句里面的{ }也属于块作用域

5. 是否能修改声明的变量?

  • varlet可以。
  • const声明一个只读的常量。一旦声明,常量的值就不能改变。const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。
3、箭头函数与普通函数区别

1.箭头函数是匿名函数,不能作为构造函数,不能使用 new

2.箭头函数内没有arguments,可以用展开运算符...解决

3.箭头函数的 this,始终指向父级上下文(箭头函数的this取决于定义位置父级的上下文,跟使用位置没关系,普通函数this指向调用的那个对象)

4.箭头函数不能通过call() 、 apply() 、bind()方法直接修改它的 this 指向。(call、apply、bind会默认忽略第一个参数,但是可以正常传参)

5.箭头函数没有原型属性

4、Promise
  • 状态

    • 进行中pending

    • 已成功resolved

    • 已失败rejected

  • 特点

    • 对象的状态不受外界影响

    • 一旦状态改变就不会再变,任何时候都可得到这个结果

  • 声明:new Promise((resolve, reject) => {})

  • 出参

    • resolve:将状态从未完成变为成功,在异步操作成功时调用,并将异步操作的结果作为参数传递出去

    • reject:将状态从未完成变为失败,在异步操作失败时调用,并将异步操作的错误作为参数传递出去

① 什么是 Promise?

  • Promise,简单说就是一个容器,包含异步操作结果的对象
  • 从语法上说,promise 是一个对象,从它可以获取异步操作的的最终状态(成功或失败)。
  • Promise 是一个构造函数,对外提供统一的 API,自己身上有 all、reject、resolve 等方法,原型上有 then、catch 等方法。

② Promise 有什么用?

解决回调地狱

③Promise 有哪些方法

  • **then():**分别指定resolved状态rejected状态的回调函数

    • 第一参数:状态变为resolved时调用

    • 第二参数:状态变为rejected时调用(可选)

    • 链式调用 promise.then()then 方法返回一个 Promise 对象,其允许方法链,从而创建一个 promise 链

  • catch():指定发生错误时的回调函数

  • Promise.resolve():将对象转为 Promise 对象 等价于 new Promise(resolve => resolve())

    • Promise 实例:原封不动地返回入参

    • thenable 对象

      thenable对象指的是具有then方法的对象

      Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法

    • 不具有 then()的对象:将此对象转为 Promise 对象并返回,状态为resolved

    • 不带参数:返回 Promise 对象,状态为resolved

  • Promise.reject():将对象转为状态为rejected的 Promise 对象(等价于new Promise((resolve, reject) => reject()))

  • Promise.all():

    并发,发起多个并发请求,将多个实例包装成一个新实例(数组形式),然后在所有 promise 都被解决后执行一些操作(齐变更再返回)

    • 成功:只有全部实例状态变成fulfilled( 成功 ),最终状态才会变成fulfilled
    • 失败:其中一个实例状态变成rejected,最终状态就会变成rejected
    • 每一个 promise 成功的值,会按照传入的顺序返回数组内
    • 用 all 方法进行接口请求,就算其中有失败的情况,别的请求也会进行,但最后的状态还是rejected
  • Promise.race():

    赛跑机制

    将多个实例包装成一个新实例,返回全部实例状态优先变更后的结果(先变更先返回)

    • 成功失败:哪个实例率先改变状态就返回哪个实例的状态
  • Promise.finally():指定不管最后状态如何都会执行的回调函数
  • **Promise.allSettled()**?:将多个实例包装成一个新实例,返回全部实例状态变更后的状态数组(齐变更再返回)
    • 成功:成员包含statusvaluestatusfulfilledvalue为返回值
    • 失败:成员包含statusvaluestatusrejectedvalue为错误原因
  • Promise.any():将多个实例包装成一个新实例,返回全部实例状态变更后的结果数组(齐变更再返回)
  • 成功:其中一个实例状态变成fulfilled,最终状态就会变成fulfilled

  • 失败:只有全部实例状态变成rejected,最终状态才会变成rejected

  • Promise.try():不想区分是否同步异步函数,包装函数为实例,使用then()指定下一步流程,使用catch()捕获错误

常见的错误

  • Uncaught TypeError: undefined is not a promise

    如果在控制台中收到 Uncaught TypeError: undefined is not a promise 错误,则请确保使用 new Promise() 而不是 Promise()

  • UnhandledPromiseRejectionWarning

    这意味着调用的 promise 被拒绝,但是没有用于处理错误的 catch。 在 then 之后添加 catch 则可以正确地处理

扩展:手写 Promise

5、数据类型

原始数据类型(基本类型):按值访问,可以操作保存在变量中实际的值。

  • 空值null):用于未知的值 —— 只有一个 null 值的独立类型。
  • 未定义undefined): 用于未定义的值 —— 只有一个 undefined 值的独立类型。
  • 布尔值boolean):用于 truefalse
  • 数字number):用于任何类型的数字:整数或浮点数,在 ±(253-1) 范围内的整数。
  • 字符串string):用于字符串:一个字符串可以包含 0 个或多个字符,所以没有单独的单字符类型。
  • 符号symbol):用于唯一的标识符。

引用类型(复杂数据类型):引用类型的值是保存在内存中的对象。

  • 对象(Object
    • 数组对象(Array)
    • 函数对象(Function)
    • 布尔对象(Boolean)
    • 数字对象(Number)
    • 字符串对象(String)
    • 日期对象(Date)
    • 正则对象(RegExp)
    • 错误对象(Error)

?? 注意: 与其他语言不同的是,JavaScript 不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。所以引用类型的值是按引用访问的。

6、检测数据类型的常用方法

typeof、instanceof、constructor、Object.prototype.toString.call()

1 .typeof
console.log(
  typeof 100, //"number"
  typeof undefined, //"undefined"
  typeof null, //"object"
  typeof function () {
    console.log("aaa");
  }, //"function"
  typeof new Number(100), //'object'
  typeof new String("abc"), // 'string'
  typeof new Boolean(true) //'boolean'
);

typeof 可以正常检测出:number、boolean、string、object、function、undefined、symbol、bigint

  • 检测基本数据类型,null 会检测 object,因为 null 是一个空的引用对象
  • 检测复杂数据类型,除 function 外,均为 object
2 . instanceof

instanceof运算符需要指定一个构造函数,或者说指定一个特定的类型,用来判断这个构造函数的原型是否在给定对象的原型链上

基本数据类型中:Number,String,Boolean。字面量值不可以用 instanceof 检测,但是构造函数创建的值可以

注意:null 和 undefined 都返回了 false,这是因为它们的类型就是自己本身,并不是 Object 创建出来它们,所以返回了 false。

console.log(
  100 instanceof Number, //false
  undefined instanceof Object, //false
  [1, 2, 3] instanceof Array, //true
  new Error() instanceof Error //true
);
3 .constructor

constructor 是 prototype 对象上的属性,指向构造函数。根据实例对象寻找属性的顺序,若实例对象上没有实例属性或方法时,就去原型链上寻找,因此,实例对象也是能使用 constructor 属性的。可以检测出字面量方式创建的对象类型

如果输出一个类型的实例的 constructor,就如下所示:

console.log(new Number(123).constructor);
//? Number() { [native code] }

可以看到它指向了 Number 的构造函数,因此,可以使用num.constructor === Number来判断一个变量是不是 Number 类型的

除了 undefined 和 null 之外,其他类型都可以通过 constructor 属性来判断类型。

var num = 123;
var str = "abcdef";
var bool = true;
var arr = [1, 2, 3, 4];

// undefined和null没有constructor属性
console.log(
  num.constructor === Number,
  str.constructor === String,
  bool.constructor === Boolean,
  arr.constructor === Array
);
//所有结果均为true
4 . 使用 Object.prototype.toString.call()检测对象类型?
const toString = Object.prototype.toString;

toString.call(123); //"[object Number]"
toString.call(undefined); //"[object Undefined]"
toString.call(null); //"[object Null]"
toString.call(/^[a-zA-Z]{5,20}$/); //"[object RegExp]"
toString.call(new Error()); //"[object Error]"

可以使用Object.prototype.toString.call(obj).slice(8,-1)来判断并截取

使用Object.prototype.toString.call()的方式来判断一个变量的类型是最准确的方法

5 . 自己封装函数
function getType(obj) {
  const type = typeof obj;
  if (type !== "object") {
    return type;
  }
  //如果不是object类型的数据,直接用typeof就能判断出来
  //如果是object类型数据,准确判断类型必须使用Object.prototype.toString.call(obj)的方式才能判断
  return Object.prototype.toString.call(obj).replace(/^\[object (\S+)]$/, "$1");
}
6、isArray

isArray 可以检测出是否为数组

const arr = [];
Array.isArray(arr); // true
7、数组的常用方法有哪些
一、操作方法

下面前三种是对原数组产生影响的增添方法,第四种则不会对原数组产生影响

push( ) unshift( ) splice( ) concat( )

push()

push()方法接收任意数量的参数,并将它们添加到数组末尾,返回数组的最新长度

unshift()

unshift()在数组开头添加任意多个值,然后返回新的数组长度

splice

传入三个参数,分别是开始位置、0(要删除的元素数量)、插入的元素,返回的是空数组

concat()

合并

首先会创建一个当前数组的副本,然后再把它的参数添加到副本末尾,最后返回这个新构建的数组,不会影响原始数组

let colors = ["red"].concat("yellow", ["black"]);// ["red", "yellow", "black"]

下面三种都会影响原数组,最后一项不影响原数组:

pop() shift() splice() slice()

pop()

pop() 方法用于删除数组的最后一项,同时减少数组的length 值,返回被删除的项

shift()

shift()方法用于删除数组的第一项,同时减少数组的length 值,返回被删除的项

splice()

传入两个参数,分别是开始位置,删除元素的数量,返回包含删除元素的数组

slice()

slice(开始索引, 结束索引) ,返回一个新数组,不会影响原始数组

即修改原来数组的内容,常用splice

splice()

传入三个参数,分别是(开始位置,要删除元素的数量,要插入的任意多个元素),返回删除元素的数组

会改变原数组

查找元素,返回元素坐标或者元素值

indexOf( ) includes( ) find( )

indexOf()

返回要查找的元素在数组中的位置,如果没找到则返回 -1

includes()

返回要查找的元素在数组中的位置,找到返回true,否则false

find()

返回第一个匹配的元素

二、排序方法

reverse()sort()

reverse()

翻转

sort()

排序

function sortArr(a, b) {
  return a - b; // 升序
  return b - a; // 降序
}
三、转换方法
join()

join() 方法接收一个参数,即字符串分隔符,返回包含所有项的字符串,转为字符串

四、迭代方法

常用来迭代数组的方法(除 forEach 外其他都不会对空数组进?检测、不会改变原始数组)有如下:

some() every() forEach() filter() map()

some()

对数组每一项都运行传入的测试函数,如果至少有 1 个元素返回 true ,则这个方法返回 true

every()

对数组每一项都运行传入的测试函数,如果所有元素都返回 true ,则这个方法返回 true

forEach()

对数组每一项都运行传入的函数,没有返回值

filter()

对数组每一项都运行传入的函数,函数返回 true 的项会组成数组之后返回

map()

映射 对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组

8、深浅拷贝

基本类型数据保存在在内存中

引用类型数据保存在内存中,引用数据类型的变量是一个指向堆内存中实际对象的引用,存在栈中

深拷贝和浅拷贝的区别

1.浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用。

如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址,即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址

2.深拷贝: 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”

浅拷贝

数组浅拷贝:

// 直接遍历
function shallowCopy(arr) {
  const newArr = [];
  arr.forEach((item) => newArr.push(item));
  return newArr;
}
// slice
const arr2 = [1, 2, 3, 4, 5];
const newArr2 = arr2.slice();
//    concat() 合并空数组实现
const arr3 = [11, 22, 33, 44, 55];
const newArr3 = arr3.concat([]);

对象浅拷贝:

// 直接遍历
function shallowCopy(obj) {
  const newObj = {};
  for (let item in obj) {
    newObj[item] = obj[item];
  }
  return newObj;
}

// 使用拓展运算符
const obj2 = { name: "Bob", age: 17 };
const newObj2 = { ...obj2 };
深拷贝

用深拷贝最后要递归到全部是基本值,不然可能会陷入死循环/循环引用,导致栈溢出

TODO(待理解)?: 处理过的数据使用 map 结构缓存起来 >> 递归的时候碰到相同的数据 >> 直接使用缓存里面的

1. 先转换成字符串,在转换成(数组/对象) JSON.parse(JSON.stringify(XXXX))

有一个缺点 里面的函数不能拷贝

const array = [{ number: 1 }, { number: 2 }, { number: 3 }];
const str = JSON.stringify(array);
const copyArray = JSON.parse(str);

递归实现简单的深拷贝:

function deepClone(obj = {}) {
  if (typeof obj !== "object" || obj == null) {
    // obj 是 null ,或者不是对象和数组,直接返回
    return obj;
  }
  // 初始化返回结果
  let result;
  obj instanceof Array ? (result = []) : (result = {});
  for (let key in obj) {
    // 保证 key 不是原型的属性
    if (obj.hasOwnProperty(key)) {
      // 递归调用!!!
      result[key] = deepClone(obj[key]);
    }
  }
  // 返回结果
  return result;
}

小结

前提为拷贝类型为引用类型的情况下:

  • 浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址
  • 深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址
9、闭包

什么是闭包

通俗地讲闭包就是在一个函数里边再定义一个函数,这个内部函数一直保持有对外部函数中作用域的访问权限(小房间一直可以有大房子的访问权限)

闭包的作用

  1. 访问其他函数内部变量
  2. 保护变量不被 JS 的垃圾回收机制回收
  3. 避免全局变量被污染 方便调用上下文的局部变量 加强封装性

闭包的优点

(一)变量长期驻扎在内存中
(二)另一个就是可以重复使用变量,并且不会造成变量污染
① 全局变量可以重复使用,但是容易造成变量污染。不同的地方定义了相同的全局变量,这样就会产生混乱。
② 局部变量仅在局部作用域内有效,不可以重复使用,不会造成变量污染。
③ 闭包结合了全局变量和局部变量的优点。可以重复使用变量,并且不会造成变量污染

闭包的缺点

由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在 IE 中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

10、数组去重

https://segmentfault.com/a/1190000016418021

Set 去重
const arr = [1, 2, 3, 3, 3, 2, 3, 4, 5, 4, 4];
const newArr = [...new Set(arr)]; // [ 1, 2, 3, 4, 5 ]
indexOf 去重
function unique(arr) {
  let newArr = [];
  for (let i = 0; i < arr.length; i++) {
    if (newArr.indexOf(arr[i]) === -1) newArr.push(arr[i]);
  }
  return newArr;
}
splice 去重
function unique(arr) {
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[i] === arr[j]) {
        arr.splice(j, 1);
        j--;
      }
    }
  }
  return arr;
}
includes 去重
function unique(arr) {
  const newArr = [];
  for (let i = 0; i < arr.length; i++) {
    if (!newArr.includes(arr[i])) {
      newArr.push(arr[i]);
    }
  }
  return newArr;
}
filter 去重
function unique(arr) {
  return arr.filter(function (item, index, arr) {
    //当前元素,在原始数组中的第一个索引===当前索引值,否则返回当前元素
    return arr.indexOf(item) === index;
  });
}
findIndex数组对象去重
function unique(arr) {
  // todo:待总结数组去重方法
  // 方法一
  return arr.filter((item, index) => {
    return arr.findIndex((child) => child.id === item.id) === index;
  });
}
Map数组对象去重
function unique(arr) {
  const res = new Map();
  return arr.filter((item) => !res.has(item.id) && res.set(item.id, 1));
}
reduce 数组对象去重
function unique(arr) {
  let obj = {};
  return arr.reduce((pre, item) => {
    obj[item.id] ? "" : (obj[item.id] = true && pre.push(item));
    return pre;
  }, []);
}
lodash 库数组和数组对象去重:
import { isEqual, uniqWith, uniqBy } from "lodash";
let arr = [
  { id: 1, name: "sli", year: 2012 },
  { id: 2, name: "ap", year: 2015 },
  { id: 1, name: "alslion", year: 2012 },
  { id: 3, name: "pose", year: 2012 },
  { id: 3, name: "pose", year: 2012 },
];
// 根据id去掉相同的元素:
console.log(uniqBy(arr, "id"));
// 深检查数组每一项进行去重:
console.log(uniqWith(arr, isEqual));
11、逻辑运算符 && 和 ||

||运算符: 条件 1 || 条件 2

  • 若条件 1 为 true、返回条件 1
  • 若条件 1 为 false、不管 || 后面是 true 还是 false、都是返回||后面的值、即则返回条件 2;
console.log(0 || ""); // ''
console.log("" || 0); // 0

&&运算符: 条件 1 && 条件 2

  • 若条件 1 为 false、无论条件 2 为 true 或 false, 都将返回条件 1 的值;
  • 若条件 1 为 true, 无论条件 2 为 true 或者 false, 都将返回条件 2 的值;
console.log(0 && ""); // 0
console.log("" && 0); // ''
12、new 的过程

分析一下 new 的整个过程:

  • 1、创建一个空对象
  • 2、this 指向 obj,并调用构造函数
  • 3、继承构造函数的原型
  • 4、返回对象

简单实现一下 new:

function myNew(fn, ...args) {
  // 第一步:创建一个空对象
  const obj = {};
  // 第二步:this指向obj,并调用构造函数
  fn.apply(obj, args);
  // 第三步:继承构造函数的原型
  obj.__proto__ = fn.prototype;
  // 第四步:返回对象
  return obj;
}
13、事件循环 Event Loop (结合着14条讲)

是什么?

首先,JavaScript是一门单线程的语言,意味着同一时间内只能做一件事,如果前面一个任务耗时太长,后续的任务不得不等待,可能会导致程序假死的问题,但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环

JavaScript中,所有的任务都可以分为

  • 同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行
  • 异步任务:异步执行的任务,比如ajax网络请求,setTimeout定时函数等

同步任务进入主线程,即主执行栈,异步任务进入任务队列,主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。过程不断重复就是事件循环

14、async 和 await 宏任务 和 微任务

https://www.mianshiya.com/qd/bf4a0bf261c7e2500090d9482499675f

宏任务 和 微任务

下面代码执行顺序是什么

console.log(1)  // 同步

setTimeout(()=>{  // 宏任务
    console.log(2)  
}, 0)

new Promise((resolve, reject)=>{
    console.log('new Promise') // 同步
    resolve()
}).then(()=>{
    console.log('then')  // 微任务
})

console.log(3) // 同步

1=>'new Promise'=> 3 => 'then' => 2

1、什么是宏任务和微任务

Javascript 把异步任务又做了进一步的划分,异步任务又分为两类,分别是:

微任务:

一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前

常见的微任务有:

  • Promise.then
  • MutaionObserver
  • Object.observe(已废弃;Proxy 对象替代)
  • process.nextTick(Node.js)

宏任务:

宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求就不太符合

常见的宏任务有:

  • script (可以理解为外层同步代码)
  • setTimeout/setInterval
  • UI rendering/UI事件
  • postMessage、MessageChannel
  • setImmediate、I/O(Node.js)

promise里面的代码是同步任务 promise的方法.then()等是异步任务 微任务

2、宏任务和微任务的执行顺序

每一个宏任务执行完之后,都会检查是否存在待执行的微任务,如果有,则执行完所有微任务之后,再继续执行下一个宏任务。

1、微任务比宏任务的执行时间要早

2、微任务在DOM渲染之前执行,宏任务在DOM渲染之后执行

async与await

async 是异步的意思,await是等待。所以可以理解async就是用来声明一个异步方法,而 await是用来等待异步方法执行

async

async函数返回一个promise对象

await

正常情况下,await命令后面是一个 Promise对象,返回该对象的结果。如果不是 Promise对象,就直接返回对应的值

不管await后面跟着的是什么,await都会阻塞后面的代码(加入微任务列表)

下面代码执行顺序是什么:

async function async1() {
    console.log('async1 start')
    await async2()
    console.log('async1 end')
}
async function async2() {
    console.log('async2')
}
console.log('script start')
setTimeout(function () {
    console.log('settimeout')
})
async1()
new Promise(function (resolve) {
    console.log('promise1')
    resolve()
}).then(function () {
    console.log('promise2')
})
console.log('script end')

分析过程:

  1. 执行整段代码,遇到 console.log('script start') 直接打印结果,输出 script start
  2. 遇到定时器了,它是宏任务,先放着不执行
  3. 遇到 async1(),执行 async1 函数,先打印 async1 start,下面遇到await怎么办?先执行 async2,打印 async2,然后阻塞下面代码(即加入微任务列表),跳出去执行同步代码
  4. 跳到 new Promise 这里,直接执行,打印 promise1,下面遇到 .then(),它是微任务,放到微任务列表等待执行
  5. 最后一行直接打印 script end,现在同步代码执行完了,开始执行微任务,即 await下面的代码,打印 async1 end
  6. 继续执行下一个微任务,即执行 then 的回调,打印 promise2
  7. 上一个宏任务所有事都做完了,开始下一个宏任务,就是定时器,打印 settimeout

所以最后的结果是:script startasync1 startasync2promise1script endasync1 endpromise2settimeout

15、call、apply、bind 的区别
  • 都可以改变 this 指向
  • call 和 apply 会立即执行,bind 不会,而是返回一个函数
  • call 和 bind 可以接收多个参数apply 只能接受两个,第二个是数组
  • bind 参数可以分多次传入
16、继承

继承的是 属性 和 原型方法

Class 继承

ES6:https://es6.ruanyifeng.com/#docs/class-extends

Class 可以通过extends关键字实现继承,让子类继承父类的属性和方法

子类如果写 constructor()就必须要写 super(),且要写在最前面,否则报错,只有super()方法才能让子类实例继承父类。

class Parent {
  constructor(x,y) {
    this.x = x
		this.y = y
		...
  }
	toString() {
    ...
  }
}

class Son extends Parent {
  constructor(x, y, color) {
    super(x, y); // 调用父类的constructor(x, y)
    this.color = color;
  }
  toString() {
    return this.color + ' ' + super.toString(); // 调用父类的toString()
  }
}
ES5 继承

js 中有很多中继承的方式,不过每一种继承方式都有优缺点,重点掌握 ES5 继承,别的继承方式基本都是 ES5 继承的语法糖

先创造子类实例,通过Parent.call(this, arg1, arg2...)将父类的属性方法添加到this上,继承了父类的属性

再通过 Son.prototype = Object.create( Father.prototype )将父类的原型继承过来

最后可以通过Son.prototype.constructor = Son 将子类的原型指到子类身上

function Father(name) {
  this.name = name;
}

Father.prototype.get = function () {
  return "黑马";
};

// 继承的是 属性 和 原型方法
function Son(name) {
  Father.call(this, name);
}

// Son.prototype = new Father() // 相互影响 会存在一个 {name:undefined}

// Object.create 创造出一个空对象
// 让当前对象的__proto__ 指向传入的对象
Son.prototype = Object.create(Father.prototype);
Son.prototype.constructor = Son;

const son = new Son("程序员");
console.log(son);
console.log(son.get() + son.name); // 黑马程序员
17、原型链

什么是原型原型链?

每个函数都有一个 prototype 原型(原型就是对象),原型对象有一个 constructor 属性,指向的是构造函数

访问对象的某一个属性或者方法时,会从对象自身查找,如果查找不到,就会去原型链上去找,原型的最终目的就是让所有的实例能够共享其属性和方法

查找顺序:

自身 >__proto__> 构造函数的原型对象 >__proto__> Object 的原型对象 >__proto__> null

18、堆和栈 TODO ??

https://juejin.cn/post/6844903618999500808

19、ES6 Set 和 Map TODO ??
20、includes

includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false

语法

arr.includes(valueToFind[, fromIndex])
  • valueToFind

    需要查找的元素值。Note: 使用 includes()比较字符串和字符时是区分大小写。

  • fromIndex 可选

    fromIndex 索引处开始查找 valueToFind。如果为负值,则按升序从 array.length + fromIndex 的索引开始搜 (即使从末尾开始往前跳 fromIndex 的绝对值个索引,然后往后搜寻)。默认为 0。

[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
[1, 2, NaN].includes(NaN); // true
21、find

find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined

arr.find(callback[, thisArg])
  • callback

    在数组每一项上执行的函数,接收 3 个参数:element当前遍历到的元素。index可选当前遍历到的索引。array可选数组本身。

  • thisArg可选

    执行回调时用作 this 的对象。

三、Git

1、列举工作中常用的几个 git 命令?
git rm                       删除工作区文件,并将这次删除放入暂存区
git add                      增加指定文件到暂存区
git init                     新建初始化 git 代码库
git status                   显示有变更的文件
git branch                   列出所有分支
git commit -m [message]      提交暂存区到仓库区,可选填备注信息 message
git checkout -b [branch]     新建分支,并切换到该分支

四、HTML-C3

1、盒子水平垂直局中的方法
flex 布局
 /* html代码: */
     <div class="wrap">
        <div class="box">
        </div>
    </div>
					/* CSS代码: */
          .wrap{
              /* 设置为弹性布局 */
              display: flex;
              /* 子元素在主轴对齐方式为居中 */
              justify-content: center;
              /* 交叉轴在y轴对齐 */
              align-items: center;
          }

Position + Transform
 /* html代码: */
     <div class="wrap">
        <div class="box">
        </div>
    </div>
/* CSS代码: */
          .wrap{
              width: 600px;
              height: 600px;
              border: 2px solid black;
               /* 设置为相对定位,用来作为绝对定位元素的容器块。 */
              position: relative;
          }
          .box{
              width:50px;
              height: 50px;
              /* 设置为绝对定位,位置在父容器的中心 */
              position: absolute;
              margin: auto;
              left: 50%;
              top:50%;
              /* 向回移动自身一半的长宽 */
              transform: translateX(-50%) translateY(-50%);
          }
利用Position + margin:auto
    <style>
       .father {
         margin: 100px auto;
         width: 500px;
         height: 300px;
         border: 1px solid #0a3b98;
         position: relative;
       }

        .son {
          width: 100px;
          height: 40px;
          background: #f0a238;
          position: absolute;
          top: 0;
          left: 0;
          right: 0;
          bottom: 0;
          margin: auto;
        }
      </style>
      <div class="father">
        <div class="son"></div>
      </div>
inline-block
 /* html代码: */
      <div class="wrap">
        <div class="box">
        </div>
    </div>
/* CSS代码: */
          .wrap{
              width: 600px;
              border: 2px solid black;
              /* 设置行高为600px */
              line-height: 600px;
              /* 让子盒子水平居中 */
              text-align: center;
          }
          .box{
              height: 50px;
              width: 50px;
              /* 设置为块级元素 */
              display: inline-block;
              /* 设置为垂直居中 */
              vertical-align: middle;
              background-color: red;
          }
table 布局
    <style>
         .father {
             display: table-cell;
             width: 200px;
             height: 200px;
             background: skyblue;
             vertical-align: middle;
             text-align: center;
         }
          .son {
              display: inline-block;
              width: 100px;
              height: 100px;
              background: red;
          }
      </style>
      <div class="father">
          <div class="son"></div>
      </div>
总结

根据元素标签的性质,可以分为:

  • 内联元素居中布局
  • 块级元素居中布局
内联元素居中布局

水平居中

  • 行内元素可设置:text-align: center
  • flex 布局设置父元素:display: flex ; justify-content: center

垂直居中

  • 单行文本父元素确认高度:height === line-height
  • 多行文本父元素确认高度:disaply: table-cell; vertical-align: middle
块级元素居中布局

水平居中

  • 定宽: margin: 0 auto
  • 绝对定位 + left:50% + margin: 负自身一半

垂直居中

  • position: absolute 设置 lefttopmargin-leftmargin-top(定高)
  • display: table-cell
  • transform: translate(x, y)
  • flex (不定高,不定宽)
  • grid(不定高,不定宽),兼容性相对比较差
2、BFC(经典面试题)

概念:

Block Formatting Context,翻译过来就是块级格式化上下文

bfc 实际是一种属性,拥有这种属性后,就会让该渲染区域独立,并且该渲染区域中的内容布局不会影响到外界

如何触发:

  • 根元素(html)

  • float 属性不为 none

  • position 为 absolute 或 fixed

  • display 为 inline-block, table-cell, table-caption, flex, inline-flex

  • overflow 不为 visible

解决什么问题

  • 外边距重叠
    • 外边距重叠,要注意这不是 bug,规范就是这样的,当两个盒子上下同时拥有上下间距,会取最大值
  • 清除浮动
  • 当子盒子开启 float 后会影响后面的布局以及盒子高度
  • 浮动覆盖
    • 由于浮动导致盒子被覆盖
3、盒模型

盒模型主要分为 4 部分:内容、外边距、内边距、边框

Css3 盒子模型可以通过 box-sizing 来改变

标准盒模型(W3C 标准):content-box 盒子实际宽度加上 padding 和 border

ie 盒模型/怪异盒模型/c3 盒模型:box-sizing: border-box; 设置 width 后,实际盒子的宽度就固定为该宽度,包含了 内容 + padding + border

4、flex:1
flex:1 → {
  flex-grow: 1;
  flex-shrink: 1;
  flex-basis: 0%;
}
  • flex-grow:1
    • flex-grow 是用来增大盒子的,比如,当父盒子的宽度大于子盒子的宽度,父盒子的剩余空间可以利用 flex-grow 来设置子盒子增大的占比
  • flex-shrink: 1
    • flex-shrink 用来设置子盒子超过父盒子的宽度后,进行缩小的比例取值
  • flex-basis: 0%
    • 设置盒子的基准宽度,并且 basis 和 width 同时存在会把 width 干掉
5、c3 新属性
  • c3 盒模型 box-sizing
  • flex 布局
  • transition 过渡
  • transform2D 转换
  • background-size 背景缩放
  • border-radius 圆角
6、

五、HTTP

HTTP 缓存

https://juejin.cn/post/6844904153043435533#heading-1

HTTP缓存

HTTP 状态码
  • 1xx - 信息响应 - 传达传输协议级别的信息
  • 2xx - 成功的响应 - 表示客户端的请求已成功接受
  • 3xx - 重定向 - 表示客户端必须采取一些额外的操作才能完成其请求
  • 4xx - 客户端错误 - 此类错误状态码指向客户端
  • 5xx - 服务端错误 - 服务器对这些错误状态代码负责
信息响应
  • 100:接受的请求正在处理,信息类状态码
成功的响应
  • 200:成功(OK) — 请求成功
  • 201:正创建(Created) — 请求已完成,已创建新资源
重定向
  • 301:永久移动(Moved Permanently) — 资源永久移动到新的 URL
  • 302:临时移动(Moved Temporarily) — 资源临时移动到新的 URL
  • 304:未修改(Not Modified)— 当协商缓存命中时会返回这个状态码。
客户端错误

此类错误状态代码指向客户端

  • 400:请求错误(Bad Request) — 服务器无法理解和处理请求
  • 401:未经授权(Unauthorized) — 需要验证,用户尚未验证
  • 403:禁止(Forbidden) — 对资源的访问权限不足
  • 404:未找到(Not Found) — 找不到请求的资源
服务端错误
  • 500:内部服务器错误(Internal Server Error)— 通用未处理的服务器错误

  • 503:服务不可用(Service Unavailable)— 服务器暂时无法处理请求

    1.浏览器缓存机制
    2.get 和 post 的区别

    3.$ nextTick(),原理

    4.防抖节流

    5.组件通信方式

    6.生命周期
    7.promise
    8.promise.all 方法什么时候走成功什么时候走失败;
    9.v-if、v-for
    10、对象和数组的响应式区别;
    11、keep-alive;
    12、组件通信的方式;
    13.vue 数据响应式如何实现
    14.vue for 循环的时候 key 的作用;
    15、call、apply、bind 区别
    15、vue.use 做了什么东西
    16、插槽 什么场景下使用插槽;
    17、平时工作中封装过哪些组件,怎么封装这些组件;
    18、响应拦截器里面都做什么事情;响应拦截器里面的回调什么时候调用;
    19、 r o u t e 和 route 和 routerouter
    20、单页和多页面的缺点;
    21、keepalive 的作用原理;
    22、http 和 https 的区别;
    23、状态码有哪些;
    24、如何实现实时更新,比如股票数据;websocket
    25、如何判断一个数据是数组还是对象
    26、路由懒加载原理
    27、data 为什么是对象而不是函数;
    28、.catch 能捕获到 return 一个 reject 吗
    29、如何做移动端的 rem 适配,除了 flexible,还要装 pxtorem;
    30、如何解决跨域问题,生产环境如何解决跨域问题?什么是反向代理;跨是浏览器的机制;
    31、回流和重绘
    32、图片懒加载的原理
    33、项目优化的方式各个方式:打包,发请求;
    34、观察者模式和发布订阅模式区别
    的宽度,父盒子的剩余空间可以利用 flex-grow 来设置子盒子增大的占比

  • flex-shrink: 1

    • flex-shrink 用来设置子盒子超过父盒子的宽度后,进行缩小的比例取值
  • flex-basis: 0%

    • 设置盒子的基准宽度,并且 basis 和 width 同时存在会把 width 干掉
5、c3 新属性
  • c3 盒模型 box-sizing
  • flex 布局
  • transition 过渡
  • transform2D 转换
  • background-size 背景缩放
  • border-radius 圆角
6、

五、HTTP

HTTP 缓存

https://juejin.cn/post/6844904153043435533#heading-1

[外链图片转存中…(img-J96pnFCj-1705469393226)]

HTTP 状态码
  • 1xx - 信息响应 - 传达传输协议级别的信息
  • 2xx - 成功的响应 - 表示客户端的请求已成功接受
  • 3xx - 重定向 - 表示客户端必须采取一些额外的操作才能完成其请求
  • 4xx - 客户端错误 - 此类错误状态码指向客户端
  • 5xx - 服务端错误 - 服务器对这些错误状态代码负责
信息响应
  • 100:接受的请求正在处理,信息类状态码
成功的响应
  • 200:成功(OK) — 请求成功
  • 201:正创建(Created) — 请求已完成,已创建新资源
重定向
  • 301:永久移动(Moved Permanently) — 资源永久移动到新的 URL
  • 302:临时移动(Moved Temporarily) — 资源临时移动到新的 URL
  • 304:未修改(Not Modified)— 当协商缓存命中时会返回这个状态码。
客户端错误

此类错误状态代码指向客户端

  • 400:请求错误(Bad Request) — 服务器无法理解和处理请求
  • 401:未经授权(Unauthorized) — 需要验证,用户尚未验证
  • 403:禁止(Forbidden) — 对资源的访问权限不足
  • 404:未找到(Not Found) — 找不到请求的资源
服务端错误
  • 500:内部服务器错误(Internal Server Error)— 通用未处理的服务器错误

  • 503:服务不可用(Service Unavailable)— 服务器暂时无法处理请求

    1.浏览器缓存机制
    2.get 和 post 的区别

    3.$ nextTick(),原理

    4.防抖节流

    5.组件通信方式

    6.生命周期
    7.promise
    8.promise.all 方法什么时候走成功什么时候走失败;
    9.v-if、v-for
    10、对象和数组的响应式区别;
    11、keep-alive;
    12、组件通信的方式;
    13.vue 数据响应式如何实现
    14.vue for 循环的时候 key 的作用;
    15、call、apply、bind 区别
    15、vue.use 做了什么东西
    16、插槽 什么场景下使用插槽;
    17、平时工作中封装过哪些组件,怎么封装这些组件;
    18、响应拦截器里面都做什么事情;响应拦截器里面的回调什么时候调用;
    19、 r o u t e 和 route 和 routerouter
    20、单页和多页面的缺点;
    21、keepalive 的作用原理;
    22、http 和 https 的区别;
    23、状态码有哪些;
    24、如何实现实时更新,比如股票数据;websocket
    25、如何判断一个数据是数组还是对象
    26、路由懒加载原理
    27、data 为什么是对象而不是函数;
    28、.catch 能捕获到 return 一个 reject 吗
    29、如何做移动端的 rem 适配,除了 flexible,还要装 pxtorem;
    30、如何解决跨域问题,生产环境如何解决跨域问题?什么是反向代理;跨是浏览器的机制;
    31、回流和重绘
    32、图片懒加载的原理
    33、项目优化的方式各个方式:打包,发请求;
    34、观察者模式和发布订阅模式区别

文章来源:https://blog.csdn.net/weixin_70610368/article/details/135648171
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。