目录
vite和webpack的区别,为什么使用vite?(问过)
接着上面的问题,vue2 中,数据变了,页面就一定会变化吗?
你能说一下为什么使用flex布局吗,它有哪些常用属性,作用是什么?(问过)
能自己实现一下js中 call,apply,bind,函数吗?
说一下promise与async ,await 的区别(问过)
JS中判断数据类型有哪些方法?(大概率连问 typeof 的缺点)(问过 *2)
一.vite相关
vite和webpack的区别,为什么使用vite?(问过)
webpack运行时 先分析依赖关系,再打包编译,然后发送给浏览器,浏览器一次性加载所有内容,当项目体积很大时,就会影响页面的加载速度。
而vite在启动时,不进行打包编译,而是先启动浏览器,再根据浏览器的请求,进行相应模块的打包编译,这样即使项目体积增大,每次也还是对浏览器请求的模块进行编译,很大程度的提高了项目的运行速度。
热更新方面vite同样是根据模块更改后,浏览器请求该模块,vite进行编译,而不用向webpack那样整体打包,vite的效率明显更高。
综上所述,随着项目体积增大,vite的执行效率会明显快于webpack,所以使用vite。
vite.config.js配置文件常用配置?
帮助文档:Vite项目打包配置详解 - 爵岚 - 博客园 (cnblogs.com)
//导入 defineConfig 函数,用于创建 Vite 的配置对象
import { defineConfig } from 'vite'
//导入 Vite 插件,支持 Vue 单文件组件。
import vue from '@vitejs/plugin-vue'
//导入 Node.js 内置模块 path,用于处理文件路径相关的操作
import path from 'path'
//使用 defineConfig 函数创建一个默认导出的配置对象
export default defineConfig({
plugins: [vue()], //定义了使用的插件。在这里,我们使用了 vue 插件
base: './', //指定了项目的基础路径
//定义了模块解析的相关配置选项
resolve: {
extensions: ['.vue', '.js'], //指定了模块的扩展名
alias: {
"@": path.resolve(__dirname, "src")
} //使用了 @ 符号作为别名,指向 src 目录
},
//定义了开发服务器的配置选项
server: {
// port: 5000, // 挂载端口
//配置代理请求
proxy: {
'/api': {
target: 'http://www.baidu.com',
changeOrigin: true, //允许代理转发,解决跨域
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
})
二.VUE 相关
介绍一下vue,为什么使用它,它的优势是什么?
vue是一款构建前端页面的JS框架,它基于web三件套进行构建,能够声明式的,组件化的进行高效率的开发。
使用的原因就是他的优势,
它使用了MVVM框架,model-view-viewModel,就是将页面的dom与js部分的数据分割,不同我们手动去修改,而是通过viewModel进行检测,自动将data渲染到dom中,也就是响应式的。
它是单页面的,相较于传统的web开发,vue的整体就是一个页面,而整个复杂的前端显示都是通过将.vue文件加载到它的路由模块,进行页面渲染,当这一个页面加载出来后,就不会再进行关于页面的请求,所以不用每次页面跳转都重新加载页面耗费时间,优化了用户体验。
视图组件化,就是前端展示的页面,我们可以将其划分成不同组件,我们可以修改组件进行页面调整,每个组件都可以包含自己的数据,逻辑,组件化大大提高了复用性。
vue2和vue3 的区别有哪些?(问过)
帮助文档:盘点 Vue3 与 Vue2 的区别 - 掘金 (juejin.cn)
(1)生命周期,vue3 将2 的 beforeCreate 和 created整合为setup,其余钩子添加onka
(2)vue2不能多根节点,必须有一个div包裹,vue3 可以多根节点
(3)vue2 的API方式,写代码时,逻辑散乱,vue3 的是组合式API,整合到setup中,最后统一返回return,增强代码可读性,内聚性
(4)响应式原理,vue2 是object.defineProperty,其操作的是对象的属性, vue3是proxy,其是对目标对象行为的拦截。
(5)虚拟DOM
(6)diff算法优化(目前不熟)
(7)vue3 有更好的TS支持
vue2/3的响应式是什么,分别实现原理是什么?
vue2 响应式原理帮助文档:vue中的响应式是什么? 怎么理解响应式原理?_vue响应式属性_J.P_P的博客-CSDN博客
vue3 响应式原理帮助文档:
在Vue中,每个绑定data属性的组件都有一个Watcher检测data属性的变化。一旦检测到改变,则重新渲染该组件,这就是响应式。再简单来说就是数据变化,页面也变。
vue2 的响应式,是在组件或是实例初始化的时候,通过object.defineProperty将data里的数据进行劫持,此时,数据就会有两个属性,setter和 getter。当我们的数据修改时,会触发setter属性和底层的一个watch监听,从而将DOM数据修改刷新,getter是在获取数据时会使用到。
vue3 的响应式,它是基于proxy代理和配合Reffect实现的,数据被proxy进行拦截,拦截的是对象层面,所以当我们对对象的属性进行增删改查时,都可以检测到,但也仅仅是检测到,而不是响应式的变化,所以当我们调用了proxy的操作方法时,同时也要对应的使用Reffect里相应的方法,通过映射实现响应式。
在Vue2中,数据响应式主要借助Object.defineProperty()来实现,存在的缺陷是无法操作数据的增加和删除,在Vue3中,数据响应式主要借助proxy和Reffect配合实现,可以做到实现数据的增删改查。
接着上面的问题,vue2 中,数据变了,页面就一定会变化吗?
帮助文档:vue中的响应式是什么? 怎么理解响应式原理?_vue响应式属性_J.P_P的博客-CSDN博客
不一定,数据变页面变的原因是,初始化时,data里的数据被劫持,拥有了setter和getter属性,所以当我们修改被劫持的数据时,会触发相应的属性,从而引起页面DOM的刷新变化,但是,如果我们新增数据,或是修改了数组的长度,此时,由于是后来对于数据的新增或是长度修改,在最初的初始化劫持部分,并没有劫持这些新的内容,所以,对于这些数据的修改,并不会引起页面的变化。
如果想让其也可以对页面内容刷新,可以使用 this.$set方法
首屏加载优化的方法(问过)
(1)懒加载
(2)骨架屏
(3)服务端渲染SSR
(4)预渲染
(5)使用CDN
vue的虚拟DOM(问过)
对于DOM来说,当HTML的一个元素(如div)需要响应数据更改时,会刷新整个页面,导致效率堪忧。
对于虚拟DOM,浏览器会将HTML文件转换为JS文件并复制一个额外使用(虚拟)。对于任何更改,虚拟DOM都将复制的JS与原始JS进行比较,只重新加载更改的部分,局部修改到真实DOM上。
如何解决跨域问题(问过)
(1)CORS
后端设置Access-Control-Allow-Origin 值为一个*,表示接受任意域名的请求。
(2)JSONP
jsonp原理:img、srcipt,link标签的src或href属性不受同源策略限制,可以用来作为请求,后端接受请求后返回一个回调函数callback,调用前端已经定义好的函数,从而实现跨域请求
jsonp的缺点:只能发送get一种请求。
(3)nginx代理
组件之间通信的方式(问过*2)
帮助文档:vue中组件间通信的6种方式_vue同级组件通信_Jiang_JY的博客-CSDN博客
(1)使用VUEX 或是 localStorage
(2)props/$emit
(3)ref/$refs
(4)eventBus事件总线($emit / $on/$off)兄弟组件通信
(5)依赖注入(provide / inject)不熟且不是响应式的
(6)$parent / $children不熟且不是响应式的
(7)$attrs / $listeners 不熟
三.HTML相关
H5 语义化标签(问过)
帮助文档:Html5 什么是语义化标签? 常见的语义化标签有哪些?_Under-_-Taker的博客-CSDN博客
语义化标签,顾名思义,就是能直接根据标签名知道其大致作用的标签,例如:
<header> <!--:页眉通常包括网站标志、主导航、全站链接以及搜索框。-->
<nav> <!--:标记导航,仅对文档中重要的链接群使用。-->
<main> <!--:页面主要内容,一个页面只能使用一次。-->
<article> <!--:定义外部的内容,其中的内容独立于文档的其余部分。-->
<section> <!--:定义文档中的节。比如章节、页眉、页脚或文档中的其他部分。-->
<aside> <!--:定义其所处内容之外的内容。如侧栏、文章链接、广告、相关产品列表等。-->
<footer> <!--:页脚,只有当父级是body时,才是整个页面的页脚。-->
<!DOCTYPE html>的作用(问过)
帮助文档:DOCTYPE的作用,常见声明,删除<!DOCTYPE>发生什么?严格模式和混杂模式_如果不写!doctype html 会发生什么_Sòrry╮的博客-CSDN博客
这行代码不是html标签,是一个声明, 其作用是告诉浏览器按照哪一种HTML文档规范解析HTML文档。如果定义不规范 或是去除此部分声明,那么不同的浏览器将根据自己的方式去渲染页面,有多少种浏览器,就有多少种展示方式。
四.CSS相关
块元素和行内元素
块元素:独占一行;元素的宽高、以及内外边距都可设置;元素宽度在不设置的情况下,是它本身父容器的100%。
行元素(行内元素):在水平方向上修改水平尺寸(padding,margin,border),能产生相应的效果,垂直方向上对行元素的高度是毫无影响的。
因此,行内元素直接定义width和height是没有意义的,行元素的宽高是靠内容撑起来的。
但是,可以通过设置line-height,来规定行元素的高度。
可以通过对行元素设置display属性,转化为块元素,display:block
。
display的属性值:
block
块类型。默认宽度为父元素宽度,可设置宽高,换行显示。none
元素不显示,并从文档流中移除。inline
行内元素类型。默认宽度为内容宽度,不可设置宽高,同行显示。inline-block
默认宽度为内容宽度,可以设置宽高,同行显示。
伪类和为元素区别?
帮助文档:伪类和伪元素 - 学习 Web 开发 | MDN (mozilla.org)
伪类更像是你给页面上的元素新增了一个类,然后设置样式,使用例如 :hover
伪元素像是给页面新增了一个元素,然后给这个元素设置样式, 使用例如 ::after
- 伪类是设置指定元素在特定状态下的样式。
- 伪元素是设置指定元素特定部分的内容和样式。
CSS选择器的权重(问过)
!important > 内联样式 style > #id > 伪类选择器 > 属性选择器 = 类选择器 > 元素选择器
CSS常见的选择器有哪些
帮助文档:css的9个常用选择器 - 知乎 (zhihu.com)
通用(*)选择器,id选择器,类选择器,属性选择器,元素(标签)选择器,分组选择器,后代选择器,直接父子选择器,兄弟选择器
长度单位px、em和rem的区别是什么
帮助文档:css中单位em和rem的区别 - 知乎 (zhihu.com)
px是一种固定的大小,设置后不会根据页面的变化而变化
em与rem都是一种相对大小,em相对父级元素设置的大小,rem相对于根元素(html)的大小
所以,px一般适用于边框或是定位使用,em与rem更适用于响应式的布局,通常使用rem更为广泛,它的相对的大小是相同的,都是相对于(html)页面,而em只是相对于它的父元素,尺寸可能会有差异。
前端CSS的几种布局了解吗?
我了解的有以下几种:
(1)正常布局,ul等标签
(2)display的block和inline,块级占据一行,行内属性只占据其内容的宽度
(3)定位,position:相对和绝对
(4)float浮动
(5)flex流式布局
(6)grid栅格布局
那你能说一下,为什么要清除浮动,我如何清除浮动吗?
使用浮动极有可能造成页面塌陷,比如说当使用了浮动的标签,它的高度超出了包含它的父级标签的高度,父级标签不会自动撑高来包裹浮动元素,此时就是高度塌陷。
清除浮动主要使用的属性是clear,具体可以是left,right,也可以是both,clear只有块级元素设置才有效。
一般,我们使用了float浮动后,我们会给使用浮动的标签的父标签的类添加一个伪元素::after在这个元素里我们添加clear属性进行清除浮动,但是,这样我们必须同时添加dispaly:block属性,将伪元素设置为块级元素。我们也可以在浮动元素的后面添加一个块级元素,然后单独给它设置clear属性去清除浮动。
你能说一下为什么使用flex布局吗,它有哪些常用属性,作用是什么?(问过)
当页面父元素需要我们进行分割时使用,比如等分成三份,我们给三个子元素添加flex:1就可以均分,或是按比例分割,也是可以设置子元素的flex占比进行设置。
一般,我们使用flex时,都会现在父级元素设置以下几种属性:
display:flex,成为弹性盒,根据主轴方向选择是否添加:
flex-direction:column(主轴垂直,从上到下),一般默认主轴是水平从左向右,
flex-wrap 选择是否换行
justify-content 选择元素在主轴上的对齐方式,有flex-start | flex-end | center等
align-items 定义弹性元素在垂轴上如何对齐,有flex-start | flex-end | center等
子元素设置如下属性:
flex: 数字,默认为0
align-self 为弹性元素单独设置在垂轴上的对齐方式,默认值为auto,属性值同上
order order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。
flex是 flex-grow,flex-shrink, flex-basis简写 默认 0 1 auto,后两个属性可选
flex-grow,当有空余空间时,元素的放大比例,默认为0,不放大
flex-shrink,当空间不足时,元素的缩放比例,默认为1,不足时缩小
flex-basis 定义元素在计算主轴剩余空间之前,当前元素所占的空间,根据这个再去计算是否有剩余空间,默认auto,即元素本身大小。
position的几种属性取值?
帮助文档:https://blog.csdn.net/qq_41306423/article/details/106912112
一般默认情况下是 position:static,等于没有设置 position属性
position:relative 相对定位,发生偏移时的参照为 position 属性取 static 时盒子的位置
position:absolute 绝对定位,元素相对于最近的非 static 定位祖先元素发生偏移
position:fixed 元素相对于屏幕视口的位置来发生偏移, 元素的位置在屏幕滚动时不会改变
盒子模型?(问过)
标准盒(W3C)和怪异盒(IE5/6)
标准盒 块的宽高= 内容 + padding + border + margin CSS的宽高 = 内容宽高
怪异盒 块的宽高= width/height + margin CSS的宽高 = 内容 + padding + border
CSS3 的 box-sizing
box-sizing:content-box||border-box||inherit
box-sizing:content-box; 将采用标准模式的盒子模型标准
box-sizing:border-box; 将采用怪异模式的盒子模型标准
box-sizing:inherit; 规定应从父元素继承 box-sizing 属性的值。
如何设置元素居中?(问过)
帮助文档:CSS 水平垂直居中 9 种方法 - 掘金 (juejin.cn)
9种水平垂直居中的方法(最详细)_Muchen灬的博客-CSDN博客
水平居中
行内元素:父元素
text-align: center;
块级元素:
指定宽度后,设置margin: auto。width: 100px; margin: 0 auto
;
垂直居中
行内元素:
line-height
与height
相等(1)flex
父元素:.parent { display: flex; justify-content: center; align-items: center; }
父子元素:
.parent { display: flex; }
.children { align-self: center; margin: auto; }
(2)table-cell
.parent { display: table-cell; text-align: center; vertical-align: middle; }
.children { display: inline-block; }
(3)absolute + transform
(4)absolute + margin: auto
(5)Grid 布局
五.ES6以及往后
ES6的新特性?(问过)
帮助文档:最全的—— ES6有哪些新特性? - 掘金 (juejin.cn)
(1)新增了一种数据类型 Symbol 独一无二的,bigInt表示任意大的整数
(2)新增了 let const 变量声明关键字
(3)解构赋值
(4)新增箭头函数(this指向,不能new出实例,没有property属性)
(5)一些新的变量方法
(6)class类的引入
(7)模板字符串(方便字符串拼接,可以插入表达式,允许多行字符串)
(8)新的异步机制 promise generator
(9)模块化(export导出)
Symbol 的使用场景? (问过)
帮助文档:javascript中symbol类型的应用场景(意义)和使用方法 - 掘金 (juejin.cn)
(1)作为对象的 键值,但是正常获取时取不到Symbol 类型的键值,得用专门的API
(2)定义常量
(3)达到类的私有化效果
说一下 var let const 这几个关键字的区别
帮助文档:var,let,const三者的特点和区别_前端Vincent的博客-CSDN博客
var 全局作用域,当在函数中声明时是局部变量,存在变量提升,可以重复声明,后面的覆盖前面的
let 和 const 都是块级作用域,没有变量提升
let 命令所在的代码块内有效,在块级作用域内有效,在声明变量之前,变量不可使用,不能在同一作用域内重复声明
const 必须初始化,声明一个只读的变量,声明后,值就不能改变,并不是变量的值不能改动,而是变量指向的内存地址所保存的数据不得改动
六.JS部分
什么是原型?(问到)
帮助文档:什么是原型、原型链?原型和原型链的作用(有图方便理解)_是小橙鸭丶的博客-CSDN博客
首先要明白一点:原型存在于对象中的。
接下来阐述什么原型:在js中,每个构造函数内部都有一个prototype属性,该属性的值是个对象,该对象包含了该构造函数所有实例共享的属性和方法。当我们通过构造函数创建对象的时候,在这个对象中有一个指针,这个指针指向构造函数的prototype的值,我们将这个指向prototype的指针称为原型。或者用另一种简单却难理解的说法是:js中的对象都有一个特殊的[[Prototype]]内置属性,其实这就是原型。
原型,构造函数,实例之间的关系?
帮助文档:谈谈对原型与原型链的理解 - 知乎 (zhihu.com)
所有的引用类型(包括数组,对象,函数)都有隐性原型属性(proto), 值也是一个普通的对象。
所有的函数,都有一个 prototype 属性,值也是一个普通的对象。
所有的引用类型的proto属性值都指向构造函数的 prototype 属性值
所以得出以下 关系:
构造函数.prototype===原型
原型.constructor===构造函数
实例对象.__proto__===原型
手写一下 new 函数(new 的时候干了什么)?(问过)
帮助文档:如何手写一个JS中的New方法 - 掘金 (juejin.cn)
1.先创建一个空对象
2.然后将父类原型继承给 对象
3.给对象添加父类属性
4.返回结果
function _new(obj,...rest) {
let newObj = Object.create(obj.prototype) //1,2
let res = obj.apply(newObj,rest) //3
return typeof res === 'object' ? res : newObj;//4
}
能自己实现一下js中 call,apply,bind,函数吗?
参考文档:JavaScript中的call,apply,bind方法详解及简单实现 - 掘金 (juejin.cn)
// 实现 call 方法
Function.prototype.myCall = function (context, ...args) {
// 如果 context 参数为空,则默认为 window 对象
context = context || window;
// 使用 Symbol 函数创建一个唯一的标识符
const fnSymbol = Symbol();
// 将原始函数存储为 context 对象的属性
context[fnSymbol] = this;
// 调用函数并将结果存储在 result 变量中
const result = context[fnSymbol](...args);
// 删除 context 对象的属性
delete context[fnSymbol];
// 返回函数的结果
return result;
};
// 实现 apply 方法
Function.prototype.myApply = function (context, args) {
// 如果 context 参数为空,则默认为 window 对象
context = context || window;
// 使用 Symbol 函数创建一个唯一的标识符
const fnSymbol = Symbol();
// 将原始函数存储为 context 对象的属性
context[fnSymbol] = this;
// 调用函数并将结果存储在 result 变量中
const result = context[fnSymbol](...args);
// 删除 context 对象的属性
delete context[fnSymbol];
// 返回函数的结果
return result;
};
// 实现 bind 方法
Function.prototype.myBind = function (context, ...args) {
// 将 this 存储在 fn 变量中
const fn = this;
// 返回一个新的函数,该函数将传入的参数与新函数的参数合并,并在新的上下文中使用 apply 调用原始函数
return function (...newArgs) {
return fn.apply(context, [...args, ...newArgs]);
};
};
这里,我们使用
原型
方法给Function 对象
添加了myCall
、myApply
和myBind
方法。myCall
和myApply
非常相似,它们的不同之处在于参数传递方式。myCall
函数使用剩余参数语法...args
来传递参数,而myApply
函数接受一个数组作为参数。myBind
函数返回一个新的函数,该函数接受一个参数,并将其与myBind
中传递的参数合并,然后调用原始函数。对于
myCall
和myApply
,我们首先将传入的context
参数与window
对象进行比较,如果context
是空的,则默认为全局window
对象。然后,我们使用Symbol
函数创建一个唯一的标识符fnSymbol
,并将原始函数存储为context[fnSymbol]
的属性。我们然后调用函数,将结果存储在result
变量中,并使用delete
关键字从context
对象中删除函数属性。最后,我们返回函数的结果。对于
myBind
,我们首先将this
存储在fn
变量中,然后返回一个新的函数,该函数使用剩余参数语法将myBind
中传递的参数与新函数的参数合并,并在新的上下文中使用apply
调用原始函数.
JS 实现继承的方式?
帮助文档:面试官:Javascript如何实现继承? | web前端面试 - 面试官系列 (vue3js.cn)
-
原型链继承
-
构造函数继承(借助 call)
-
组合继承
-
原型式继承
-
寄生式继承
-
寄生组合式继承
JavaScript的代码执行顺序(问过)
(1). js的执行顺序,先同步后异步
(2). 异步中任务队列的执行顺序: 先微任务microtask队列,再宏任务macrotask队列
注意,按顺序从上到下时,没有轮到执行的任务会进入相应的队列,落到它所处队列运行时,队列内会有先后运行顺序的,promise本身是同步的,它的.then()是异步的微任务。
(3).练习
详解文档:都2023年了,还有人没搞懂微任务与宏任务吗? - 掘金 (juejin.cn)
console.log('CEO');
setTimeout(function () {
console.log('程序员1');
Promise.resolve().then(res => { console.log('销售4') })
new Promise(function (resolve) {
console.log('程序员2');
resolve();
}).then(function () {
console.log('销售5')
})
})
Promise.resolve().then(res=>{ console.log('销售1') })
new Promise(function (resolve) {
console.log('财务主管');
resolve();
}).then(function () {
console.log('销售2')
})
setTimeout(function () {
console.log('程序员3');
Promise.resolve().then(res => { console.log('销售3') })
new Promise(function (resolve) {
console.log('程序员6'); resolve();
}).then(function () {
console.log('销售6')
})
})
宏任务、微任务(问过 *2)
帮助文档:什么是宏任务、微任务?宏任务、微任务有哪些?又是怎么执行的?_F N Janine的博客-CSDN博客
为什么区分微任务和宏任务?(问过)
帮助文档:为什么要分微任务和宏任务?_为什么要有宏任务和微任务执行机制_PYlenmt的博客-CSDN博客
微任务是线程之间的切换,速度快。不用进行上下文切换,可以快速的一次性做完所有的微任务。
宏任务是进程之间的切换,速度慢,且每次执行需要切换上下文。因此一个Eventloop中只执行一个宏任务。
更简洁地来说就是方便插队。
console.log('1')
setTimeout(function () {
console.log('4');
Promise.resolve().then(res => { console.log('6') })
new Promise(function (resolve) {
console.log('5');
resolve();
}).then(function () {
console.log('7')
})
})
setTimeout(function () {
console.log('8');
Promise.resolve().then(res => { console.log('9') })
})
new Promise(function (resolve) {
onsole.log('2');
resolve();
}).then(function () {
console.log('3')
})
正常执行顺序:123456789
不区分微任务宏任务(二者进入同一个队列):124583679
说一下promise与async ,await 的区别(问过)
promise的出现解决了传统callback函数导致的地狱回调问题,但是他的语法导致它纵向发展形成了一个回调链,遇到复杂的业务场景显然是不美观的;
async、await看起来更加简洁,使得异步代码看起来像同步代码,只有await的代码执行完毕后才会执行下面的代码,与promise一样,也是非阻塞的;
async/await基于Promise实现,相当于Promise的升级版,不能用于普通的回调函数;
JS中判断数据类型有哪些方法?(大概率连问 typeof 的缺点)(问过 *2)
帮助文档:JS 判断数据类型的 8 种方式 - 掘金 (juejin.cn)
(1)typeof 只能识别基础类型和引用类型,如果是对象,数组,则返回object
(2)constructor
(3)instanceof
(4)Object.prototype.toString.call()
后续通过帮助文档学习
null 和 undefined 如何判断?(问过)
帮助文档:JS 中判断空值 undefined 和 null - Rainyn - 博客园 (cnblogs.com)
如果使用 typeof 判断 null 会返回 object 而 undefined 会返回 undefined
所以具体判断时,我们需要判断三个地方。
具体如下:
let aa = undefined
let bb= null
let cc = 0
console.log(typeof(aa),typeof(bb),typeof(cc))
//undefined object number
if(!aa){
console.log('!aa')
}
if(!bb){
console.log('!bb')
}
if(!cc){
console.log('!cc')
}
//!aa
//!bb
//!cc
if(!aa && typeof(aa) === 'undefined'){
console.log('aa is undefined')
}
if(!bb && typeof(bb) !== 'undefined'){
console.log('bb is null')
}
if(!cc && typeof(cc) !== 'undefined'){
console.log('cc is null')
}
//aa is undefined
//bb is null
//cc is null
if(!aa && typeof(aa) === 'undefined' && aa !== 0){
console.log('aa is undefined')
}
if(!bb && typeof(bb) !== 'undefined' && bb !== 0){
console.log('bb is null')
}
if(!cc && typeof(cc) !== 'undefined' && cc === 0){
console.log('cc is 0')
}
//aa is undefined
//bb is null
//cc is 0
数组的去重有哪些方法?
帮助文档:前端开发之JS数组去重方法 - 掘金 (juejin.cn)
操作原数组:
(1)双层循环 + splice去除 ,如果相等,arr.splice(j,1)
返回新数组:
(2)循环 + indexOf() 判断元素是否在数组中,是则返回位置,没有则是-1
(3)循环 + arr.sort() 方法,sort相近似的元素排序,循环时判断当前元素和上一位元素是否相同
(4)循环 + includes() arr.includes(item) 返回 true/false
(5)ES6 的Set方法,let arr = new Set(oldArr) ,set方法成员都是唯一的,可以去重
(6)循环+ES6的Map, 判断map里是否有当前键值,没有则添加并设置false,否则修改为true
sort方法的使用
帮助文档:js排序——sort()排序用法_sort排序_前端小狮的博客-CSDN博客
语法:array.sort(fun);参数fun可选。规定排序顺序。必须是函数。
注:如果调用该方法时没有使用参数,将按字母顺序对数组中的元素进行排序,说得更精确点,是按照字符编码的顺序进行排序。
如果想按照其他规则进行排序,就需要提供比较函数,该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字。比较函数应该具有两个参数 a 和 b,其返回值如下:
若 a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值。
若 a 等于b,则返回 0。
若 a 大于 b,则返回一个大于 0 的值。
简单点就是:比较函数两个参数a和b,返回a-b 升序,返回b-a 降序
注:原数组发生改变
v-if和v-show的区别,使用情况?
帮助文档:v-if与v-show的区别、使用场景_v-if和v-show的区别,什么时候用_程序员良仔的博客-CSDN博客
这两个都用来控制元素的隐藏显示,但是,
v-show 控制的是 元素的属性 display: none,页面初始加载后,再更改不会影响性能
v-if 是直接控制虚拟DOM上的节点,真实DOM再根据虚拟DOM去判断是否生成元素。
所以根据 页面加载后是否会继续频繁更改元素的显示来决定使用哪一种方式
如果只是加载时判断显示隐藏,使用 v-if
如果后续通过按钮会是其他方式频繁更元素的显示隐藏,使用v-show
随机数的一些用法?(问过)
帮助文档:js 生成随机数 | 菜鸟教程 (runoob.com)
主要使用的是
Math.floor()
Math.random()
例子:
生成数组长度范围的随机数,用来随机获取数组元素
let arr = [.......省略] //arr.length = 150
let index = Math.floor(Math.random() * arr.length)
let num1 = 0
let num2 = 0
let num3 = 0
for (let index = 0; index < 1000000; index++) {
let num = Math.floor(Math.random() * arr.length)
if(num >=150 || num<0){
console.log(num,'error')
}
if(num >=0 && num<50){
num1++
}
if(num >=50 && num<100){
num2++
}
if(num >=100 && num<150){
num3++
}
}
console.log(num1,num2,num3,num1+num2+num3)
TS有哪些数据类型?(问过)
帮助文档:TS的数据类型大全_ts 数组常量类型_超人不会飞/的博客-CSDN博客
(1)string/number/boolean
(2)Array/Tuple
(3)enum
(4)null/undefined
(5)any
(6)void
TS配置文件的一些配置项?
"compilerOptions": {
"target": "esnext", //指定TS编译的目标版本
"module": "esnext", //指定TS模块输出格式
"strict": true,//启用严格模式,对类型检查进行更严格的验证。
"moduleResolution": "node",//指定了模块解析策略为 Node.js 风格的解析方式
"baseUrl": ".", //
//用于提供在 Webpack 环境中使用的全局变量和模块的类型定义
"types": [
"webpack-env"
],
//设置模块的路径映射,允许通过 @ 来引用 src 目录下的文件
"paths": {
"@/*": [
"src/*"
]
},
//指定编译时可用的库文件
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
//用于指定需要包含和排除的源代码文件
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
TS常见操作符用法 (问过)
帮助文档:TS中常见的7种操作符&运算符 - 掘金 (juejin.cn)
(1)非空断言操作符(!)
(2)可选链操作符(?.)
简化:let nestedProp = obj.first && obj.first.second;
let nestedProp = obj.first?.second;
(3)空值合并运算符(??)
(4)可选属性(?:)
(5)运算符(&)
(6)运算符(|)
(7)数字分隔符(_)
七.一些简单代码题
快速排序
const arr = [5, 2, 9, 1, 7, 6];
const sortedArr = quickSort(arr);
console.log(sortedArr); // [1, 2, 5, 6, 7, 9]
function quickSort(arr) {
if (arr.length <= 1) {
return arr;
}
const pivot = arr[Math.floor(arr.length / 2)];
const left = [];
const right = [];
for (let i = 0; i < arr.length; i++) {
if (i === Math.floor(arr.length / 2)) {
continue;
}
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return [...quickSort(left), pivot, ...quickSort(right)];
}
冒泡排序
const arr = [5, 2, 9, 1, 7, 6];
const sortedArr = bubbleSort(arr);
console.log(sortedArr); // [1, 2, 5, 6, 7, 9]
function bubbleSort(arr) {
const len = arr.length;
for (let i = 0; i < len - 1; i++) {
for (let j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
// 交换位置
const temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
洗牌算法
function shuffle(arr) {
let i = arr.length;
while (i) {
let j = Math.floor(Math.random() * i--);
[arr[j], arr[i]] = [arr[i], arr[j]];
}
return arr;
}
将一个数组里的多层对象扁平化处理
纯数组扁平化
const arr = [1,[2,[3,4,[5],6],7],8,[9,[10,11],1],2]
function fun1(obj=[],res=[]) {
if(!obj) return
if(Array.isArray(obj)){
obj.forEach((item)=>{
if(Array.isArray(item)){
fun1(item,res)
}
else{
res.push(item)
}
})
}
return res
}
纯对象扁平化
const obj= { a: { b: { c: 1, d: 2 }, e: 3 }, f: { g: 2 } };
function fun2(obj, preKey = "", res = {}) {
Object.entries(obj).forEach(([key, value]) => {
if (value && typeof value=== "object") {
fun2(value, preKey + key + ".", res);
} else {
res[preKey + key] = value;
}
});
return res;
}
复杂类型扁平化
const obj1 = [1,2,3,{a:1,b:{c:2,d:{e:3}}},[4,[5,6,[7]]]]
const obj2 = {a:[1,[2,[3]],b:{c:{d:1}},e:2,f:3}
const obj3 = [{a:1,b:[2,{c:3}]}]
function fun3(obj = {}, preKey = "", res = {}) {
//空值判断,如果obj是空,直接返回
if(!obj) return
//获取obj对象的所有[key,value]数组并且遍历,forEach的箭头函数中用了解构
Object.entries(obj).forEach(([key,value])=>{
if(Array.isArray(value)){
//如果obj是数组,那么key就是数组的index,value就是对应的value
//obj是数组的话就用[]引起来
//因为value是数组,数组后面是直接跟元素的,不需要.号
let temp = Array.isArray(obj) ? `${preKey}[${key}]` : `${preKey}${key}`
flat(value,temp,res)
}else if(typeof value === 'object'){
//因为value是对象类型,所以在末尾需要加.号
let temp = Array.isArray(obj) ? `${preKey}[${key}].` : `${preKey}${key}.`
flat(value,temp,res)
}else{
let temp = Array.isArray(obj) ? `${preKey}[${key}]` : `${preKey}${key}`
res[temp] = value
}
})
return res;
}
八.拓展
什么是闭包?(问过)
帮助文档:面试官:谈谈对JS闭包的理解及常见应用场景(闭包的作用)_LYFlied的博客-CSDN博客
闭包是指有权访问另一个函数作用域中变量的函数 。. 创建闭包最常见的方式就是,在一个函数内部创建另一个函数。
使用闭包主要为了设计私有的方法和变量。
- 优点是可以避免变量的污染
- 缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
什么是uniapp, 为什么使用,它的优点?
帮助文档:使用uniapp开发项目来的几点心得体会 - 知乎 (zhihu.com)
uniapp是一款能够编辑一套代码,就能够在多种平台发布的应用,它的语法与vue.js类似,可以极快的上手,开发效率高。
微信小程序制作和web网页有什么区别?(问过)
帮助文档:微信小程序与web页面制作的区别_微信小程序和做网页的区别_无名小贼的博客-CSDN博客
(1)标签差异
(2)微信小程序JS调用,并不可以操作dom对象
(3)组件样式的使用
什么是CSS预处理器,为什么使用SCSS?
CSS预处理器是一种新的专门编程语言,编译后可以生成正常的Css文件,无需考虑浏览器兼容问题,使得css更加简洁,易读性更强,更加方便代码的维护。
从URL输入到页面展示内容过程中发生了什么(问过)
- DNS 解析:将域名解析成 IP 地址
- TCP 连接:TCP 三次握手
- 发送 HTTP 请求
- 服务器处理请求并返回 HTTP 报文
- 浏览器解析渲染页面
- 断开连接:TCP 四次挥手
什么是回流和重绘,发生时机,如何优化?(问过)
回流也叫重排
帮助文档:【面试】什么是回流和重绘?减少或避免页面文档回流、重绘的方案有哪些?_页面重绘和回流_Danli.的博客-CSDN博客
回流就是当页面一个元素的几何属性变化或是页面布局变化,引起的整个页面重新布局和渲染加载。
重绘是页面的某个元素的外观等属性发生变化,但是不影响整体页面布局,而是重新渲染当前元素的操作
回流一定引起重绘,但是重绘不一定引起回流
前端加载图片过多,如何优化?(问过)
帮助文档:前端|加载的图片太多五种优化方法_前端多图优化_sakoooo的博客-CSDN博客
帮助文档2:前端性能优化——图片篇 - 掘金 (juejin.cn)
(1)图片懒加载,当需要图片展示时再进行加载
(2)进行压缩
(3)图片服务和应用服务分离,使用专门的图片服务器对图片进行储存
(4)CSS Sprites,将图片制成小的 icon 再将这些小 icon 合并成一张大图,通过css的position绝对定位进行拆分获取
(5)将图片压缩成base64储存,随html或者css一起下载到浏览器,不需要额外的请求,这样就节约了请求
什么是服务端渲染,解释一下vue中是如何实现这一步的?
帮助文档:Vite+Vue3服务端渲染改造指南 - 掘金 (juejin.cn)
一般来说,渲染是指前端通过请求,获取后端返回的JSON数据,之后前端通过事先的HTML模板对数据进行拼接,处理等操作,最后将数据插入到页面中。
浏览器在请求页面的URL时,服务端将我们需要的HTML页面进行拼接处理,然后返回发给浏览器,浏览器解析这个HTML文本后,不需要再去经过JS的执行,可以直接将预期的DOM效果展示在页面里,这个服务端处理拼接HTML的过程是服务端渲染。
vue的SSR服务端渲染,其实是基于VNode实现的,它使用了vue的特性虚拟DOM,通过频繁修改虚拟DOM,然后对比真实的DOM,找出不同的地方,进行页面的局部渲染,从而减少性能损耗。
前端的兼容性问题有哪几种,你是如何解决的?
帮助文档:(建议收藏)前端开发中常见的浏览器兼容性问题及解决方案大汇总 - 掘金 (juejin.cn)
大体来说就是浏览器之间的兼容问题,PC上web网页和移动端网页的展示问题
浏览器的兼容大都是些写法上的不同,主要是IE5那几个版本,
web网站在不同设备的显示主要通过响应式布局来解决:前端响应式布局原理与方案(详细版) - 掘金 (juejin.cn)
什么是对称加密 / 非对称加密?(问过)
帮助文档:通俗易懂的对称加密与非对称加密原理浅析 - 掘金 (juejin.cn)
对称加密又叫做私钥加密,即信息的发送方和接收方使用同一个密钥去加密和解密数据。过程是可逆的。对称加密的特点是算法公开、加密和解密速度快,适合于对大数据量进行加密。
非对称加密也叫做公钥加密。非对称加密与对称加密相比,其安全性更好。对称加密的通信双方使用相同的密钥,如果一方的密钥遭泄露,那么整个通信就会被破解。而非对称加密使用一对密钥,即公钥和私钥,且二者成对出现。私钥被自己保存,不能对外泄露。公钥指的是公共的密钥,任何人都可以获得该密钥。用公钥或私钥中的任何一个进行加密,用另一个进行解密。
cookie和session的区别?(问过)
帮助文档:你真的了解 Cookie 和 Session 吗? - 纯洁的微笑 - 博客园 (cnblogs.com)
HTTP Cookie(也叫 Web Cookie或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。
Session 代表着服务器和客户端一次会话的过程。Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当客户端关闭会话,或者 Session 超时失效时会话结束。
- 作用范围不同,Cookie 保存在客户端(浏览器),Session 保存在服务器端。
- 存取方式的不同,Cookie 只能保存 ASCII,Session 可以存任意数据类型,一般情况下我们可以在 Session 中保持一些常用变量信息,比如说 UserId 等。
- 有效期不同,Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭或者 Session 超时都会失效。
- 隐私策略不同,Cookie 存储在客户端,比较容易遭到不法获取,早期有人将用户的登录名和密码存储在 Cookie 中导致信息被窃取;Session 存储在服务端,安全性相对 Cookie 要好一些。
- 存储大小不同, 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie。