前端面试题总结 数据类型 基本类型
string ‘字符串’
number ‘64 位双精度浮点型数字’(包含 NaN)
boolean ‘布尔’,
undefined ‘没有任何值’
null ‘没有任何对象’
bigint ‘任意精度格式的整数’
symbol ‘标记值’
引用类型
对象’object’(Function 也是一个特殊的对象)
类型判断
如何判断一个数据是 NaN
用全等判断 x === NaN;
用 isNaN(x) 来判断;
null 与 undefined 区别
null 表示一个值被定义了,定义为“空值”;
undefined 表示根本不存在定义;
typeof 和 istanceof 区别?
typeof 会返回一个运算数的基本类型
instanceof 判断一个对象是否是某个构造函数的实例1 2 3 class Person {}const p = new Person ();console .log (p instanceof Person );
如何判断一个对象是对象
Object.prototype.toString.call({}) // “[object Object]”
如何判断一个对象时数组
Object.prototype.toString.call([]) // “[object Array]”
Array.isArray([])
[] instanceof Array // true
Map & Set
概念
Map:主要用于存储和管理键值对数据,适合在需要快速查找、插入和删除键值对的场景中使用.常见应用包括缓存、配置管理和数据关联.
Set:主要用于存储唯一值集合,适合在需要确保元素不重复的场景中使用.常见应用包括去重、集合操作和存在性检查.
用途
Map:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const map = new Map ();map.set ("a" , 1 ); map.set ("b" , 2 ); map.set ("c" , 3 ); console .log (map.get ("a" )); console .log (map.has ("b" )); console .log (map.size ); map.delete ("c" ); for (let [key, value] of map) { console .log (`${key} : ${value} ` ); } map.clear ();
Set:
1 2 3 4 5 6 7 8 9 10 11 12 13 const set = new Set ();set.add (1 ); set.add (2 ); set.add (3 ); set.add (1 ); set.delete (3 ); console .log (set.has (1 )); console .log (set.size ); for (let value of set) { console .log (value); } set.clear ();
数组/字符串 数组操作方法
arr.push() 从后添,返回值为添加完后的数组的长度
arr.pop() 从后删一个,返回值是删除的元素
arr.unshift() 从前添,返回值是添加后数组的长度
arr.shift() 从前删一个,返回值是删除的元素
arr.splice(i,n)从数组中添加/删除/修改值,返回值被改动的值
arr.concat() 连接两个数组 返回值为连接后的新数组
arr.sort() 将数组进行排序,返回值是排好的数组,默认是按照最左边的数字进行排序,不是按照数字大小排序的
arr.reverse() 将数组反转,返回值是反转后的数组
arr.slice(start,end) 切一段从索引值 start 到 end(不包含)的数组,返回值是切出来的数组
arr.join(separator) 把数组的所有元素放入一个字符串,方法内的参数作为分隔符,省略该参数,则使用逗号作为分隔符
arr.forEach(callback) 遍历数组,无 return 即使有 return,也不会返回任何值,并且会影响原来的数组
arr.map(callback) 映射数组(遍历数组),有 return 返回一个新数组
arr.filter(callback) 过滤数组,返回一个满足要求的数组
arr.indxOf() : 返回数组中满足提供的测试函数的第一个元素的下标
arr.lastIndexOf() : 从后往前返回数组中满足提供的测试函数的第一个元素的下标
arr.find() : 返回数组中满足提供的测试函数的第一个元素的值
arr.some() : 测试数组中是否至少有一个元素通过了由提供的函数实现的测试( 一个直则真 )
arr.every() : 测试一个数组内的所有元素是否都能通过指定函数的测试( 全真才真 )
map,forEach 和 for 循环区别:
forEach 只是进行简单的数组遍历,无返回值,且 break 和 return 语句不能跳出循环,for 可以适用于更复杂的循环且效率更高
map 代表是映射它有返回值,返回值是一个处理过后的新数组且不改变原数组
字符串操作方法
str.charAt(index) 返回在指定位置的字符
str.concat(stringA,stringB,…,stringZ) 连接两个或多个字符串,返回连接后的字符串
str.indexOf(检索的字符串,开始检索的位置【可省略】)检索字符串返回某个指定的字符串值在字符串中首次出现的位置,如果没有返回+1
str.lastIndexOf(检索的字符串,开始检索的位置【可省略】) 从后向前搜索字符串,返回一个指定的字符串值最后出现的位置,如果没有返+1
str.replace(正则表达式/检索的字符串,替换成的字符串)在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串.
str.slice(起始下标【slice 独有:可为负数,+1 指字符串的最后一个字符】,结尾的下标【可省略,如果省略则代表一直到字符串结尾】) 提取字符串的片断,返回被提取的部分.从 start 开始到 end 结束(不包括结尾的下标所代表的值)
str.substr(起始下标,长度【可选】) 返回从起始索引号提取字符串中指定数目的字符.
str.substring(起始下标,结束的下标【可选】) 返回提取字符串中两个指定的索引号之间的字符.
str.split(字符串或正则表达式,从该参数指定的地方分割,多少个【可选】) 把字符串分割为字符串数组.
数组字符串操作题
正则 正则中的特殊字符:
单个字符:
^ :正则开始
$ :正则结束
. :元字符, 表示任意一个字符
\ :表示转义字符 如:.表示.
- :表示其前面紧挨着的字符至少出现 1 次 等价{1,}
* :表示其前面出现的字符至少出现过 0 次 等价{0,}
? :表示其前面出现的字符至少出现过 0 次,至多 1 次 等价{0,1}
| :表示或者
组合字符:
\d :0-9 之间的任意一个数字 \d 只占一个位置
\D :除了\d
\w :数字,字母 ,下划线 0-9 a-z A-Z _
\W :除了\w
\s :空格或者空白等
\S :除了\s
函数 普通函数
function 构造函数 1 2 3 4 5 6 7 function People (name, sex ) { this .name = name; this .sex = sex; } People .prototype .sayName = function ( ) { console .log (this .name ); };
class 构造函数(本质仍然是 function 的语法糖) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Person { constructor (name, sex ) { this .name = name; this .sex = sex; } sayName ( ) { console .log (this .name ); } } class Superman extends Person { constructor (name, sex, skill ) { super (name, sex); this .skill = skill; } useSkill ( ) { console .log (this .skill ); } }
bind()和 call()与 apply()区别:
call 和 apply 主要用于 改变 this 指向,它们可以让一个对象借用另一个对象的方法。它们的主要区别在于 参数传递的方式不同。
bind 也是改变 this 的方法,但它不会立即执行函数,而是返回一个新的函数:1 2 3 4 5 6 7 8 9 10 11 const person1 = { name : "Alice" };function sayHello (age ) { console .log (`Hello, my name is ${this .name} and I am ${age} years old.` ); } sayHello.call (person1, 25 ); sayHello.apply (person1, [25 ]); const boundFunc = sayHello.bind (person1, 25 );boundFunc ();
浏览器 API ajax 如何创建和使用
实例化 XMLHttpRequest 对象:
open()准备发送求:
send()执行发送动作:
onreadystatechange 指定回调函数接收来自服务器端的请求,readyState 即为回调状态 responseText 返回内容
1 2 3 4 5 6 7 8 9 let http = new XMLHttpRequest ();http.open (post,"https://www.baidu.com" ,false ) http.send (); http.onreadystatechange = function ( ){ if (http.status === 200 && http.readyState === 4 ) { console .log (http.responseText ); } }
本地存储与 Cookie 的区别
Cookie:它有 4kb 的大小限制,数据的生命周期比较灵活,不设置失效时间默认关闭浏览器后失效,原生 Cokkie 接口不好需要二次封装,可以通过它设置记住密码功能,存储的内容会保留在 HTTP 请求的 Header 中,并且会随每次请求发送到浏览器
本地储存:是 HTML5 新增技术,有 localStorage 和 sessionStorage 两种,他们可存储的大小为 5mb,localStorage 的生命周期是永久存储,sessionStorage 的生命周期是当页面关闭后销毁
Json 如何新增/删除键值对
新增:
使用数组下标形式添加 Object[“属性名”] =’xxx’;
使用对象参数的形式添加 Object.属性名 = ‘xxx’;
删除:
使用 delete 删除 delete Object.属性名
ES6 新特性
const 和 let
Let 与 var 与 const 的区别
var 声明的变量会挂载在 window 上,而 let 和 const 声明的变量不会
var 声明变量存在变量提升,let 和 const 不存在变量提升
let 和 const 声明形成块级作用域
同一作用域下 let 和 const 不能声明同名变量,而 var 可以
let 有暂存死区
模板字符串
箭头函数
函数的参数默认值
对象和数组解构
for…of 和 for…in
ES6 中的类
Promise 对象
async 函数
核心概念 闭包
概念: 写一段自调用函数,函数体内声明变量后 return 出一个方法,在方法内引用数据并处理,之后随着函数的调用结束定义的数据不会被而释放,第一次调用后的值被保存了下来,所以形成了”闭包”.
原因:因为当声明一段函数时,它的作用域和变量就已经在内存中被声明了,因为 js 的垃圾处理机制是被引用的堆内存不会被释放.所以当变量被函数内部引用,函数执行完毕后作用域被销毁,但被引用的值不会被销毁,所以这些变量在闭包执行期间是永久性存在的
特性:
函数内变量以私有成员的存在,避免了全局变量的污染
函数内部可以引用外部的参数和变量
这些变量的值始终保持在内存中,不会在外层函数调用后被自动清除
参数和变量不会被垃圾回收机制回收
影响:
用途:
保存状态:1 2 3 4 5 6 7 8 9 10 const countNum = (() => { let a = 0 ; return () => { a += 1 ; return a; }; })(); countNum (); countNum ();
模块化:1 2 3 4 5 6 7 8 9 10 11 12 13 export default moduleA = (()=> { let data = "this is my data" ; return { getData :()=> { return data } } })() import moduleA from moduleA.js const data = moduleA.getData () + "deal" ;...
什么是内存泄露?
内存因为某些原因没有被垃圾回收装置回收成为常驻内存,造成系统内存的浪费,可能会导致程序运行速度减慢甚至崩溃
原型/原型链/继承
概念:
原型: 在 JS 中每个构造函数有一个原型对象,构造函数可以通过 prototype 去访问原型对象,而原型对象有一个 constructor 指回构造函数,构造函数可以生成一个实例,生成的实例有一个proto 属性指向原型对象.
原型链:当访问 JS 中一个实例的方法和属性时,首先会查找自身;如果并未查找到,那么就会寻找实例的原型对象中是否存在,如果仍不存在,就会访问原型对象的原型对象.这个搜索链条就叫原型链1 2 3 4 5 function People (name, sex ) { this .name = name; this .sex = sex; } People .prototype ;
用途:
用于继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function Animal ( ) { this .step = 0 ; this .category = "animal" ; } Animal .prototype .run = function ( ) { this .step += 1 ; }; function Dog (name ) { Animal .call (this ); this .name = name; } Dog .prototype = Object .create (Animal .prototype );Dog .prototype .construtor = Dog ;Dog .prototype .say = function ( ) { console .log ("woof" ); }; const xiaobai = new Dog ("xiaobai" );
继承:是面向对象编程中的一个重要概念,通过继承可以使子类的实例使用在父类中定义的属性和方法.[每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性.搜索首先从对象实例本身开始.如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索_proto_指针指向的原型对象,在原型对象中查找具有给定名字的属性.如果在原型对象中找到了这个属性,则返回该属性的值.]
同步与异步的区别/阻塞与非阻塞区别
(https://blog.csdn.net/qq_22855325/article/details/72958345 )
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行下一个任务;
异步任务指的是,不进入主线程、而进入”任务队列”,只有等主线程任务执行完毕,”任务队列”才开始通知主线程,请求执行任务,该任务才会进入主线程执行 [javascript 是单线程,所有任务需要排队,前一个任务结束,才会执行后一个任务]
阻塞:调用时,如果被调用者状态未就绪,会导致调用线程被挂起.
非阻塞:调用时,如果被调用者就绪则立即返回结果,如果未就绪也会返回一个错误值,告诉调用者当前的状态
请简述 async 的用法
(https://segmentfault.com/a/1190000007535316 )
async 用于申明一个函数是异步的并且它会返回一个 Promise 对象,函数的返回值就是.then()里的数值 [await 只能出现在 async 函数中,await 在等待一个 async 函数完成,因为 async 函数返回一个 Promise 对象,所以 await 可以用于等待一个 async 函数的返回值——这也可以说是 await 在等 async 函数,但要清楚,它等的实际是一个返回值]
什么是 JSONP 工作原理是什么?
工作原理: 通过 script 标签绕过同源策略 前端动态生成一个 script 标签向后端发送 get 请求,然后准备函数接收数据,并用 callback 为键将函数名发送给后端,后端接收函数名后将数据作为实参拼接一段执行调用函数的 js 代码返回给前端,前端接收到执行函数的代码后 script 标签 会自动调用声明的函数,函数的形参就是后端返回的数据.
1 2 3 4 5 6 7 8 9 dealData ({ name : "Alice" , age : 25 });function dealData (data ) { console .log (data); }
事件委托是什么?如何确定事件源
事件委托就是利用事件冒泡机制,只给一个元素绑定事件,就可以代理管理某一类型的所有事件.这样可以避免绑定一大堆事件,优化性能
事件源通过事件对象的 target 属性来确定
This 指向
在一般函数内或全局作用域下,指向的 window 对象
在对象的方法中,指向的该对象
在事件处理函数中,指向触发事件的元素(Node 节点)
在箭头函数中: 继承上一级的指向位置(无视当前函数)
在构造函数中,指向的是实例化对象.(需要 new 的对象)
1 2 3 4 5 6 function People (name, age ) { this .sayMyName = alert (name); this .age = age; } let student = new People ("二狗" , 18 );student.sayMyName ;
在新增原型对象属性中,this 指的就是这个原型的主人(实例化的对象)
箭头函数与普通函数的区别
箭头函数都是匿名函数,没有自己 this,arguments,原型对象
普通函数和构造函数的区别
写法不同构造函数习惯上首字母大写
调用方式不一样,普通函数可以用 函数名() 的形式直接调用,构造函数需要用 new 关键字来调用创建实例对象
返回值不一样,普通函数返回值由 return 决定,构造函数返回值是实例化的对象
什么是面向对象请简述
面向对象的三大特征 继承、封装、多态
继承:通过继承使子类可以使用在父类中定义的属性和方法
封装:就是指把内部的实现封装起来,然后暴露必要的方法让外部调用.
多态:多态指的是同一类事物有多种形态,如文件有:文本文件,可执行文件,视频文件等…
重绘以及回流是什么
重绘(repaint):当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时由于只需要 UI 层面的重新像素绘制,因此损耗较少,常见的重绘操作有:改变元素颜色
回流(reflow):当元素的尺寸、结构或者触发某些属性时,浏览器会重新渲染页面,称为回流.此时,浏览器需要重新经过计算,计算后还需要重新页面布局,因此是较重的操作.常见的回流操作有:浏览器窗口大小改变,添加或者删除可见的 DOM 元素
回流必定会触发重绘,重绘不一定会触发回流.重绘的开销较小,回流的代价较高.
事件循环
解释: JS 是单线程的语言,为了避免阻塞主线,它引入了事件循环机制来处理异步任务,JS 会先执行同步任务,遇到异步任务的时候会交给 Web API,当异步任务执行完成的时候,会将回调函数放入 任务队列 Task Queue / Callback Queue 然后 Event Loop 事件循环 负责一直监听任务队列 执行队列中的任务
宏任务微任务:遇到同步任务直接执行,遇到异步任务分类为宏任务(macro-task)和微任务(micro-task).有微则微,无微则宏:
宏任务: 整体代码 定时器 ajax DOM 事件
微任务: promise async/await
微任务 > DOM 渲染 > 宏任务
深浅拷贝
是什么如何实现?
js 有两种类型基本类型和引用类型,基本类型它的名和值会储存在栈内存中,当我们复制它时会开一条新内存再创建一个同样的值,但是引用类型在我们新建的时候它会在栈内存中储存它的名和一个指向它堆内存的地址,当我们复制的时候其实只是复制了它的引用地址并非堆里面的值,如果要深拷贝我们就需要新建一个和它一样的堆内存的值
什么时候用深拷贝/浅拷贝
当我们创建了一个对象并复制,如果我们想修改复制的对象,但却不想让原对象也随之改变的时候使用深拷贝,但是如果我们只是想复制它的基本类型的数据或者指向它的堆内存的指针的时候使用浅拷贝
深拷贝的代码实现
简单深拷贝(一层拷贝)1 2 3 4 5 6 7 function deepCopy (obj ) { let copyObj = Array .isArray (obj) ? [] : {}; for (let key in obj) { copyObj[key] = obj[key]; } return copyObj; }
复杂深拷贝(递归实现多层拷贝)1 2 3 4 5 6 7 8 9 10 function deepCopy (obj ) { let copyObj = Array .isArray (obj) ? [] : {}; for (let key in obj) { if (obj.hasOwnProperty (key)) { copyObj[key] = typeof obj[key] === "object" ? deepCopy (obj[key]) : obj[key]; } } return copyObj; }
Promise
理解: promise 是为解决异步处理回调地狱问题而产生的;它是一个构造函数,可以通过 new 得到一个 Promise 的实例对象,Promise 里有两个函数分别叫做 resolve(成功之后的回调函数)和 reject(失败之后的回调函数)在 Promise 构造函数的 Prototype 属性上,有一个.then()和.catch()方法,只要是 Promise 的实例化对象都可以使用
Promise 在哪里使用过
在使用 ajax,axios 等一类的数据请求的时候用过,只要是一些需要解决回调函数问题的都可以用(在函数中 return 一个 promise 实例,成功后干嘛失败后干嘛,则该函数就可以使用.then 和 catch 方法)
函数节流和防抖
函数节流:
(https://www.cnblogs.com/fightjianxian/p/12077570.html )
作用: 在 n 秒中只执行一次函数(指定时间间隔内只会执行一次任务)
实现:1 2 3 4 5 6 7 8 9 10 11 12 13 var pass = true ;document .getElementById ("throttle" ).onscroll = function ( ) { if (!pass) { return ; } pass = false ; setTimeout (function ( ) { console .log ("函数节流" ); pass = true ; }, 300 ); };
应用场景: 高频触发的事件,多数在监听页面元素滚动事件
函数防抖:
(https://www.cnblogs.com/fightjianxian/p/12077451.html )
作用: 在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间(任务频繁触发的情况下,只有任务触发的间隔超过指定间隔,任务才会执行)
实现:1 2 3 4 5 6 7 var timer = false ;document .getElementById ("debounce" ).onscroll = function ( ) { clearTimeout (timer); timer = setTimeout (function ( ) { console .log ("函数防抖" ); }, 300 ); };
应用场景:如邮箱验证和手机号验证.只有等用户输入完毕后,前端才需要检查格式是否正确,如果不正确,再弹出提示语
VUE VUE2
Vue 的核心是什么
数据驱动和组件化
数据驱动:视图的内容随着数据的改变而改变
组件化:把页面封装成为若干个组件进行拼装,让页面的复用性达到最高
请简述你对 vue 的理解
一套渐进式的自底向上增量开发的前端 MVVM 框架
渐进式:可以只使用部分功能,也可以整个用 vue 开发,不做职责之外的事
自底向上增量开发:先编写出基础页面,再逐步扩大规模,补充和升级某些功能和效果
M 代表模型层也是数据层 V 代表视图层,VM 是用来沟通的桥梁,负责监听模型层或者视图层的修改
mvvm 框架是什么?
M 代表数据层 V 代表视图层,VM 是用来沟通的桥梁,负责监听模型层或者视图层的修改
区别:vue 数据驱动,通过数据来显示视图层而不是节点操作.
场景:数据操作比较多的场景,更加便捷.
MVVM 与 MVC 的区别
MVC 中 Model 和 View 还有 Controller 是完全独立的,由 Controller 作为中间人来负责二者的交互
MVVM 中 VM 是 V 与 M 沟通的桥梁,开发者只需关注业务逻辑,不需要手动操作 DOM,不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来管理
请简述 vue 的单向数据流
组件之间一旦传值完毕,接收数据的这个组件无论怎么修改,传递数据的那个组件的数据都不会改变;我们经常会采用父子组件通过正向/逆向传值来对数据进行传递.以上的这些模式非常脆弱,通常会导致无法维护的代码.
Vue 常用的修饰符有哪些
事件修饰符:
prevent(阻止事件的默认行为)
stop(阻止事件冒泡)
capture(让事件传递成为捕获)
self(只会触发自己范围内的事件,不包含子元素)
once(只会触发一次)
按键修饰符(按下某个键):up down ctrl enter space
什么是计算属性
它是一种属性,有“计算”这个特殊性质.每次取得它的值得时候,它并不像普通属性那样直接返回结果,而是经过一系列的计算之后再返回结果.
计算属性与 watch 区别
计算属性是依赖于缓存的,当依赖的值发生改变才会触发.而 watch 是当 watch 监听的值发生改变就会被调用相应方法
计算属性适合在数据展示时做一些处理
Vue 如何定义一个过滤器
`全局过滤器使用 Vue.filter(‘过滤器名字’,function(val){return 返回的内容})在 app.vue 中
局部过滤器使用 filters(‘’,function(val){})在 data 同级
通过 { { 要过滤的数据|过滤器名 } }来调用
Vue 循环的 key 作用
key 的作用主要是为了高效的更新虚拟 DOM,是遍历数组或元素中的唯一标识,增加或删减元素时,通过 key 判断是否是之前的元素,如果是则直接会复用该标签,不会将所有标签重新删除和创建,只会重新渲染数据,然后再创建新的元素直到数据渲染完为止
v-for 与 v-if 优先级
v-for 比 v-if 具有更高的优先级,但是不能把 v-if 与 v-for 用在同一个元素上,因为如果两者同时出现的话,那每次循环都会执行 v-if,会很浪费性能
Vue 单页面的优缺点
优点:用户体验好,速度快,内容的改变不需要再加载整个页面,前后端分离,组件化便于修改和调整
缺点:初次加载耗时高,页面复杂度提高,导航需要自行实现前进后退,不利于 seo 搜索引擎优化
Vue 的生命周期请简述
beforeCreate:创建 vue 实例前
created:创建实例完成后,开始监听 data 对象数据变化情况,初始化 VUE 内部事件
beforeMount:编译模板,把 data 里面的数据和模板生成 html
mounted:用编译好的 html 替换掉 el 属性所指向的 DOM 对象
beforeUpate:数据更新前
updated:数据更新后
beforedestroy:销毁实例前
destroyed:销毁所有事件监听器和子实例,完成销毁 vue 实例
Vue 生命周期的作用:
DOM 渲染在那个生命周期阶段内完成
Vue 中路由跳转方式(声明式/编程式)
声明式:
编程式: this.$router.push(“/路由名”)
跨域的解决方式
在 vue.config.js 中进行 proxy 配置
在后台用 cors 跨域
Vue 路由的实现
通过 hash 和 history 两种模式来实现,它们分别基于 location 和 history 对象
Vue 路由模式 hash 和 history,简单讲一下
hash 模式:基于 location 对象,地址栏会出现#(hash)符,只有#前的内容会被包含在请求中,前端路由修改的是#后的信息,所以刷新是不会向服务端请求添加#后面的参数,所以刷新不会出问题
history 模式:基于 History 对象,前端的 URL 必须和实际向后端发起请求的 URL 一致,不然会出现 404 错误(比如刷新的时候),需要后端配置一下 apache 或是 nginx 的 url 重定向,重定向到我的首页路由上.
Vue 路由懒加载(按需加载路由)
因为 vue 的路由技术是为了完成单页面应用的,在第一次页面初始化的时候路由会把所有的路由页面都渲染好可能会造成用户的页面白屏相应慢: component:()=>import(‘路由组件路径’)
Route 与 router 区别
route 对象表示当前的路由信息,是一个局部对象,包含了当前 URL 解析得到的信息.包含当前的路径,参数,query 对象等.
router 对象是全局路由的实例,他包含所有的路由拥有的对应的对象,属性和方法.比如 history 对象
Vue 路由传参的两种方式,prams 和 query 方式与区别
params:需要在路由规则中绑定接收的参数名,通过路由规则的 name 值来绑定发送的参数,传递的参数不会暴露在地址栏相对安全
query:不需要在路由规则中绑定参数名,通过路由规则的 path 路径来绑定发送的参数,传递的参数会暴露在地址栏不安全
Vue 数据绑定的几种方式
普通文本绑定 v-text
解释 HTML 标签的绑定 v-html
数据特殊属性 v-bind
双向绑定 v-model
Vue 注册一个全局组件
在 main.js 通过 import 引入封装的组件
使用 vue.component(“设置使用时的组件名”,引入的组件)
Vue 的路由钩子函数/路由守卫有哪些
全局钩子:
路由前置:router.beforeEach((to,from,next)=>{})
路由后置:router.afterEach((to,from,next)=>{})
路由独享钩子:在路由规则中写入 只有前置 beforeEnter:(to,from,next)=>{}
组件内钩子:beforeRouteEnter(to,from,next){} beforeRouteLeave(to,from,next){}
Vue 中如何进行动态路由设置?有哪些方式?怎么获取传递过来的数据?
前端列表页点击之后通过 params 或 query 的声明式( )或编程式发送参数(this.$router.push(name/路径,params/query:{}))如果使用 params 传参需要在路由规则中配置接收参数的参数名
最后详情页再通过 this.$route.params/query.接收的参数名后 发送不同参数的请求接收不同的数据渲染页面
Vue 中指令有哪些
v-text 将普通数据显示在页面上
v-html 输出 html 内容
v-show 控制元素的显示和隐藏
v-if 判断是否加载内容 v-else-if:满足一项先执行它 v-else:不然就执行它
v-model 用于表单的双向绑定
v-for 遍历数据
v-bind 绑定特殊属性
v-once 只渲染一次 数据改变不会影响该值的变化
v-on 绑定事件
v-on 可以绑定多个方法吗
可以 使用 v-on”{ click:dbclick,mouseomve:mouseclick }”
Vue-cli 中如何自定义指令
自定义指令有 5 个钩子;
bind 代表绑定指令到元素上,只执行一次
inserted 代表绑定指令的元素插入到页面时就调用(常用)
componentUpdated:指令所在组件的节点及其子节点全部更新完成后调用
update:所有组件节点更新时调用
unbind:解除指令和元素的绑定,只执行一次
如果时局部指令使用 directives:{ 自定义指令的名字:{ 钩子函数(el){ 操作逻辑 } } }写在 data 同级
如果时全局使用 Vue.directive(‘自定义的名字’,{钩子函数(el){ 操作逻辑 } })
Watch 请简述
监听 data 模型数据 当模型数据改变的时候就会触发;watch 初始化的时候不会运行,只有数据被改变之后才会运行
v-text 与区别{ { } }
{ { } }是模板插值,v-text 是指令,模板插值 { { } } 如果数据过多可能会把大括号显示出来(屏幕闪动)需要使用 v-cloak 指令
对 vue 中 keep-alive 的理解
切换过程中将状态保留在内存中,防止重复渲染 DOM,减少加载时间及性能消耗,提高用户体验性
用元素将其路由出口包裹起来;它有两个钩子函数 activated(激活后)和 deactivated(停用后)
如何让组件中的 css 在当前组件生效
Vue 组件中的 data 为什么是函数
data 数据会以函数返回值形式定义,这样每复用一次组件,就会返回一份新的 data,相当于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据.如果是对象形式,就使得所有组件实例共用了一份 data,就会造成数据的公用的结果.
Vue 双数据绑定过程中,这边儿数据改变了怎么通知另一边改变
当我们读取或者设置对象属性的时候,都会触发 Object.defineProperty()函数的 get 和 set 方法,在这两个方法中添加操作从而劫持数据,当属性发生变化的时候,会执行一系列的渲染视图操作
Vue 双向绑定的原理
数据劫持:当我们读取或者设置对象属性的时候,都会触发 Object.defineProperty()函数的 get 和 set 方法,在这两个方法中添加操作从而劫持数据,当属性发生变化的时候,会执行一系列的渲染视图操作
发布者订阅模式:对象间一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知.
Vue 中组件怎么传值
正向传值:
在子组件用 props 新建一个数据名并使用,来接收父组件传过来的值
在父组件中使用子组件,并以特殊属性的方式,把需要传递的数据通过 props 数据名传递给子组件
逆向传值:可以使用自定义事件传值 或者 ref 获取节点传值或 vuex 传值
需通过事件函数来触发一个自定义事件: this.$emit(“自定义事件名”,传递的数据)
在使用子组件的父组件中,使用事件绑定指令绑定抛出的自定义事件名并让其等于一个函数,函数的形参就是传递的数据
Vue 兄弟组件传值
可以使用传统的子传父 父再传子的办法或 vuex 或者 eventBus
在父组件中使用两个子组件让其成为兄弟
在兄弟 a 引入事件总线,并通过事件函数来触发一个自定义事件 eventBus.$emit(“自定义事件名”,”要传递的数据”)
在兄弟 b 引入事件总线,在钩子函数中使用 eventBus.$on(“自定义事件名”,(val)=>{})接收传递的数据,回调函数中的形参就是传递的数据
请简述插槽
父组件中子组件中数量不同内容也不相同的时候使用的技术; 子组件中插入 则可在父组件的子组件开标签内部写入标签和内容
Vue 首屏加载慢的原因,怎么解决的,白屏时间怎么检测,怎么解决白屏问题
使用路由懒加载(还有如将第三方依赖打包进入 CDN 服务器,按需引入 ui,压缩代码,精灵图等等)
Vuex 是什么?怎么使用?在那种场景下使用?
状态(数据)管理工具,就是一个数据的仓库,将数据全部存入仓库,组件就可以自由使用数据
将数据保存在 state 中,通过(this.$store.state.数据名)来调用数据,修改数据在Mutations中,在Actions调用mutations中的方法异步操作数据,在需要使用的组件通过this.$store.dispatch()来调用和传递实参
用于开发中大型 web 单页应用中对应用的状态进行管理或解决组件间数据通信麻烦的问题
vuex 的优势
能够集中管理和共享数据,易于开发和维护
能够高效地实现组件之间的数据传递,提高开发效率
数据都是响应式的,能够实时保持数据与页面的同步
Vuex 怎么请求异步数据
在 action 使用封装的 axios 发送请求,接收到数据后使用 commit 调用 mutation 给 state 数据赋值
Vuex 中 action 如何提交给 mutation 的
在组件内通过 this.$store.dispatch()调用 action 的方法并传递实参,在 action 中使用 commit 调用 mutation 方法,通过 mutation 修改 state 中的数据
vuex 有哪几种状态和属性
数据保存的地方:State
对数据过滤的:Getter
修改数据的:Mutation
处理异步操作的:Action
让数据模块化的:Module
vuex 的 State 特性是?
state 就是存放数据的仓库,特性就是若 store 中的 state 数据发生改变,依赖这个数据的组件也会相应更新(当 mutation 修改了 state 的数据的时候,他会动态的去修改所有的调用这个变量的组件里的值)
vuex 的 Getter 特性是?
vuex 的 Mutation 特性是?
同步执行,修改 state 数据的唯一途径,直接变更 state 数据.
vuex 的 actions 特性是?
用于异步请求数据,不能直接操作 state,需要通过提交给 mutation 来修改 state 数据
$set:解决 data 数据改变和视图不改变(vue3.0 以上已解决,面试常问)
vue 中的拖拽+js 原生拖拽说思路?
需要三个事件 鼠标 按下\移动\抬起
当按下时获取鼠标点击元素的内部鼠标的位置 移动时获取鼠标在页面的位置并减去元素内部鼠标的位置,同时判断边界条件,当减去的位置的 x 轴小于零代表出屏幕的左边,当减去的位置的 y 轴小于零代表出屏幕的右边,当大于时则需要算出页面的宽度高度(window.innnerXXX)减去元素的宽高度,当大于这个值就等于这个值, 当抬起时把移动的事件归位为 null
vue 中 assets 和 public 里的静态区别?
public 放不会变动的文件(相当于 vue-cli2.x 中的 static)public/ 目录下的文件并不会被 Webpack 处理:它们会直接被复制到最终的打包目录(默认是 dist/static)下.必须使用绝对路径引用这些文件,这个取决于你 vue.config.js 中 publicPath 的配置,默认的是/.
assets 放可能会变动的文件 assets 目录中的文件会被 webpack 处理解析为模块依赖,只支持相对路径形式.简单来说就是就是 public 放别人家 js 文件(也就是不会变动),assets 放自己写的 js 文件(需要改动的文件)
怎么提升页面性能?性能优化有哪些?
不用将所有的数据都在 data 中注册,不需要响应式的数据可以定义在实例上
v-for 循环生产的代码,要操作 dom 可以用事件委托
使用 keep-alive 缓存组件,防止切换路由时来回创建组件浪费性能
如果没有安全性考虑使用 v-show 代替 v-if 指令
使用路由懒加载
VUE3
TypeScript Typescript 是什么 请简述?
Typescript 与 javascript 的优势?
Typescript 优势
能帮助开发人员检测出错误并修改
TypeScript 工具使重构更变的容易、快捷
类型安全能在编码期间检测错误,可以更好的协作,对于大型项目更友好
便于开发人员做注释
javascript 优势
不需要编译,直接由浏览器执行
社区成熟,可以很方便地找到大量成熟的开发项目和可用资源
比较灵活
工程化工具 Webpack
Webpack 与 gulp 区别
Webpack 和另外两个并没有太多的可比性,Gulp/Grunt 是一种能够优化前端的开发流程的工具,而 WebPack 是一种模块化的解决方案,不过 Webpack 的优点使得 Webpack 在很多场景下可以替代 Gulp/Grunt 类的工具.
请简述 webpack 中的 loaders 与 plugin 的区别
loader:模块转换器(翻译器),如 less –> css, 如识别 js 结尾的,css 结尾的,图片格式结尾的,通过 loader 转换成相应的文件格式
plugin:扩展插件,如 HtmlWebpackPlugin
说一下 webpack 的打包原理
把所有依赖打包成一个 bundle.js 文件,通过代码分割成单元片段并按需加载.
说一下对 websocket 的理解
WebSocket 是 html5 出的一个持久化的协议,它是 HTTP 协议上的一种补充,因为 HTTP 协议通信只能由客户端发起,如果要获取一些实时性较高的信息,只能通过“轮询”一类的方式,但是要不停连接所以轮询的效率低,非常浪费资源.所以通过 websocket 协议可以让服务器主动向客户端推送消息解决了这种问题,做到双向平等对话
Vite
rollup babelrc eslint 网络 Get 和 post 有什么区别?
get 安全性低,post 有加密所有安全性高
get 发送的数据会暴露在地址栏并且会被保存在历史记录里,而 post 不会
get 参数通过 URL 传递,post 放在 Request body 中.
传输的数据量不同,get 传输的量小,不能大于 2kb,post 理论上没有限制
get 一般用于获取数据,post 一般用于发送数据
常见的 HTTP 状态码:
1##:信息响应类,表示接收到请求并且继续处理
2##:处理成功响应类,表示动作被成功接收、理解和接受
3##:重定向响应类,为了完成指定的动作,必须接受进一步处理
301 Moved Permanently 永久性转移/重定向
302 Move Temporarily 临时转移
307 Temporary Redirect 临时重定向,一般应用于服务器的负载均衡
304 Not Modified ——客户端已经执行了 GET,但文件未变化
4##:客户端错误,客户请求包含语法错误或者是不能正确执行
400 Bad Request 请求参数错误
401 Unauthorized 无权限访问
404 Not Found 地址错误
405 Method Not Allowed 当前请求的方式服务器不支持
5##:服务端错误,服务器不能正确执行一个正确的请求
500 Internal Server Error 未知服务器错误
503 Service Unavailable 服务器超负荷
http 是什么?有什么特点
http 是客户端浏览器或其他程序与 Web 服务器之间的应用层通信协议
特点:
简单快速灵活:客户向服务器请求服务时,只需传送请求方法和路径且 HTTP 允许传输任意类型的数据对象
无连接:每次连接只处理一个请求,收到用户应答后,就断开连接
无状态:协议对于事务处理没有记忆能力,如果后续处理需要前面的信息,则它必须重传
HTTP 协议和 HTTPS 区别
传输信息安全性不同;前者是超文本传输协议,信息是明文传输,后者是具有安全性的 ssl 加密传输协议
连接方式不同:前者很简单是无状态的,后者是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议
端口不同:前者端口是 80,后者是 443
什么是 csrf 攻击
csrf(cross-site request forgery)是跨站请求伪造;攻击者盗用了用户的身份,以用户的名义发送恶意请求,它可以做的事情包括 以用户的名义发邮件,发消息,购买物品和盗取用户账户
为什么会造成跨域/请简述同源策略
不符合同源协议(不同协议;不同域名/IP;不同端口号),
解决方法:
JSONP 请求:JSONP 的原理是利用 script 标签的跨域特性,可以不受限制地从其他域中加载资源
CORS 跨域:就是使用自定义的 HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是应该失败.
Web sockets 跨域:是 HTML5 一种新的协议.它实现了浏览器与服务器全双工通信,同时允许跨域通讯,在 js 中创建 WebSockets 后,会有一个 HTTP 请求发送到浏览器以发起连接.在取得服务器响应后,建立的连接会使用 HTTP 升级从 HTTP 协议交换为 WebSocket 协议,所以只能支持此协议的专门服务器才能正常工作
proxy 代理:请求不会直接发给目标主机,而是先发给代理机再向目标主机发送,接收目标机数据后再由代理服务器返还给用户
什么是 CORS
CORS 是一个 W3C 标准,全称是”跨域资源共享”(Cross-Origin Resource Sharing).它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 ajax 只能同源使用的限制.
后台传递过来的数据格式有哪些
json:一段用数组或对象表示得键值对
arraybuffer: 通用的、固定长度的原始二进制数据缓冲区,它是一个字节数组
blob : 一个不可变、原始数据的类文件对象.它的数据可以按文本或二进制的格式进行读取
document : 如 xml 一类的文件格式
text : 文本格式
stream : 数据流
算法题 数组去重的方式
双层 for 循环
1 2 3 4 5 6 7 8 9 10 11 12 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; }
利用 indexOf 去重
1 2 3 4 5 6 7 8 9 function unique (arr ) { let array = []; for (let i = 0 ; i < arr.length ; i++) { if (array.indexOf (arr[i]) === -1 ) { array.push (arr[i]); } } return array; }
利用 filter 过滤
1 2 3 4 5 6 function unique (arr ) { return arr.filter (function (item, index, arr ) { return arr.indexOf (item, 0 ) === index; }); }
利用 ES6 Set 去重
1 2 3 function unique (arr ) { return Array .from (new Set (arr)); }
数组排序的方式
冒泡排序
1 2 3 4 5 6 7 8 9 10 11 12 13 function sort (arr ) { let temp = null ; for (let i = 0 ; i < arr.length - 1 ; i++) { for (let j = 0 ; j < arr.length - i - 1 ; j++) { if (arr[j] > arr[j + 1 ]) { temp = arr[j]; arr[j] = arr[j + 1 ]; arr[j + 1 ] = temp; } } } return arr; }
擂台排序
1 2 3 4 5 6 7 8 9 10 11 12 13 function sort (arr ) { let temp = null ; for (let i = 0 ; i < arr.length - 1 ; i++) { for (let j = i + 1 ; j < arr.length ; j++) { if (arr[i] > arr[j]) { temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } return arr; }
sort 排序
1 2 3 4 5 6 function sort (arr ) { arr.sort ((small, big ) => { return small - big; }); return arr; }
遍历一个多维数组 1 2 3 4 5 6 7 function each (obj ) { for (let key in obj) { typeof obj[key] == "object" ? each (obj[key]) : console .log (key + "--" + obj[key]); } }
深拷贝的代码实现
简单深拷贝(一层拷贝)1 2 3 4 5 6 7 function deepCopy (obj ) { let copyObj = Array .isArray (obj) ? [] : {}; for (let key in obj) { copyObj[key] = obj[key]; } return copyObj; }
复杂深拷贝(递归实现多层拷贝)1 2 3 4 5 6 7 8 9 10 function deepCopy (obj ) { let copyObj = Array .isArray (obj) ? [] : {}; for (let key in obj) { if (obj.hasOwnProperty (key)) { copyObj[key] = typeof obj[key] === "object" ? deepCopy (obj[key]) : obj[key]; } } return copyObj; }
合并两个有序数组
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。 注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n