# 【周刊-1】三年大厂面试官-面试题精选及答案
## 前言
在阿里和腾讯工作了6年,当了3年的前端面试官,把期间我和我的同事常问的面试题和答案汇总在我 Github 的 Weekly-FE-Interview 中。希望对大家有所帮助。
如果你在bat面试的时候遇到了什么不懂的问题,欢迎给我提issue,我会把题目汇总并将面试要点和答案写好放在周刊里,大家一起共同进步和成长,助力大家进入自己理想的企业。
项目地址是:[github.com/airuikun/We…](https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Fairuikun%2FWeekly-FE-Interview)
## 常见面试题精选
以下是十道大厂一面的时候常见的面试题,如果全部理解并且弄透,在一面或者电话面的时候基本上能中1~2题。小伙伴可以先不急着看答案,先自己尝试着思考一下和自己实现一下,然后再看答案。
#### 第 1 题:http的状态码中,499是什么?如何出现499,如何排查跟解决
499对应的是 “client has closed connection”,客户端请求等待链接已经关闭,这很有可能是因为服务器端处理的时间过长,客户端等得“不耐烦”了。还有一种原因是两次提交post过快就会出现499。 解决方法:
* 前端将timeout最大等待时间设置大一些
* nginx上配置proxy\_ignore\_client\_abort on;
提问解答与更多解析:[github.com/airuikun/We…](https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Fairuikun%2FWeekly-FE-Interview%2Fissues%2F1)
#### 第 2 题:如何遍历一个dom树
~~~
function traversal(node) {
//对node的处理
if (node && node.nodeType === 1) {
console.log(node.tagName);
}
var i = 0,
childNodes = node.childNodes,
item;
for (; i < childNodes.length; i++) {
item = childNodes[i];
if (item.nodeType === 1) {
//递归先序遍历子节点
traversal(item);
}
}
}
复制代码
~~~
提问解答与更多解析:[github.com/airuikun/We…](https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Fairuikun%2FWeekly-FE-Interview%2Fissues%2F4)
#### 第 3 题:new操作符都做了什么
四大步骤:
1、创建一个空对象,并且 this 变量引用该对象,// lat target = {};
2、继承了函数的原型。// target.**proto**\= func.prototype;
3、属性和方法被加入到 this 引用的对象中。并执行了该函数func// func.call(target);
4、新创建的对象由 this 所引用,并且最后隐式的返回 this 。// 如果func.call(target)返回的res是个对象或者function 就返回它
~~~
function new(func) {
lat target = {};
target.__proto__ = func.prototype;
let res = func.call(target);
if (typeof(res) == "object" || typeof(res) == "function") {
return res;
}
return target;
}
复制代码
~~~
提问解答与更多解析:[github.com/airuikun/We…](https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Fairuikun%2FWeekly-FE-Interview%2Fissues%2F5)
#### 第 4 题:手写代码,简单实现call
~~~
Function.prototype.call2 = function(context) {
var context = context || window; //因为传进来的context有可能是null
context.fn = this;
var args = [];
for (var i = 1; i < arguments.length; i++) {
args.push("arguments[" + i + "]"); //不这么做的话 字符串的引号会被自动去掉 变成了变量 导致报错
}
args = args.join(",");
var result = eval("context.fn(" + args + ")"); //相当于执行了context.fn(arguments[1], arguments[2]);
delete context.fn;
return result; //因为有可能this函数会有返回值return
}
复制代码
~~~
提问解答与更多解析:[github.com/airuikun/We…](https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Fairuikun%2FWeekly-FE-Interview%2Fissues%2F6)
#### 第 5 题:手写代码,简单实现apply
~~~
Function.prototype.apply2 = function(context, arr) {
var context = context || window; //因为传进来的context有可能是null
context.fn = this;
var args = [];
var params = arr || [];
for (var i = 0; i < params.length; i++) {
args.push("params[" + i + "]"); //不这么做的话 字符串的引号会被自动去掉 变成了变量 导致报错
}
args = args.join(",");
var result = eval("context.fn(" + args + ")"); //相当于执行了context.fn(arguments[1], arguments[2]);
delete context.fn;
return result; //因为有可能this函数会有返回值return
}
复制代码
~~~
提问解答与更多解析:[github.com/airuikun/We…](https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Fairuikun%2FWeekly-FE-Interview%2Fissues%2F7)
#### 第 6 题:手写代码,简单实现bind
~~~
Function.prototype.bind2 = function(context) {
var _this = this;
var argsParent = Array.prototype.slice(arguments, 1);
return function() {
var args = argsParent.concat(Array.prototype.slice(arguments)); //转化成数组
_this.apply(context, args);
};
}
复制代码
~~~
提问解答与更多解析:[github.com/airuikun/We…](https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Fairuikun%2FWeekly-FE-Interview%2Fissues%2F8)
#### 第 7 题:讲解一下HTTPS的工作原理
HTTPS在传输数据之前需要客户端(浏览器)与服务端(网站)之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息。TLS/SSL协议不仅仅是一套加密传输的协议,更是一件经过艺术家精心设计的艺术品,TLS/SSL中使用了非对称加密,对称加密以及HASH算法。握手过程的简单描述如下:
* 浏览器将自己支持的一套加密规则发送给网站。
* 网站从中选出一组加密算法与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。
* 获得网站证书之后浏览器要做以下工作:
* a) 验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等),如果证书受信任,则浏览器栏里面会显示一个小锁头,否则会给出证书不受信的提示。
* 如果证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。
* 使用约定好的HASH计算握手消息,并使用生成的随机数对消息进行加密,最后将之前生成的所有信息发送给网站。
* 网站接收浏览器发来的数据之后要做以下的操作:
* a) 使用自己的私钥将信息解密取出密码,使用密码解密浏览器发来的握手消息,并验证HASH是否与浏览器发来的一致。
* b) 使用密码加密一段握手消息,发送给浏览器。
* 浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密。
提问解答与更多解析:[github.com/airuikun/We…](https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Fairuikun%2FWeekly-FE-Interview%2Fissues%2F2)
#### 第 8 题:讲解一下https对称加密和非对称加密。
对称加密: 发送方和接收方需要持有同一把密钥,发送消息和接收消息均使用该密钥。相对于非对称加密,对称加密具有更高的加解密速度,但双方都需要事先知道密钥,密钥在传输过程中可能会被窃取,因此安全性没有非对称加密高。
非对称加密: 接收方在发送消息前需要事先生成公钥和私钥,然后将公钥发送给发送方。发送放收到公钥后,将待发送数据用公钥加密,发送给接收方。接收到收到数据后,用私钥解密。 在这个过程中,公钥负责加密,私钥负责解密,数据在传输过程中即使被截获,攻击者由于没有私钥,因此也无法破解。 非对称加密算法的加解密速度低于对称加密算法,但是安全性更高。
提问解答与更多解析:[github.com/airuikun/We…](https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Fairuikun%2FWeekly-FE-Interview%2Fissues%2F3)
#### 第 9 题: 简单实现项目代码按需加载,例如import { Button } from 'antd',打包的时候只打包button
原理很简单,就是将
~~~
import { Select, Pagination, Button } from 'xxx-ui';
复制代码
~~~
通过babel转化成
~~~
import Button from `xxx-ui/src/components/ui-base/Button/Button`;
import Pagination from `xxx-ui/src/components/ui-base/Pagination/Pagination`;
import Select from `xxx-ui/src/components/ui-base/Select/Select`;
复制代码
~~~
自定义拓展一个babel插件,代码如下:
~~~
visitor: {
ImportDeclaration (path, { opts }) {
const specifiers = path.node.specifiers;
const source = path.node.source;
// 判断传入的配置参数是否是数组形式
if (Array.isArray(opts)) {
opts.forEach(opt => {
assert(opt.libraryName, 'libraryName should be provided');
});
if (!opts.find(opt => opt.libraryName === source.value)) return;
} else {
assert(opts.libraryName, 'libraryName should be provided');
if (opts.libraryName !== source.value) return;
}
const opt = Array.isArray(opts) ? opts.find(opt => opt.libraryName === source.value) : opts;
opt.camel2UnderlineComponentName = typeof opt.camel2UnderlineComponentName === 'undefined'
? false
: opt.camel2UnderlineComponentName;
opt.camel2DashComponentName = typeof opt.camel2DashComponentName === 'undefined'
? false
: opt.camel2DashComponentName;
if (!types.isImportDefaultSpecifier(specifiers[0]) && !types.isImportNamespaceSpecifier(specifiers[0])) {
// 遍历specifiers生成转换后的ImportDeclaration节点数组
const declarations = specifiers.map((specifier) => {
// 转换组件名称
const transformedSourceName = opt.camel2UnderlineComponentName
? camel2Underline(specifier.imported.name)
: opt.camel2DashComponentName
? camel2Dash(specifier.imported.name)
: specifier.imported.name;
// 利用自定义的customSourceFunc生成绝对路径,然后创建新的ImportDeclaration节点
return types.ImportDeclaration([types.ImportDefaultSpecifier(specifier.local)],
types.StringLiteral(opt.customSourceFunc(transformedSourceName)));
});
// 将当前节点替换成新建的ImportDeclaration节点组
path.replaceWithMultiple(declarations);
}
}
}
复制代码
~~~
提问解答与更多解析:[github.com/airuikun/We…](https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Fairuikun%2FWeekly-FE-Interview%2Fissues%2F9)
#### 第 10 题:简单手写实现promise
~~~
// 简易版本的promise
// 第一步: 列出三大块 this.then resolve/reject fn(resolve,reject)
// 第二步: this.then负责注册所有的函数 resolve/reject负责执行所有的函数
// 第三步: 在resolve/reject里面要加上setTimeout 防止还没进行then注册 就直接执行resolve了
// 第四步: resolve/reject里面要返回this 这样就可以链式调用了
// 第五步: 三个状态的管理 pending fulfilled rejected
// *****promise的链式调用 在then里面return一个promise 这样才能then里面加上异步函数
// 加上了catch
function PromiseM(fn) {
var value = null;
var callbacks = [];
//加入状态 为了解决在Promise异步操作成功之后调用的then注册的回调不会执行的问题
var state = 'pending';
var _this = this;
//注册所有的回调函数
this.then = function (fulfilled, rejected) {
//如果想链式promise 那就要在这边return一个new Promise
return new Promise(function (resolv, rejec) {
//异常处理
try {
if (state == 'pending') {
callbacks.push(fulfilled);
//实现链式调用
return;
}
if (state == 'fulfilled') {
var data = fulfilled(value);
//为了能让两个promise连接起来
resolv(data);
return;
}
if (state == 'rejected') {
var data = rejected(value);
//为了能让两个promise连接起来
resolv(data);
return;
}
} catch (e) {
_this.catch(e);
}
});
}
//执行所有的回调函数
function resolve(valueNew) {
value = valueNew;
state = 'fulfilled';
execute();
}
//执行所有的回调函数
function reject(valueNew) {
value = valueNew;
state = 'rejected';
execute();
}
function execute() {
//加入延时机制 防止promise里面有同步函数 导致resolve先执行 then还没注册上函数
setTimeout(function () {
callbacks.forEach(function (cb) {
value = cb(value);
});
}, 0);
}
this.catch = function (e) {
console.log(JSON.stringify(e));
}
//经典 实现异步回调
fn(resolve, reject);
}
~~~
- 首页
- pm2
- pm2
- pm2 离线安装
- pm2 使用指南
- node
- 正则
- web
- webpack
- 配置
- 优化代码体积
- plugin-proposal-decorators
- webpack 打包原理解析
- babel presets配置 babel7
- 配置路径别名
- 去除开发中的警告信息
- css
- 滚动条
- input自动填充背景色
- 颜色渐变
- scss
- 网页定制光标
- 超出文本显示省略号。。。
- calc兼容性写法
- box-sizing
- clip-path
- 苹果手机页面滑动卡顿
- 字体间距根据父级宽度自适应
- 纯css动态效果
- 清除浮动的三种方法
- 按钮增加闪烁效果
- 字体渐变
- react
- mobx
- 路由
- antd 表格在safari上卡顿
- 项目初始化
- react-antd-mobx-momnet
- 显示字符串中的标签
- antd Select 在搜索精准度
- 路由切换动态过渡效果
- css中图片打包后的路径出错
- antd upload 无法及时更新state
- antd DatePicker设置中文失败
- antd-pro 添加登录页面报错
- new Array创建新数组数据指向相同
- react 页面刷新渲染两次
- useEffect
- Hooks 闭包解决方案
- hooks 方法封装
- Plugin "react" was conflicted between "package.json » eslint-config-react-app
- javascript
- canvas
- 多张图片合成一张
- 排序
- js比较符号==、===
- 运动函数封装(简易、通用)
- 导出表格(excel )
- react使用demo
- xlsx导出excel
- js获取屏幕高度宽度
- toFixed 函数修改
- 获取cookie,url参数
- 奇怪的错误问题
- copy(深拷贝 浅拷贝)
- 导出pdf
- 解决图片失真
- 判断字符串长度(带中文)
- js中 文件、图片二进制和base64的互转
- 读取深度嵌套的json数据
- 手动实现Promise.all
- cookie 删除
- webpack 打包过后的文件报错 regeneratorRuntime is not defined
- 防抖与节流
- react hooks 中使用防抖节流
- 图片懒加载
- 重排和重绘
- 修复部分无法JASON.parse的数据
- react-native
- android-studio 打开调试工具
- 适配全面屏
- node
- 服务端 node + nginx 反向代理
- 生成文件夹目录列表
- mogodb常用操作
- 发布npm包
- cli工具
- 上传文件
- nodejs使用crypto进行加密/解密操作
- mongodb 加入验证之后连接失败
- nextjs使用问题
- node转发http请求
- mongodb 导入导出 备份
- node-sass 安装问题、安装失败等
- npm yarn 安装依赖太慢
- puppeteer 安装问题 centos
- mongoose
- 其他
- 禁止浏览器缓存
- chrome平滑滚动
- pdf预览
- 问题整理
- 资料
- 小程序
- fetch
- cookie 设置跨域资源共享
- taro 小程序
- taro request
- 设置npm镜像
- esbuild the service is no longer running
- 离线地图
- uniapp 转 vue-cli
- 工具
- Excel表格密码保护的解除方法
- vscode(插件)
- vscode 常用代码片段
- vscode 开启tab补全代码
- mac 百度网盘破解
- mysql 重置密码
- chrome 好用的扩展
- Mac/Linux/Windows通过命令调用浏览器打开某网页
- 小链接
- 数据库
- mongo
- sql文件导入
- join 用法
- sql 时间格式化 DATE_FORMAT
- 创建全文检索并分词查询
- 阿里云node-mysql 操作文档
- sql 时间查询
- mysql group查询结果合并为一行
- mysql 锁
- mysql count 同个字段多个结果合并到一行
- 解决Node.js mysql客户端不支持认证协议引发的“ER_NOT_SUPPORTED_AUTH_MODE”问题
- mysql 根据经纬度计算距离
- PHP
- 文件读取
- 接收前端json数据
- 自定义排序
- session 写入失败无法保存
- php 上传大文件$_FILES为空
- base64转图片
- composer.phar 安装东西太慢 切换国内镜像
- laravel sql查询记录
- 解决: Please provide a valid cache path.
- thinkphp开启多应用
- 上传文件报错 Filename cannot be empty
- php curl 报错 curl: (35) SSL connect error
- App
- android未授权错误(Flutter)
- uniapp
- 服务端
- mongodb 定时备份
- mysql 错误
- nginx 转发网络请求
- midwayjs 使用egg-mysql
- https 无法访问
- egg 配置跨域
- 算法实现
- 排序
- 全排列
- 无重复字符的最长子串
- 反转单向链表
- 斐波那契数列
- 有效的括号
- GIT
- git克隆大文件
- 面试整理
- 前端整理
- 大厂高级前端面试题
- 三年大厂面试题
- 面试经验
- 头条it技术工程师
- 每日学习
- 常见的数据结构
- 面试地址汇总
- 练习汇总
- 前端八股文
- mac环境配置
- mac nginx重启报错
- mac 安装redis
- fis配置
- 切换php版本
- Mac OS X下的Oh-My-ZSH安装与配置
- mac 查看端口进程 停止进程
- mac 配置ssh 免密码登录服务器
- navigate 中文破解
- 删除启动台无效文件夹
- 删除顶部图标(卸载后的软件还存在)
- 修复mac 下安装全局依赖失效
- navicate 完美破解 内有下载地址
- nginx 报错 500 "/usr/local/var/run/nginx/client_body_temp/0000000004" failed (13: Permission denied)
- 安装PHP redis扩展
- 安装zsh后 nvm node命令失效
- python
- python 在vscode中编辑,格式化文件总是提示There is no Pip installer available in the selected environment.
- 杂项
- 膝盖修复
- 微信打开网页链接反应巨慢
- chrome 显示http/https完整连接
- doracms
- pdfjs 中文无法显示
- docker
- go
- 指针、指针地址* &
- 脚本
- 京东疯狂的joy脚本
- 2021京东炸年兽
- LINUX
