概述
面试题
//考察:原型,迭代器,可迭代协议
//让下面的代码成立
var [a,b] = {a:1,b:2}
//在执行这个代码前,执行以下代码
//把对象变成可迭代对象,因为数组就是可迭代对象
Object.prototype[Symbo.iterator] = function(){
return Object.values(this)
}
React
1.使用 useState 多次渲染,如何优化
2.react的hook组件有哪些?
3.类组件和函数组件有什么不同?
4.react的生命周期
5.useCallBack和useMemo优化在哪里
6.函数渲染安全还是使用类的this渲染安装
7.react源码解刨
Vue
1.vue 响应式原理如何实现?
vue2 通过object.defineProperty(发布订阅模式+数据劫持)
vue3 通过proxy
Observer负责将数据转换成getter/setter形式;
Dep负责管理数据的依赖列表;是一个发布订阅模式,上游对接Observer,下游对接Watcher
Watcher是实际上的数据依赖,负责将数据的变化转发到外界(渲染、回调);
首先将data传入Observer转成getter/setter形式;当Watcher实例读取数据时,会触发getter,被收集到Dep仓库中;当数据更新时,触发setter,通知Dep仓库中的所有Watcher实例更新,Watcher实例负责通知外界
https://juejin.cn/post/6844903597986037768
2.vue中如何让没有响应式数据变成响应式?
3.proxy和object.defineProperty 之间的优缺点
vue2
vue一般使用template来创建HTML,然后在有的时候,我们需要使用javascript来创建html,这时候我们需要使用render函数。
render参数里有一个函数叫h,h就是对单文件组件进行虚拟DOM的创建,然后通过render进行解析
template最终还是通过render的方式再次进行编译
相同之处:
1.render和template都是创建html
不相同之处:
1.template适合简单逻辑,render适合复杂逻辑
2.template理解容易,但灵活度不够高。自定义render灵活性高,但是对使用者要求高
3.render性能比template高
4.render使用js方式做渲染,template使用html方式做渲染
5.render函数优先级大于template
4.1 什么时候用render函数?
组件复杂时可以用render,简单直接使用template
创建一个各种类型的按钮为例子:
template
<template>
<div class="btn btn-success" v-if="type === 'success'">{{ text }}</div>
<div class="btn btn-danger" v-else-if="type === 'danger'">{{ text }}</div>
<div class="btn btn-warning" v-else-if="type === 'warning'">{{ text }}</div>
</template>
render
<script>
Vue.component("G-Btn", {
# 获取父级传来的值
props: {
type: {
type: String,
default: 'default'
},
text: {
type: String,
default: '按钮'
}
},
# 计算属性
computed: {
tag() {
switch(this.type) {
case'success':
return 1;
case'danger':
return 2;
case'warning':
return 3;
default:
return 1;
}
}
},
# 创建html
render(createElement) {
return createElement('div', {
class: {
btn: true,
'btn-success': this.type==='success',
'btn-danger': this.type==='danger',
'btn-warning': this.type==='warning'
},
on: {
click: this.handleClick
}
},
this.$slots.default
);
},
methods: {
handleClick() {
console.log('点击成功');
}
}
})
let appData= new Vue({
el: "#app"
})
</script>
5.template渲染过程
Vue推荐在绝大多数情况下使用template来创建你的HTML。但是模版毕竟是模版,不是真实的dom节点。从模版到真实dom节点还需要经过一些步骤:
1.把模版编译为render函数
2.实例进行挂载,根据跟节点render函数的调用,递归的生成虚拟dom
3.通过diff算法对比虚拟dom,渲染到真实dom(类似于react的虚拟DOM渲染过程)
4.组件内部data发生变化,组件和子组件引用data作为props重新调用render函数,生成虚拟DOM,返回到步骤3
6.在 Vue 中为什么不推荐用 index 做 key
1.用 index 作为 key 时,在对数据进行,逆序添加,逆序删除等破坏顺序的操作时,会产生没必要的真实 DOM更新,从而导致效率低
2.用 index 作为 key 时,如果结构中包含输入类的 DOM,会产生错误的 DOM 更新
3.在开发中最好每条数据使用唯一标识固定的数据作为 key,比如后台返回的 ID,手机号,身份证号等唯一值
4.如果不存在对数据逆序添加,逆序删除等破坏顺序的操作时,仅用于渲染展示用时,使用 index 作为 key 也是可以的(但是还是不建议使用,养成良好开发习惯)。
作者:政采云前端团队
链接:https://juejin.cn/post/7026119446162997261
来源:稀土掘金
7.mutation为什么是同步,action为什么是异步的?
Vuex
state:存储公共数据(相当于组件中的data)
mutations:修改数据方法的集合,必须是同步
getters:类似于computed计算属性(利用现有的状态进项计算得到新的数据—派生)
actions:异步操作
modules:模块化拆分
8.封装vue3组件的注意问题
前端
1.单向数据流和双向数据流的区别。
2.js中的this指针指向问题
https://zhuanlan.zhihu.com/p/159322871
箭头函数、call、apply、bind等手法改变this指向
3.call apply修改this指向
4.闭包的坏处,如何使用闭包?
5.原型链
6.作用域和作用域链
作用域:程序中定义变量的区域,它决定了当前执行代码对变量的访问权限
作用域链:可执行代码内部访问变量时,会先查找当前作用域下有无该变量,有则立即返回,没有则会去父级作用域中查找,一直找到全局作用域。这种作用域的嵌套机制称为作用域链
7.堆和栈
基本数据类型:直接存储在栈内存中,占据空间小,大小固定,属于被频繁使用的数据。
引用数据类型:同时存储在栈内存与堆内存中,占据空间大,大小不固定。引用数据类型将指针存在栈中,将值存在堆中。当我们把对象值赋值给另外一个变量时,复制的是对象的指针,指向同一块内存地址。
堆:存储引用类型值的空间
栈:存储基本类型和指定代码的环境
let a={},
b = "0",
c = 0
a[b] = "1111"
a[c] = "2222"
console.log(a[b])
结果为:2222
解刨:
a["0"] = "1111"
a[0] = "2222"
结论:对象属性不能重复,数字和字符串属性名相同,堆就不重要了
#ES6新增:Symbol 表示独一无二的值
let a={},
b = Symbol("1")
c = Symbol("1")
a[b] = "1111"
a[c] = "2222"
console.log(a[b])
结果为:1111
结论:对象属性名只能是字符串是不严谨的,这里看出还有Symbol的值
8.对象和数组的区别?
区别一:
数组表示有序数据的集合
对象表示无序数据的集合
如果数据对顺序很重要,就用数组,否则用对象
区别二:
数组的数据没有名称,对象的数据有名称
9.闭包
9.1
var test = (function(i){
return function(){
alert(i *=2)
}
})(2)
test(5)
结果为:"4"
因为alert会把值转为字符串
匿名函数可以访问外部 i 的值,因为匿名函数可以创建自己的执行上下文,会创建自己相应的作用域链和变量对象
9.1 执行上下文
每当运行代码时,代码就会执行上下文(执行环境)
执行上下文分为两个阶段:
创建阶段:作用域链(当前变量对象+所有父级变量对象),变量对象(参数,变量,函数声明),this
执行阶段:变量赋值,函数引用等
执行上下文分为:全局环境,函数环境,Eval环境
9.2 作用域链
9.2.1 作用域链例子一
作用域链是通过闭包实现
//books执行上下文
fucntion books(){
var book = "书包里的书本"
//匿名函数执行上下文
return function(){
console.log(book)
}
}
var bag = books()
//全局执行上下文
bag();
说明:
可执行代码内部访问变量时,会查找当前作用域是否有book变量,有就会立马返回输出,没有就会向父级作用域中查找,知道找到全局作用域
匿名函数执行上下文:{作用域链:{匿名函数变量对象+books变量对象+books变量对象+全局变量对象},{变量对象}}
books执行上下文:{作用域链:{books变量对象+全局变量对象},{变量对象:book}}
全局执行上下文:{作用域链:{全局变量对象},{变量对象:books,bag}}
作用域链的解释:
作用域链都是一层链接一层,每新建一层都会包含之前新建的一层变量对象,最顶层的链层是最优先的。这里的三层中匿名函数就是顶层链,所以是最优先执行
9.2.2 作用域链例子二
for(var i = 0;i<5;i++){
setTimeout(function(){
console.log(i++)
},4000)
}
console.log(i)
运行结果:
5,5,6,7,8,9
先运行主栈,然后再运行宏任务(setTimeout中的方法)
9.2.3 作用域链例子三
for(var i = 0;i<5;i++){
//立即执行
(function(x){
setTimeout(function(){
console.log(i++)
},4000)
})(i)
}
console.log(i)
运行结果:
5,0,1,2,3,4
先运行主栈,然后再运行宏任务(setTimeout中的方法)
10.null和undefined的区别
/**
* null 表示无对象
* undefined 表示没有值
*/
/**
* var a;
* a没有赋值所以是undefined
*/
/**
* document.getElementById("xxxxx") 等到为null
* 应该获取到一个dom对象,但是xxxxx不存在,所有是no object.所以是null
*/
什么时候用null?
你要表达的数值是一个对象,但是现在没有对象,就可以用null
什么时候用undefined?
当前值可以表示任何东西,但是他没有值,就可以用undefined
11.同步和异步
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")
},0)
async1();
new Promise(functtion(resolve){
console.log("promise1")
resolve();
}).then(function(){
console.log("promise2")
})
console.log("script end")
运行结果
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
浏览器是多线程
JS是单线程 => 浏览器只给其中一个线程来渲染
分析:
创建函数 async1
创建函数 async2
=》"script start"
设置定时器(异步) (宏任务A)
函数执行
=》"async1 start"
await async2() 执行async2,等待返回结果(微任务B)
=》"async2"
new Promise会立即执行函数(new的时候是同步)
=》"promise1"
resolve()/reject() (微任务C)
=》"script end"
主栈第一阶段完成
异步队列分为:微任务队列和宏任务队列
有宏任务执行完,才到微任务
微任务 宏任务
B:async2的结果,有结果 A:setTimeout() xxms执行
再执行下面代码
C:resolve/reject
执行的时候把then/catch
中的方法执行
12.宏任务和微任务
异步队列分为:微任务队列和宏任务队列
宏任务执行完,才到微任务
宏任务队列有:
1.setTimeout()和setInterval()
微任务队列有:
1.新程序或子程序被直接执行
2.事件的回调函数
3.Promise .then() .catch() .finally()
4.MutationObserver
遇到new promise直接执行,因为构造方法会直接执行
宏任务和微任务的运行机制
参考:https://blog.csdn.net/qq_44624386/article/details/107344664
13.defer和async的区别
因为js是单线程执行,所以async和defer可以解决网络的阻塞的问题,但是两则都只适用于外部脚本
async:异步处理函数
浏览器会立即进行下载,同时继续加载页面。但是async下的脚本具体什么时候执行就说不一定了
defer:推迟执行函数
不管脚本是否加载完,都会等到浏览器解析完html以后再执行脚本,defer比较适合与DOM有关联的脚本
14.Promise
Promise优点:
1.Promise能解决异步处理的问题
2.解决回调地狱
3.能对错误代码进行捕获
Promise缺点:
1.无法取消Promise,一旦新建他会立即执行,中途无法取消。
2.如果不设置回调,promise内部抛出的错误,外部是不会获取到。
3.如果处于pending等待状态时,无法得知目前进展到哪一个阶段。
15.typeof和instanceof的区别
typeof:检测数据类型输出类型名称
简单数据类型
Undefined,Null,Boolean,Number和String
复杂数据类型
Object
instanceof:检测对象之间的关联性。(常用在检测该属性和函数是否在原型链中)
16.函数柯里化
/**
* 什么是柯里化
* 把接收多个参数的函数编程一个接收一个参数的函数,并返回接收多个参数的时,返回新函数
* @returns {inner}
*/
//基本柯里化
// function add(){
// //把保存参数的arguments赋值给args变量
// let args = arguments
// let inner = function(){
// args.push(...arguments)
// }
// //返回inner内部函数实现基本的柯里化
// return inner
// }
//
// console.log(add(1));
// function add(){
// //Array.prototype.slice.call是把调用方法的参数截取出来
// let args = Array.prototype.slice.call(arguments)
// let inner = function(){
// args.push(...arguments)
// let sum = args.reduce((prev,cur)=>{
// return prev+cur;
// })
// return sum;
// }
// //返回inner内部函数实现基本的柯里化
// return inner
// }
//
// console.log(add(1)(2));
function add() {
// 将传入的不定参数转为数组对象
let args = Array.prototype.slice.call(arguments);
// 递归:内部函数里面进行自己调用自己
// 当 add 函数不断调用时,把第 N+1 个括号的参数加入到第 N 个括号的参数里面
let inner = function() {
args.push(...arguments);
return inner;
}
inner.toString = function() {
// args 里的值不断累加
return args.reduce(function(prev, cur) {
return prev + cur;
});
};
return inner;
}
// 测试一下
let result = add(1)(2)(3)(4);
console.log(result.toString());
//结果为:10
17.虚拟DOM
17.1 什么是虚拟DOM?
虚拟DOM就是一个普通的js对象,用于描述视图的界面结构。
在vue中,每个组件都有一个render函数,每个render函数都会返回一个虚拟DOM树,这也就意味着每个组件都对应一棵虚拟DOM树
17.2 为什么需要虚拟DOM?
在渲染时,直接使用真实DOM,因为真实DOM的创建,更新,插入等操作会带来大量的性能损耗,降低渲染效率。所以需要减少对真实DOM的操作
17.3 虚拟DOM是如何转换为真实DOM的?
在一个组件实例首次被渲染时,它先生成虚拟DOM树,然后根据虚拟DOM树创建真实DOM,并把真实DOM(挂载)到页面中适合的位置,此时,每个虚拟DOM便会对应一个真实的DOM
17.4 模板和虚拟dom的关系?
vue框架中有一个compile模块,它主要负责将模版转换为render函数,而render函数调用后得到虚拟DOM。
18.require和import的区别
require/exports
//module.js文件导出
module.exports = {counts, sayHello};
//module.js文件引入
const { xxx } = require('./module.js');
import/exports
//module.js文件导出
exports = {counts, sayHello};
//module.js文件引入
import { xxx } from'./module.js';
- import: 是编译时执行,理解为异步加载
- require: 是运行时执行,理解为同步加载
- 导入require 导出 exports/module.exports 是 CommonJS 的标准,通常适用范围如 Node.js
- import/export 是 ES6 的标准,通常适用范围如 React,Vue
- require性能比import稍低
因为 require 是在运行时才引入模块并且还赋值给某个变量,而 import 只需要依据 import 中的接口在编译时引入指定模块所以性能稍高
19.计算属性和methods方法的区别
计算属性 只有当数值更改后才能被触发。如果依赖的值,没有发生改变,使用的就是缓存中的属性
值
methods方法 无论什么时候,只要你触发了方法,数值都会被重新计算。不存在缓存机制。
20.性能优化
1.1 while循环快还是for循环快? 关于循环
1.2 0是不是比Math.floor性能好? 关于取值
1.3 if else与三元运算符哪个快? 关于逻辑运算
以上都不算不上性能优化,都是执行效率
20.1 输入url与浏览器渲染过程
20.2 什么是性能?什么是执行效率
操作虚拟dom就是性能问题
vue是数据驱动 => 虚拟dom 操作dom(影响到浏览器速度)
20.3 性能分析
页面加载性能(加载时间,用户体验)
动画与操作性能(是否流畅无卡顿)
内存占用(占用过大,浏览器崩掉等)
电量消耗(游戏方面,可不考虑)
20.4 性能优化有哪些?
a.加载
1.减少http请求 css和图片都能做的事,最好用css,因为图片也是一个请求
2.缩小文件大小(资源压缩)
3.cdn库
4.懒加载
5.服务端渲染,预渲染
b.动画与操作性能
1.减少dom操作 => 文档碎片
2. 避免回流
21.TCP协议三次握手和四次挥手
面试题视频链接
https://www.bilibili.com/video/BV1iE411q7Qd?spm_id_from=333.337.search-card.all.click&vd_source=65e0538de0332111f890a0bd755ca4e7
前端面试题:
https://juejin.cn/post/7049164339630047245
TCP和UDP的区别
22.防抖和节流的应用场景
防抖
防抖,顾名思义,防止抖动。用于将用户的操作行为触发转换为程序行为触发,防止用户操作的结果抖动。一段时间内,事件在我们规定的间隔 n 秒内多次执行,回调只会执行一次。
特点:等待某种操作停止后,加以间隔进行操作
1.持续触发不执行
2.不触发的一段时间之后再执行
节流
节流,顾名思义,控制流量。用于用户在与页面交互时控制事件发生的频率,一般场景是单位的时间或其它间隔内定时执行操作。一段时间内,事件在每次到达我们规定的间隔 n 秒时触发一次。
特点:每等待某种间隔后,进行操作
1.持续触发并不会执行多次
2.到一定时间 / 其它间隔 ( 如滑动的高度 )再去执行
作者:政采云前端团队
链接:https://juejin.cn/post/7018296556323340324
23.Typescript中的extends关键字理解
1.继承/拓展
2.泛型约束
3.条件类型与高阶类型
https://blog.csdn.net/qq_34998786/article/details/120300361
24.跨域是怎么引起?
跨域就是同源策略引起
同源策略: 同端口 同域名 同协议(如果这三个里有一个不同,就叫不同源)
跨域: 违反同源策略
常见的跨域方式有哪些?
-
jsonp
html中的script src属性获取其他源的数据 -
.cors 跨域资源共享 支持所有的主流浏览器
XMLHttpRequest 发送请求的时候,如果不同源
后台处理: 请求头部设置 Access-control-allow-origin -
h5 window.postMessage跨域
window.postMessage(“字符串”,“*”)
注意:vue的跨域: proxy代理跨域(本质上就是cors跨域)
vue.config.js里进行配置
25.AMD与CMD区别
https://juejin.cn/post/6844903541853650951#heading-2
26.资源提示符
资源提示符跟浏览器渲染密切相关
- async //异步加载
- defer //延迟加载
- preload //加载优先级大于prefetch
- prefetch //加载优先级较低,浏览器空闲时才加载
preload,prefetch加载的资源重复时,浏览器不会进行重复请求
27.脚本加载失败该怎么办?
1.什么时候重试?
使用window.addEventListener捕获脚本报错的时候重试
2.如何重试?
使用document.write阻塞浏览器进行重试
<!DOCTYPE html>
<html lang="en">
<head>
<script>
//需要替换的域名
const domains = [
"unpkg.com",
"www.bootcdn.cn",
"www.staticfile.org",
"cdn.baomitu.com",
"cdn.bytedance.com",
]
//重试次数
const maxRetry = 3;
const retryInfo = {}
window.addEventListener('error',(e)=>{
//通过标签,判断是脚本爆出的错误
const tag = e.target
//什么时候触发脚本加载失败?
if(tag.tagName === "SCRIPT" && !(e instanceof ErrorEvent)){
let url = new URL(tag.src)
if(!retryInfo[url.pathname]){
retryInfo[url.pathname]={
time:0,
nextIndex:0,
}
}
const info = retryInfo[url.pathname]
if(maxRetry>=info.time){
url.host = domains[info.nextIndex]
//阻塞页面后续的加载
document.write(`<script src="${url.toString()}"></script>`)
info.time++;
info.nextIndex = (info.nextIndex+1)%domains.length
}
}
},true)
</script>
</head>
<body>
<script src="https://333.com/vue"></script>
<script src="https://4444.com/pinia"></script>
</body>
</html>
28.计算属性(computed)和监听器(watch)的区别
- 计算属性的应用场景是计算的内容需要依赖多个属性的情况
侦听器的应用场景是计算的内容依赖一个属性的情况 - 计算属性缓存结果时每次都会重新创建变量
而侦听器是直接计算,不会创建变量保存结果 - computed的结果是通过return返回的,而watch不需要return
- watch中的参数可以得到侦听属性改变的最新结果,而computed函数没有这种参数。
29.外包线上面试前端
vue
-
v-model实现原理
-
MVVM简要
https://zhuanlan.zhihu.com/p/59467370 -
父子组件通信方式有哪些?
-
父子组件生命周期执行顺序
-
computed和watched的使用场景和区别
-
模板数据不同步,该如何同步?
-
vue2和vue3使用什么实现双向绑定?实现原理是什么?
-
keep-alive使用场景有哪些?
css
10. em rem px rpx的区别
-
css选择器优先级
-
什么是盒子模型?
-
display: none与visibility: hidden的区别
javascript
-
多个对象合并 多个数组合并方式有哪些,说出es5和es6
-
JavaScript 数据类型和数据结构
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Data_structures -
sessionStorage,localstorage,cookie的区别
-
防抖和节流的区别和概念
最后
以上就是仁爱橘子为你收集整理的前端面试点集合【持续更新】ReactVue前端的全部内容,希望文章能够帮你解决前端面试点集合【持续更新】ReactVue前端所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复