当前位置: 首页 > 面试经验 >

一个大二前端的实习面试初体验

优质
小牛编辑
115浏览
2023-03-28

一个大二前端的实习面试初体验

先介绍以下我目前的情况,我目前是一名大二的学生,因为下学期的课比较水,想着能不能找一个实习去磨练一下自己,也试试自己的水平能不能被面试上,然后前段时间在家投递了一些前端日常实习岗位,到现在为止面试了一共6场,1Offer,1拒,1二面,2凉

这两天刚结束了两场比较烦人的开学考试,正好有了点时间,想把之前几场面试中没有回答上来或者是还可以再优化的地方复盘一下,牢固一下不熟悉的知识点,也写一下对于最近的总结

京东健康

从第一场京东健康开始,这场面试是我第一次面试,全程很紧张,虽然题目整体比较简单,但是我回答的很牵强,但是面试官不错,一直在鼓励我,但是最后还是婉拒了,让我再继续沉淀一下,下面来复盘一下这场面试我没回答好的几个问题

这是完整的问题

1.自我介绍

2.问项目中有哪些难点,怎么解决的

4.代码遇到冲突怎么办

5.分支上代码没写完怎么缓存

6.怎么合并对象

7.正则怎么判断字符串里面有没有某东西

8.浏览器怎么数据缓存

9.cookie怎么防止被篡改

10.原型链的理解

11.用React怎么渲染服务器传来的数据

12.怎么继承类

13.反问

1. git stash

这个问题是这样的,面试官问我,如果你在当前分支写完一部分代码,但是没有写完,想缓存一下刚写的代码然后去写另一个分支的代码,这时候该怎么办,我当时答的是git add,明显存在一些问题,然后面试官问我有没有了解过git stash,我显然没有了解过,然后这道题就过去了,下面我来总结下git add 和 git stash的区别

git stash的用法:

git stash会把所有的未提交的修改(包括暂存的和未暂存的)都储存起来,用于恢复当前的工作目录,而且当stash之后,这时候当前的工作目录会变得干净

当解决完另一个分支的问题之后再切换会stash之后的分支后可以使用git stash pop 或者 git stash apply命令来恢复stash之前的状态,这二者的区别在于pop会将缓存堆栈中第一项删除,而apply不会删除,可以反复使用

可以用git stash list命令来查看当前stash的列表

可以使用git stash drop + stash名删除某个stash,也可以通过git stash clear来删除所有的stash

2. SessionStorage,LocalStorage,Cookie区别

这三者都是用于浏览器中存储数据的,他们有以下这些主要区别

  1. 存储的时间不同

cookie的有效期是可以设置的,默认情况下是关闭浏览器后失效。 sessionStorage的有效期是仅存在于当前会话,关闭当前会话或者管理浏览器后就会失效。localStorage的有效期是只要没有手动删除就一直存在

  1. 存储的大小不同

cookie的存储在4kb左右,储存量较小。其他二者的储存容量在5mb左右(不同浏览器中可能存在差异)

  1. 与服务端通信

cookie会参与到与服务端的通信当中,一般会携带在http请求的头部中,比如身份认证,其他二者是单纯的前端储存,不参与服务器通信

3. Cookie怎么防止被篡改

Cookie的安全规范:

  • 不放重要数据,重要数据放Session。我们已经知道 Cookie 不安全了,就不要作死非把重要数据放在 Cookie 里了
  • Cookie 数据加签名。对 Cookie 数据添加签名,这样 Cookie 如果被篡改了,服务端使用的时候通过校验签名就能发现了。
  • Cookie数据加密。加密后数据就很难篡改了,但是加解密过程会带来性能损耗,这个就要进行衡量了。
  • 开启 Cookie 的 httponly 参数,让 Cookie 只能在网络请求中被传输,避免js操作和获取 Cookie
  • 全站 HTTPs + Cookie secure 设置。避免 Cookie 在网络传输过程中被劫持。仅仅设置全站 HTTPs 是不够的,还得开启 Cookie secure 设置,否则当secure的限制没有开启时,那么在一个HTTPs 的网站中,一个 XSS 攻击还是能通过 HTTP 读取到 HTTPs 下的 Cookie。
  • 设置更小作用域的 Cookie,尽可能给 Cookie 设置合适且小的作用域。

4.正则怎么判断字符串中有没有某个东西

我正则之前一点都没学过,从来都是面向搜索引擎编程,这下给我问住了,这部分内容过多,且大部分同学应该都会,在这里就不赘述了。

其他问题感觉回答的还可以,但是第一场面试实在是没经验,也是太紧张,所以直接凉凉了

百度

面完京东健康的第二天接到了百度的面试邀请,这次整体发挥的不错,基本上都能回答上来,问面试官面评的时候也说我很不错,但是就是没后话了

这是完整的问题

电话面试 1h

1.自我介绍

2.平常怎么学习的前端

3.ES6新特性有哪些

4.let const var的区别

5.js类型判断有哪些方法 怎么判断对象还是数组 Obj.prototype.toString.call方法判断null 和undifined会返回什么 怎么去实现一个判断数据类型的方法

6.怎么去遍历数据

答:对象有for of 数组用for in 或者是forEach map等方法

7.for in和 for of有什么区别 for in能不能遍历对象 for of能不能遍历数组 for in能不能遍历到对象继承的数据

8.Object.keys values entries的区别

9.CSS优先级问题

10.postion中fix和 absolute的区别

11.怎么做移动端适配

12.em 和 rem有什么区别

13.前端性能优化

14.回流与重绘有什么区别 怎么避免

15.ESmodule和CommonJS有什么区别

16.V8垃圾回收机制

17.观察者模式怎么实现

18.怎么做技术选型

19.反问:面评怎么样

1. Obj.prototype.toString.call方法判断null 和undifined会返回什么

2. 怎么实现判断数据类型的方法

function checkType(value) {
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
}

console.log(checkType([])); // "array"
console.log(checkType({})); // "object"
console.log(checkType(42)); // "number"
console.log(checkType("Hello")); // "string"
console.log(checkType(true)); // "boolean"
console.log(checkType(null)); // "null"
console.log(checkType(undefined)); // "undefined"
console.log(checkType(function(){}));// "function"

3. postion中fix和 absolute的区别

这里直接回顾以下position的属性

absolute: 绝对定位,相对于static以外的一个父元素进行定位,可通过top bottom left right来定位

relative:相对定位,相当于他原来的位置精选定位 可通过top bottom left right来定位

fiexed:绝对定位相对值屏幕视窗(viewport)来定位,可通过top bottom left right来定位

static:默认值,没有定位,元素会出现在正常的文档流中,忽略top bottom left right z-index声明

inherit:继承父元素postion的值

那这道题该这么回答:

fixed和absolute都是绝对定位,二者区别在于定位的参照不同,absolute是相对于static以外的一个父元素进行定位,而fixed是相对于viewport来定位

4. for in 和 for of的区别

for in 和for of都是用于遍历对象和数组的语句,他们的区别如下

  1. for in 遍历对象的属性名,而for of遍历的对象的属性值
  2. for in 可以遍历对象自身的属性和继承的属性,而for of只能遍历对象自身的属性
let arr = [1, 2, 3, 4, 5];
arr.foo = "hello";

// for...in 循环遍历属性名
for (let i in arr) {
console.log(i); // 输出 0, 1, 2, 3, 4, foo
}

// for...of 循环遍历属性值
for (let i of arr) {
console.log(i); // 输出 1, 2, 3, 4, 5
}

因此,一般使用for of来遍历数组,for in来遍历对象

5. 回流与重绘有什么区别 怎么避免

回流和重回都是浏览器中用于更新页面的概念,他们的区别如下

  • 回流:当页面中元素发生结构上的改变,比如改变元素大小,位置,内容等,会触发浏览器重新计算页面中元素的位置和几何结构,然后重新布局整个页面,这个过程叫回流,只要有回流就会发生重绘
  • 重绘:当页面中元素样式改变,但不影响在页面中位置时候,会使用新的样式重绘这个元素,而不需要重新布局,这个过程叫重绘,重绘不一定会回流

以下是一些可以避免回流和重绘的方法:

  1. 避免频繁操作DOM,可以使用缓存或者批量更新的方式,一次性对多个DOM进行修改,减少回流和重绘的次数
  2. 使用CSS3动画代替js动画,CSS3动画利用GPU加速
  3. 使用离线DOM操作,可以先将DOM隐藏起来,进行一系列操作之后再显示
  4. 对于需要频繁获取元素大小或者位置的操作,可以将结果缓存,避免多次计算
  5. 对于复杂布局,可以使用绝对定位脱离文档流,避免对其他元素的影响

6. ESmodule和CommonJS有什么区别

  1. 用法不同:ES module是原生支持的Javascript模块系统,使用import/export 关键字实现模块的导入和导出。而CommonJs是Node最早引入的模块化方案,采用require和module.exports实现模块的导入和导出
  2. 加载方式不同:ES module是在编译时静态加载,就是说在编译时就能确定依赖关系,而CommonJs是在运行时动态加载,也就是说在运行时才能确定依赖关系
  3. 导入和导出特性不同:ES module支持异步导入,动态导入和命名导入等特性,可以根据需要动态导入导出。而CommonJs只支持同步导入导出
  4. 循环依赖处理方式不同:ES module采用在编译阶段解决并处理,而CommonJS只能在运行时抛出错误
  5. ESmodule需要在支持ES6的浏览器或者Nodejs的版本才能使用,而CommonJS的兼容性会更好

7.V8垃圾回收机制

V8采用了分代垃圾回收算法,将内存分为新生代和老生代两个区域,分别使用不同的垃圾回收策略。新生代中对象生命周期短暂。垃圾回收比较繁琐,使用了基于Scavenge算法的垃圾回收策略,而老生代中对象生命周期较长,垃圾挥手比较复杂,使用了基于标记清除,标记整理,增量标记等算法的垃圾回收策略

在新生代当中,V8将内存分为两个相等的空间,分别为From空间和To空间。当From空间被占满的时候,V8会将Form空间中存活对象复制到To空间,并且将Form空间进行清空,然后将From空间和To空间交换,这样就完成了一次垃圾回收

在老生代中,V8会通过标记-清楚算法和标记-整理算法进行垃圾回收。标记-清除算法首先会遍历整个对象图,并且将所有的对象活动标记出来,然后清除未标记的对象。标记-整理算法则会将所有活动对象移动到内存的一端,然后将另一端的空间全部清空,这样就可以获得一块连续的内存空间。为了避免垃圾回收时间过长导致的阻塞,V8还采用了增量标记算法,将垃圾回收的过程拆分成多个阶段,每个阶段完成后都会让JavaScript程序执行一段时间

京东电商

这是我面试中我个人觉得最难的一次,主要难点看代码说输出的部分,但是前面的八股部分也正好问到了我不太熟悉的地方,所以没啥意外的凉了

这是完整的问题:

1.js数组有哪些方法

2.reduce有哪些参数 slice有哪些参数

3.ul里面有一堆li,需要点击li弹出li的内容,怎么实现 ×

4怎么实现三栏布局

5.css3有哪些新特性,渐变怎么用

6.正向代理和反向代理 ×

7.什么是跨域,怎么解决

8.JSONP是什么原理 ×

那么多跨域方法就挑了个我不会的,倒霉的

9.什么是预检请求 ×

我答出来了OPTIONS,然后就啥也不知道了

10.问我计网学了点啥

11.localStorage sessionStorage cookie的区别

12.为什么身份认证用cookie不用其他俩

13.看代码说输出 ×

全是关于变量的,这部分死的透透的。之前没看过这种题,啥也不会。

14.js事件循环

  1. 算法 千分位转换

1.ul里面有一堆li,需要点击li弹出li的内容,怎么实现

这里考察的是事件冒泡和事件捕获 ,我当时答的是给每个li都添加点击事件。面试官问我这样有什么问题,我说性能消耗大,然后面试官接着问我怎么解决,我就没答上来

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</body>
<script>
const ul = document.querySelector('ul');
ul.addEventListener("click",(e) => {
console.log(e.target)
})
</script>
</html>

然后下面是js事件模型的总结

js事件模型包括事件冒泡和事件捕获。事件冒泡是指事件从最底层的元素开始向上传播,事件捕获则是相反,他从最顶部的元素开始向下传播

要实现事件冒泡和事件捕获,可以通过addEventListener()方法,此方法用于在元素上添加事件监听器,可以传递三个参数:监听的事件类型,回调函数,是否在捕获阶段处理事件。在默认情况下,事件监听器会在事件冒泡阶段触发,如果要在事件捕获阶段触发,需要将第三个参数设置为true

下面是一段示例代码

// 事件冒泡示例
let parentElement = document.getElementById("parent");
let childElement = document.getElementById("child");

parentElement.addEventListener("click", function() {
console.log("Clicked parent element");
});

childElement.addEventListener("click", function() {
console.log("Clicked child element");
});

// 点击子元素后输出:
// Clicked child element
// Clicked parent element


// 事件捕获示例
let parentElement = document.getElementById("parent");
let childElement = document.getElementById("child");

parentElement.addEventListener("click", function() {
console.log("Clicked parent element");
}, true);

childElement.addEventListener("click", function() {
console.log("Clicked child element");
}, true);

// 点击子元素后输出:
// Clicked parent element
// Clicked child element

在事件冒泡示例中,当点击子元素时,最先触发子元素的事件监听器,然后冒泡到父元素的点击事件监听器

在事件捕获示例中,当点击子元素时,首先触发父元素的点击事件监听器,然后捕获到子元素的点击事件监听器

2.正向代理和反向代理

正向代理是指客户端向目标服务器请求资源时向正向代理服务器发送请求,并指定目标服务器,然后代理服务器向目标服务器转发请求,将内容返回给客户端。一般正向代理需要在客户端进行一些特殊的设置才行,主要解决了访问限制的问题

反向代理是指代理服务器来接收客户端的请求,然后再将请求转发给内部网络上的服务器,将服务器上的结果返回给客户端。对于客户端来说,反向代理就相当于目标服务器,一般客户端不需要进行特殊配置,一般提供负载均衡,安全防护的作用

3.JSONP的原理

JSONP的实现原理是通过动态创建一个<script>标签,将请求的数据作为参数拼接在URL中,并指定回调函数的名称,然后通过这个<script>标签来向服务器请求数据,服务器收到请求后将数据以参数的形式传递给回调函数,最后将数据作为参数传递回来,由于这个回调函数实在同域下执行的,因此就解决了跨域问题。注意JSONP只能用于GET请求

要想实现一个JSONP,可以按照以下的步骤

1.在HTML中创建一个回调函数,例如handleData,并在全局范围定义它。这个函数将在数据请求成功后执行并处理返回后的数据

<script>
function handleData(data) {
// 处理返回的数据
}
</script>

2.动态创建一个<script>标签,并将数据源的URL作为其src的属性值,并将回调函数名称以参数的形式传递给服务器,例如`http://example.com/data?callback=handleData`。

<script>
var script = document.createElement('script');
var url = 'http://example.com/data?callback=handleData'; // 数据源的 URL
script.src = url;
document.head.appendChild(script);
</script>

3.服务器接收到请求之后,将数据作为参数传递给回调函数并将其包装在函数调用中返回。如果使用Nodejs,可以使用querystring模块解析请求参数并返回符合JSONP规范的响应

const http = require('http');
const querystring = require('querystring');

const server = http.createServer((req, res) => {
const params = querystring.parse(req.url.split('?')[1]);
const data = { "name": "John", "age": 30 };
const callback = params.callback;

res.writeHead(200, { 'Content-Type': 'text/javascript' });
res.end(`${callback}(${JSON.stringify(data)})`);
});

server.listen(80);

4.当数据源返回响应时候,浏览器会将响应解释为js代码,并在页面中执行回调函数。在handleData函数中,可以通过参数访问返回的数据并且进行处理

4.预检请求

是指在跨域请求时候,浏览器会首先发送一个预检请求,以确定实际请求可否被服务器所接受。是CORS规范中一部分。可以提高程序的安全性

在跨域请求中,如果请求的方法不是简单请求(例如,使用 PUT 或 DELETE 方法),或者请求头包含了一些自定义头部,那么浏览器会发送一个 OPTIONS 请求(预检请求)到服务器端,以获取一些必要的信息,包括:

  • 请求的方法是否被服务器所允许;
  • 请求头中是否包含了某些不支持的内容类型;
  • 是否需要在请求中使用凭据(例如,cookies、HTTP 认证等)。

服务器收到预检请求后,会根据请求中的 Origin 字段来确定是否接受请求,如果接受,则在响应头中设置一些额外的信息,包括:

  • Access-Control-Allow-Origin:指定可以接受请求的源地址;
  • Access-Control-Allow-Methods:指定可以接受的请求方法;
  • Access-Control-Allow-Headers:指定可以接受的请求头;
  • Access-Control-Allow-Credentials:指定是否可以在请求中使用凭据。

浏览器收到预检请求的响应后,如果服务器允许该跨域请求,则会发送实际的请求。如果服务器拒绝该跨域请求,浏览器会在控制台中显示相应的错误信息。需要注意的是,预检请求会增加额外的网络开销,因此应该尽量避免在跨域请求中使用不必要的自定义请求头或不常用的请求方法,以减少预检请求的发送次数。

轻流

这是我面试过唯一的小厂,面下来整体感觉很舒适,面试官比较和蔼,HR小姐姐也比较温柔,效率也比较高,某一天早上给我打的电话预约笔试,笔试做完后没两个小时就通知我预约一面,第二天早上又预约二面,第三天早上就电话沟通给offer了

这是完整考题:

视频面试 1h

1.先问了半个小时我的项目,还有团队,因为我简历上这部分东西比较多

2.项目中权限怎么做的

3.说下对RBAC的理解,怎么实现

4.怎么设计数据模型

5.怎么批量添加用户,在这个功能上To B 和To C有啥区别

6.问我在项目里怎么做前端基础架构

7.怎么做的技术选型,为什么不开发通用的脚手架去生成项目

8.Vue的templete是怎么生成AST的 ×

9.V-model的原理 怎么做的数据双向绑定

10.react 的 setState是同步还是异步 ×

11.讲下vue的nextTick×

12.说下JS的事件循环机制

13.textarea里面内容,如果有 // 注释要进行提示,但是要去除http://等影响,

这次面试的感觉和大厂完全不一样,比起大厂问八股,这家公司更加倾向于我的综合实力,问了许多我项目中的问题,还有很多开放试题让我自由发挥

1.Vue的templete是怎么生成AST的

在 Vue 中,模板会被解析成一个抽象语法树(Abstract Syntax Tree,AST),然后再被转换成渲染函数,最终渲染成 DOM。

Vue 中的模板编译器会把模板解析成一个抽象语法树(AST),这个 AST 是由一系列节点构成的树形结构,每个节点代表了一个模板中的元素、指令、文本等等。模板编译器会遍历模板中的每个节点,解析出它们的含义,然后构造成相应的 AST 节点。这个过程中会使用一些规则来判断哪些节点需要被解析成元素、哪些节点需要被解析成指令,等等。

在解析模板的过程中,模板编译器还会收集模板中用到的各种信息,包括组件、指令、过滤器等等。这些信息会被存储在编译选项对象中,供后续的编译和渲染使用。

最后,模板编译器会把生成的 AST 节点转换成渲染函数,这个渲染函数就是 Vue 组件的核心。渲染函数中包含了组件的各种属性和方法,以及对应的 VNode(虚拟节点)树,通过这些 VNode,Vue 可以高效地进行 DOM 更新。

总的来说,Vue 的模板编译器会把模板解析成 AST,然后再把 AST 转换成渲染函数,最终用渲染函数来生成 VNode 树,最后通过比较新旧 VNode 树,进行高效地 DOM 更新。这个过程中,模板编译器扮演了一个非常重要的角色,它是实现 Vue 响应式渲染的关键所在。

2. react 的 setState是同步还是异步

这题是个陷阱啊

在 React 中,setState 方法可能是同步的也可能是异步的,这取决于执行上下文和更新队列的状态。

当在 React 事件处理程序中调用 setState 方法时,React 会在下一次渲染之前,将更新加入到更新队列中,并异步更新组件。这是为了优化性能,避免在一次事件处理程序中多次更新组件。因此,在大多数情况下,setState 方法是异步的。

但是,在一些特定的情况下,setState 方法是同步的,例如在生命周期方法和回调函数中调用 setState 方法时。这是因为在这些情况下,React 已经开始进行组件的更新,并且需要立即更新组件的状态,以便后续的更新能够正确地进行。

需要注意的是,无论 setState 方法是同步还是异步,它都是基于浅合并的方式来更新组件的状态。也就是说,setState 方法只会更新指定的属性,并将其与组件的当前状态进行浅合并,而不会覆盖组件的整个状态。

3.Vue的nextTick

在Vue中当我们修改了组建的状态或者属性后,组件并不会立刻进行视图更新,而是会在下一个事件循环中更新,这是因为Vue的更新过程是异步的,可以减少不必要的重复计算,提高渲染性能

但是在一些特定情况下,我们必须要在更新组件状态后立刻获取更新后的DOM,这时候就需要用到nextTick方法

nextTick方法可以接受一个回调函数作为参数,这个回调函数会在下一个事件循环中被执行,并且能够获取到更新后的DOM并且进行操作。除了传递回调函数之外,nextTick 方法还可以返回一个 Promise 对象,可以使用 await 关键字等方式等待 nextTick 执行完成

接下来是两场很水的面试

滴滴

滴滴是找人内推,当天晚上面完之后就约了二面

这是完整的问题:

1.自我介绍

2.Vue和React的区别

3.Vue2和Vue3的区别

4.React有哪些常用的Hooks

5.Vue和React的状态管理工具怎么理解,说下Pinia

6.问我们这主要是写业务,问我抵触不抵触

7.问我的职业规划

8.问我的Node怎么样

9.做了一道算法题,给一个字符串date,判断这是一年的第几天

面试官给我的感觉是要抓壮丁,切图仔,因为给我的感觉不好,而且决定签轻流了,就拒掉了

VIVO

这是完整的问题:

1.上来让我自我介绍

2.然后介绍项目

3.怎么制定前端规范

4.用过element没有 用过哪些组件

5.你实习想得到点啥

6.目前有什么职业规划

我说我想深入Node,React,听面试官的预期估计觉得我要跑

7.介不介意我们技术栈老(vue2),实习生老跑,能不能待够6个月,不到6个月不发实习证明

8.反问 问面评

他说能满足他的要求,后续等HR通知,然后拒掉了

面试总结

这是我的第一波面试,面试下来也引发了我的一些思考。

之前有想过,是等八股都准备好了再去面试还是一边面试一边学习。但是这轮面试中我发现,一场面试学到的东西比我坐在那里背一上午面试题要多得多,面试官的一两句话能让你的思路豁然开朗,而且在面试中记忆的东西往往记忆深刻,比如说京东的面试官因为我对闭包的概念理解有一些问题,他当场给我讲了一遍,我到现在还能清楚的记着他给我讲的内容。除了技术上的,在沟通上也能学到很多东西,学着怎么去和面试官沟通,怎么和HR沟通,面试的流程,整个招聘环节的流程,当你提前熟悉了这些内容的时候,到了真正找工作的时候才不会两眼一抹黑啥也不会。总之这一轮的面试让我收获颇多。

还有一个问题,对于我来说现在真的要盲目选择进大厂吗?

以我目前的水平,大厂招我过去只会让我当切图仔,需要一些技术含量的岗位我面试还过不了,很尴尬的局面。大厂Title VS小厂核心 怎么选?

最后我选择了清流,选择他有这么几个原因:

  1. 待遇还不错,一个月5000包住,配Mac,有一些日常小福利,双休也不加班,比某些大厂待遇还好些
  2. 整个面试下来感觉很好,没有刁难为难,面试官和HR小姐姐都很尊重人,跟HR沟通也发现公司气氛很融洽,还有猫,
  3. 这家公司专注于一个业务,做零代码的,属于技术为主导的公司,有一些大厂来的大牛。而且HR给我许诺公司对于实习生不做限制,完全能让我接触到零代码核心还有组件库,这点让我很动心,感觉去了能学到东西
  4. 有了这段日常实习经历之后,找一份大厂的非切图的暑假实习可能会更容易一些

这个月底就要去入职了,对于职场还是一张白纸的我既有憧憬,也有担忧。那也没法,谁叫自己选择了这条路呢,只能不断学习,不断努力,最后希望未来一切安好!希望我的第一次实习一切顺利!

#我的实习求职记录##我的求职思考##前端##我的实习日记##前端中小厂##前端实习面试#
 类似资料: