我的回答:看过,没记起来,当时想的是Session Storage, Local Storage相关的东西
强制缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程。
控制强制缓存字段的分别是Expires和Cache Control,其中Cache Control比Expires优先级高(补充304会分析为什么
强制缓存主要分三种:
1、不存在该缓存结果和缓存标识,强制缓存失效,则直接向服务器发起请求(跟第一次请求一致
2、存在该缓存结果和缓存标识,但结果已经失效,强制缓存失效,则使用协商缓存
3、存在该缓存结果和缓存标识,且该结果尚未失效,强制缓存生效,则直接返回结果
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,同样,协商缓存的标识也是在响应报文的HTTP头中和请求结果一起返回浏览器的。
控制协商缓存的字段分别有:
Last-Modified/If-Modified-Since, Etag/If-None-Match,其中Etag/If-None-Match的优先级比Last-Modified/If-Modified-Since高
协商缓存主要有以下两种情况:
1、协商缓存生效,返回304(缓存接着用?)
2、协商缓存失效,返回200和请求结果
我的回答:
少答了第三条,呜呜呜
正解:
我的回答:当时想的是useMemo、useCallBack,没get到点呜呜呜
正解:
1、组件Render时,避免state,props没变的子组件render
组件Render会导致其子组件render,子组件用PureComponent(类组件)和React.memo(函数组件)可以避免该情况。
// 类组件
class class ClassComp extends React.PureComponent{}
// 函数组件
function FnComp () {}
React.memo(FnComp)
2、函数组件Render时,避免变化的函数属性值,导致子组件Render,会监听第二个数组参数中的值,其中的值没有变化则不会重新渲染
用useCallBack包裹该函数
const handleClick = useCallback(() => ..., [])
return (
<ChildComp onClick={handleClick}>
)
3、组件Render时,属性值避免使用箭头函数值,导致子组件Render,直接是有实例方法
如果子组件的属性是个箭头函数,父组件每次刷新,箭头函数都是新的,会导致子组件重新render,属性值用实例方法就可以避免该问题
例如
handleClick = () => {...},
render() {
return (
<ChildComp onClick={handleClick}>
)
}
改为
<Mouse>
{this.renderChild}
</Mouse>
4、避免prop Drilling导致的中间组件的render
Prop Drilling 指将外层组件的state通过props一层层传下去,传递到很深的子组件的过程。外层组件的State发生变化,中间组件都会Render
层级很深的子组件可以直接取到值,不需要中间属性的传递,使用Context APi或者Redux等状态管理工具可让子组件直接取到值
示例
// 父组件提供数据
<ThemeContext.Provider value={{ theme: this.state.theme }}>
<Comp1>
<Comp2>
<Comp3>
<ThemeContext.Consumer>
{({theme}) => {
// 子组件拿值
}}
</ThemeContext.Consumer>
</Comp3>
</Comp2>
</Comp1>
</ThemeContext.Provider>
我的回答:
正解:
1、类组件是ES6使用class定义的组件,函数组件是接受单一props并返回一个React元素
2、钩子更简洁,数据的状态和操作方法分离
区别:
1、状态的有无:
hooks出现之前函数组件没有实例、生命周期、State、this,称函数组件为无状态组件
2、调用方式的不同:
函数组件重新渲染,将重新调用组件方法返回新的react元素
类组件重新渲染将new一个新的组件实例,然后调用render类方法返回组件实例
3、因为调用方式不同,在函数组件中使用会出现问题:
在操作中改变状态值,类会返回最新的值,而函数组件会按照顺序返回状态值
我的回答:
类、箭头函数、var、let、promise,当时就记得这些,主要是之前看到一篇博客就介绍了几个,面试完一看发现哇竟然这么多ES6新特性
正解:
// 作用与 arguments 类似
function add(...args){
console.log(args);
}
add(1,2,3,4,5);
// rest 参数必须是最后一个形参
function minus(a,b,...args){
console.log(a,b,args);
}
minus(100,1,2,3,4,5,19);
// 展开数组
let ls = ['1','2','3'];
function fn(){
console.log(arguments);
}
fn(...ls)
//父类
class Phone {
//构造方法
constructor(brand, color, price) {
this.brand = brand;
this.color = color;
this.price = price;
}
//对象方法
call() {
console.log('我可以打电话!!!')
}
}
//子类
class SmartPhone extends Phone {
constructor(brand, color, price, screen, pixel) {
super(brand, color, price);
this.screen = screen;
this.pixel = pixel;
}
//子类方法
photo(){
console.log('我可以拍照!!');
}
playGame(){
console.log('我可以玩游戏!!');
}
//方法重写
call(){
console.log('我可以进行视频通话!!');
}
//静态方法
static run(){
console.log('我可以运行程序')
}
static connect(){
console.log('我可以建立连接')
}
}
//实例化对象
const Nokia = new Phone('诺基亚', '灰色', 230);
const iPhone6s = new SmartPhone('苹果', '白色', 6088, '4.7inch','500w');
//调用子类方法
iPhone6s.playGame();
//调用重写方法
iPhone6s.call();
//调用静态方法
SmartPhone.run();
//添加标识的 Symbol
let s2 = Symbol('scorpios');
let s2_2 = Symbol('scorpios');
console.log(s2 === s2_2); // false
//使用 Symbol for 定义
let s3 = Symbol.for('scorpios');
let s3_2 = Symbol.for('scorpios');
console.log(s3 === s3_2); // true
function makeRangeIterator(start = 0, end = Infinity, step = 1) {
let nextIndex = start;
let iterationCount = 0;
const rangeIterator = { //对象
next: function() {
let result;
if (nextIndex < end) {
result = { value: nextIndex, done: false }
nextIndex += step;
iterationCount++;
return result;
}
return { value: iterationCount, done: true }
}
};
return rangeIterator;
}
let it = makeRangeIterator(1, 10, 2);
let result = it.next();
while (!result.done) {
console.log(result.value); // 1 3 5 7 9
result = it.next();
}
console.log("Iterated over sequence of size: ", result.value); // 5
function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
for (let i = start; i < end; i += step) {
yield i;
}
}
var a = makeRangeIterator(1,10,2)
a.next() // {value: 1, done: false}
a.next() // {value: 3, done: false}
a.next() // {value: 5, done: false}
a.next() // {value: 7, done: false}
a.next() // {value: 9, done: false}
a.next() // {value: undefined, done: true}
代码说明:
生成器会按需计算它们的产生值,这使得它们能够有效的表示一个计算成本很高的序列,甚至是如上所示的一个无限序列。
The next()
方法也接受一个参数用于修改生成器内部状态。传递给 next()
的参数值会被 yield 接收。要注意的是,传给第一个 next()
的值会被忽略。
function* fibonacci() {
var fn1 = 0;
var fn2 = 1;
while (true) {
var current = fn1;
fn1 = fn2;
fn2 = current + fn1;
var reset = yield current;
if (reset) {
fn1 = 0;
fn2 = 1;
}
}
}
var sequence = fibonacci();
console.log(sequence.next().value); // 0
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 2
console.log(sequence.next().value); // 3
console.log(sequence.next().value); // 5
console.log(sequence.next().value); // 8
console.log(sequence.next(true).value); // 0
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 2
//1. 数组的结构
const Hua = ['小花','刘花','赵花','宋花'];
let [xiao, liu, zhao, song] = Hua;
// 结构赋值完,可以直接使用
console.log(xiao);
console.log(liu);
console.log(zhao);
console.log(song);
//2. 对象的解构
const zhao = {
name: '赵本山',
age: '不详',
xiaopin: function(){
console.log("我可以演小品");
}
};
let {name, age, xiaopin} = zhao;
console.log(name);
console.log(age);
console.log(xiaopin);
xiaopin();
let {xiaopin} = zhao;
xiaopin();
get和set方法:
当对某个属性进行获取时,调用get方法;当对某个属性进行修改时,调用set方法。
模块功能主要由两个命令构成:export和 import
export命令用于规定模块的对外接口
import命令用于输入其他模块提供的功能
export暴露方式: 统一暴露(暴露对象:export {})、分别暴露(分别使用export)、默认暴露(export default{})。
import 导入方式:通用导入、结构赋值导入、针对默认暴露方式
//1. 通用的导入方式,引入 m1.js 模块内容
import * as m1 from "./src/js/m1.js"; //统一暴露
//2. 解构赋值形式
import {school, teach} from "./src/js/m1.js"; //分别暴露
import {school as guigu, findJob} from "./src/js/m2.js";
import {default as m3} from "./src/js/m3.js";
//3. 简便形式 //默认暴露
import m3 from "./src/js/m3.js";
console.log(m3);
_proto_
、 setPrototypeOf、 setPrototypeOf可以直接设置对象的原型
//1. Object.is 判断两个值是否完全相等
console.log(Object.is(120, 120));// ===
console.log(Object.is(NaN, NaN));// ===
console.log(NaN === NaN);// ===
//2. Object.assign 对象的合并
const config1 = {
host: 'localhost',
port: 3306,
name: 'root',
pass: 'root',
test: 'test'
};
const config2 = {
host: 'http://scorpios.com',
port: 33060,
name: 'scorpios.com',
pass: 'iloveyou',
test2: 'test2'
}
console.log(Object.assign(config1, config2));
//3. Object.setPrototypeOf 设置原型对象 Object.getPrototypeof
const school = {
name: 'scorpios'
}
const cities = {
xiaoqu: ['北京','上海','深圳']
}
Object.setPrototypeOf(school, cities);//不改变school原有属性,只是school的proto指向cities
console.log(Object.getPrototypeOf(school));
console.log(school);
参考链接:https://blog.csdn.net/zxd1435513775/article/details/119862852
我的回答:少说了第一个
100
–199
)200
–299
)300
–399
)400
–499
)500
–599
)钩子函数是指在特定事件发生时自动执行的函数。它们通常用于在程序执行某些操作之前或之后执行其他操作,例如在提交表单之前验证表单数据。
钩子函数不能放在 if 语句中的原因是,如果条件不成立,该函数将不会被执行。这可能会导致一些不可预测的行为,例如在某些情况下缺少必要的验证或清理步骤。就容易导致调用顺序的不一致性,从而产生难以预料到的后果。
此外,将钩子函数嵌套在 if 语句中会使代码更难以维护和理解。最好将钩子函数放在函数或类中,并在需要时调用它们。这样可以使代码更具可读性,易于维护和重用。
分为cookie和token两种认证方式
token 的认证方式是无状态的,服务端不保存登陆状态,也不关心哪些客户端签发了 token ,每个请求都会携带 token 通常在 header 中,也可以出现在 body 和 query 如下:
我个人感觉token可以通过设置setTimeout回调函数来手动实现时效性权限认证,不知道对不对
这道题比较简单,就是通过事件捕获来判断事件触发的位置
当时说的比较乱,大概就说了id选择器、内联样式、类选择器(这里想指的是标签选择器)
!important(正无穷)》行内样式(1000)》id选择器(0100)》class = 伪类 = 属性(0010)》标签选择器 = 伪元素(0001)》通配符(*)、子类选择器(>)、兄弟选择器(0000)(+),256进制
可以使用flex布局
1、使用flex布局
2、网格布局
相关链接:https://blog.csdn.net/qq_39903567/article/details/1147515
里面介绍了很多常见的CSS布局
对React的打包印象不是很深,因为之前接触过Vue打包相关的一些东西,这里就结合自己的经历说了之前在使用Vue进行开发时,发现Webpack打包比较慢,就有用Vite进行打包,当时看网上说Vite打包比较快,然后面试官就很打趣的问我:“我感觉你的项目应该不是很大吧,用Webpack打包很慢么?”
1、目前前端主流的打包工具是Webpack,是一个静态模块打包器(moudule bundler),当webpack处理应用程序时,其会递归地构造一个依赖关系图,其中包含应用程序的每一个模块,然后将所有这些模块打包成一个或多个buddle
2、webpack 通过 Tapable 来组织这条复杂的生产线。 webpack 在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条生产线中,去改变生产线的运作。
入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。
进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
每个依赖项随即被处理,最后输出到称之为 bundles 的文件中。
告诉webpack在哪里输出这些bundles,以及如何命名,默认为./dist
Webpack中一切皆模块,一个模块对应着一个文件,Webpack会从配置的Entry开始递归找出所有依赖的模块
代码块,由一个或多个模块组成,用于代码合并和分割
让Webpack可以去处理非JS的文件(Webpack自身只理解JS),loader将文件转换为Webpack可以处理的有效模块
用于执行范围更广的任务,从打包优化到压缩,到重新定义环境变量
1、初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数。
2、开始编译:用参数初始化Compiler对象,加载所有配置的插件,执行对象的run方法开始编译
3、确定入口:根据配置中的 entry 找出所有的入口文件。
4、编译模块:从入口文件出发,调用所有配置的Loader对模块进行翻译,再找出该模块的依赖的模块,递归本步骤直到所有入口依赖文件都经过该步骤
5、完成模块编译:得到每个模块的翻译内容及其依赖关系
6、输出资源:根据入口和模块之间的依赖关系,组装成多个chunk,再把每个chunk转换成一个单独的文件加入到输出列表
7、输出完成:根据配置确定输出路径和文件名,把文件内容写入到文件系统
Webpack会在特定时间点广播出特定事件,插件监听到对应的事件后执行特定的逻辑
分析出Webpack打包中loader和plugin的耗时
我沙比了,当时就写了个Onlogn的,面试官提醒之后才想到On的解法
#阿里前端实习面经##24届暑期实习##软件开发2023笔面经#