Skip to content

Latest commit

 

History

History
1507 lines (867 loc) · 67.7 KB

霖呆呆的近期中大厂面试汇总(含超详细答案).md

File metadata and controls

1507 lines (867 loc) · 67.7 KB

YY

5月14日

面试部门:广州某部门

一面

面试形式:牛客网视频面试

面试时长:50分钟

面试感悟:YY和CVTE都是托树酱小哥哥帮忙找的朋友内推,另外YY算是呆呆第一个面的比较大的厂了吧,也是第一次在牛客网上视频面试,所以还是有些激动的。面试官稍微晚到了2分钟,不过问题不大。呆呆一般在面试前喜欢听些自己喜欢的歌,算是能让自己放松下来吧。整个面试过程也并没有之前以为的那么压抑,虽然面试官问的问题基本都回答上来了,但是面试官并没有告诉我正确与否。

说一下你们项目动态表单的实现

说一下动态表格的实现

动态表格由两部分组成:

  • 一个是上面的索引内容(搜索条件),索引内容因为和动态表单很像,又有输入框,又有下拉框,所以上面的索引我也是基于动态表单UI组件库进行开发的
  • 二是下面具体的表格查询的内容,这个是基于Ant Design of Vue中的Table进行封装的,它的结构其实很简单,只要传入需要的表头字段列表,也就是columns,还有具体的数据进去就能显示出表格。不过这里因为表头字段的数量不确定,所以可能需要用到Vue的计算属性来进行一个计算,例如如果表头字段的数量是小于8的话则表格的x方向不需要滚动,否则整个x方向的长度就为数量 X 105px类似这样的值。以及还有涉及到是否分页,每个字段的排序呀等操作...

你们这个JSON树是在哪里生成的,前端会生成吗?

盘石中配置的,后台生成。

有了解过微前端吗?

我们后台有用到微服务,但是前端这边没有用到。

了解浏览器的哪些兼容问题

(兼容性这一块呆呆做的不多,参考:https://blog.csdn.net/wanmeiyinyue315/article/details/79654984)

1、不同浏览器的标签默认的外补丁( margin )和内补丁(padding)不同 解决方案: css 里增加通配符 * { margin: 0; padding: 0; }

2、IE6双边距问题;在 IE6中设置了float , 同时又设置margin , 就会出现边距问题 解决方案:设置display:inline;

3、当标签的高度设置小于10px,在IE6、IE7中会超出自己设置的高度 解决方案:超出高度的标签设置overflow:hidden,或者设置line-height的值小于你的设置高度

4、图片默认有间距 解决方案:使用float 为img 布局

5、IE9以下下浏览器不能使用opacity 解决方案:

div {
  opacity: 0.5;
  filter: alpha(opacity = 50);
  filter: progid:DXImageTransform.Microsoft.Alpha(style = 0, opacity = 50);
}

6、边距重叠问题;当相邻两个元素都设置了margin 边距时,margin 将取最大值,舍弃最小值; 解决方案:为了不让边重叠,可以给子元素增加一个父级元素,并设置父级元素为overflow:hidden;

7、cursor:hand 显示手型在safari 上不支持 解决方案:统一使用 cursor:pointer

8、两个块级元素,父元素设置了overflow:auto;子元素设置了position:relative ;且高度大于父元素,在IE6、IE7会被隐藏而不是溢出 解决方案:父级元素设置position:relative

IE有哪些兼容性问题

同上

说一下EventLoop

必问...

这个在上篇面试文章中已经总结过喽,可以戳这里👇:

霖呆呆的近期面试128题汇总(含超详细答案) | 掘金技术征文

在项目里有用过node吗

之前自己工作之余有玩过一些,但是没有实际运用在工作中。

如何实现一个findIndex

针对于这种要我们口述实现的问题,大家可以从这么几个方面去回答:

  • 需要实现的API的功能点
  • API的参数及返回值
  • 是否需要挂载在某个原型对象上(例如Array.prototype),或者就是一个单独的函数(例如实现new)
  • 具体的实现方式

根据MDN上的要求,呆呆这里贴一个简易版的实现方式。

需要满足的条件:

  1. findIndex方法对数组中的每个数组索引0..length-1(包括)执行一次callback函数,直到找到一个callback函数返回真实值(强制为true)的值。
  2. 如果找到这样的元素,findIndex会立即返回该元素的索引。
  3. 如果回调从不返回真值,或者数组的length为0,则findIndex返回-1。
  4. 回调函数调用时有三个参数:元素的值,元素的索引,以及被遍历的数组。
  5. 如果一个 thisArg 参数被提供给 findIndex, 它将会被当作this使用在每次回调函数被调用的时候。如果没有被提供,将会使用undefined
  6. findIndex不会修改所调用的数组。

Coding:

Array.prototype.myFindIndex = function (cb, context = undefined) {
  if (typeof cb !== 'function') {
    throw new TypeError('cb must be a function');
  }
  var arr = [].slice.call(this);
  var len = arr.length >>> 0;
  let i = 0;
  while (i < len) {
    if (cb.call(context, arr[i], i, arr)) {
      return i;
    }
    i++;
  }
  return -1;
}
function isEven (num) {
  return num % 2 === 0;
}
console.log([3, 4, 5].myFindIndex(isEven)) // 1

具体的Polyfill可以看MDN上:Array.prototype.findIndex()

移动端布局有哪些方案?

  • flex + rem
  • vw + px

...

如果一个移动端的项目要显示在PC端上保证结构稳定你会如何做?

这个没有唯一的答案,还希望有大佬能够补充一下,后期呆呆更新到文章中来。

这边有的一个方案是:

首先保证在移动端上显示正常,然后设置一个最大的max-width来限定死。

你自己的项目里有做过哪些webpack上的优化

参考文章: https://juejin.im/post/5ea528496fb9a03c576cceac#heading-2

  • 先使用webpack-bundle-analyzer分析打包后整个项目中的体积结构,既可以看到项目中用到的所有第三方包,又能看到各个模块在整个项目中的占比。

  • Vue路由懒加载,使用() => import(xxx.vue)形式,打包会根据路由自动拆分打包。

  • 第三方库按需加载,例如lodash库、UI组件库

  • 使用purgecss-webpack-pluginglob插件去除无用样式(glob插件可以可以同步查找目录下的任意文件夹下的任意文件):

    new PurgecssWebpackPlugin({
        // paths表示指定要去解析的文件名数组路径
        // Purgecss会去解析这些文件然后把无用的样式移除
        paths: glob.sync('./src/**/*', {nodir: true})
        // glob.sync同步查找src目录下的任意文件夹下的任意文件
        // 返回一个数组,如['真实路径/src/css/style.css','真实路径/src/index.js',...]
    })
  • 文件解析优化:

    • babel-loader编译慢,可以通过配置exclude来去除一些不需要编译的文件夹,还可以通过设置cacheDirectory开启缓存,转译的结果会被缓存到文件系统中
    • 文件解析优化:通过配置resolve选项中的aliasalias创建import或者require的别名,加快webpack查找速度。
  • 使用webpack自带插件IgnorePlugin忽略moment目录下的locale文件夹使打包后体积减少278k

    • 问题原因:使用moment时发现会把整个locale语言包都打包进去导致打包体积过大,但是我们只需要用到中文包

    • webpack配置中使用webpack自带的插件IgnorePlugin忽略moment目录下的locale文件夹

    • 之后在项目中引入:

      // index.js
      // 利用IgnorePlugin把只需要的语言包导入使用就可以了,省去了一下子打包整个语言包
      import moment from 'moment';
      // 单独导入中文语言包
      import 'moment/locale/zh-cn';
    • (或者不用这种方式,直接使用更加轻量的Day.js也可以)

  • 使用splitChunks进行拆包,抽离公共模块,一些常用配置项:

  • chunks:表示选择哪些 chunks 进行分割,可选值有:async,initial和all

    • minSize: 表示新分离出的chunk必须大于等于minSize,默认为30000,约30kb
    • minChunks: 表示一个模块至少应被minChunks个chunk所包含才能分割,默认为1
    • name: 设置chunk的文件名
    • cacheGroups: 可以配置多个组,每个组根据test设置条件,符合test条件的模块,就分配到该组。模块可以被多个组引用,但最终会根据priority来决定打包到哪个组中。默认将所有来自 node_modules目录的模块打包至vendors组,将两个以上的chunk所共享的模块打包至default组。
  • DllPlugin动态链接库,将第三方库的代码和业务代码抽离:

    • 根目录下创建一个webpack.dll.js文件用来打包出dll文件。并在package.json中配置dll指令生成dll文件夹,里面就会有manifest.json以及生成的xxx.dll.js文件
    • 将打包的dll通过add-asset-html-webpack-plugin添加到html中,再通过DllReferencePlugin把dll引用到需要编译的依赖。

具体说一下splitChunksPlugin

有自己写过webpack插件吗

写过一些简单的插件。

一、File-list-plugin:

作用:在每次打包成功之后都会在最终的输出目录下生成一个fileList.md文件,记录了输出目录的所有文件信息。

实现原理:

  • 定义一个名为FileListPlugin的构造函数,重写它原型对象上的apply方法。

  • 在此方法中触发compiler'emit'钩子,也就是生成资源到output目录之前

  • 然后因为此钩子的回调函数中有一个compilation对象,所以可以通过compilation.assets获取到所有待生成的文件

  • 在构建一下md里的内容,也就是把将每一项文件的名称写入markdown文件内

  • 最后通过以下方式把markdown文件放到要输出的目录下:

    compilation.assets[fileListName] = {
    	source: function () {
    	  return content;
    	},
    	size: function () {
    	  return content.length;
    	}
    }

完整代码:

File-list-plugin.js:

function FileListPlugin (options) {
  this.options = options || {};
  this.filename = this.options.filename || 'fileList.md'
}

FileListPlugin.prototype.apply = function (compiler) {
  // 1.
  compiler.hooks.emit.tapAsync('FileListPlugin', (compilation, cb) => {
    // 2.
    const fileListName = this.filename;
    // 3.
    let len = Object.keys(compilation.assets).length;
    // 4.
    let content = `# 一共有${len}个文件\n\n`;
    // 5.
    for (let filename in compilation.assets) {
      content += `- ${filename}\n`
    }
    // 6.
    compilation.assets[fileListName] = {
      // 7.
      source: function () {
        return content;
      },
      // 8.
      size: function () {
        return content.length;
      }
    }
    // 9.
    cb();
  })
}
module.exports = FileListPlugin;

二、Watch-plugin

作用:观察者模式下,当每次资源变动了,将改动资源的个数以及改动资源的列表输出到控制台中

Watch-plugin.js:

function WatcherPlugin (options) {
  this.options = options || {};
}

WatcherPlugin.prototype.apply = function (compiler) {
  compiler.hooks.watchRun.tapAsync('WatcherPlugin', (compiler, cb) => {
    console.log('我可是时刻监听着的 🚀🚀🚀')
    let mtimes = compiler.watchFileSystem.watcher.mtimes;
    let mtimesKeys = Object.keys(mtimes);
    if (mtimesKeys.length > 0) {
      console.log(`本次一共改动了${mtimesKeys.length}个文件,目录为:`)
      console.log(mtimesKeys)
      console.log('------------分割线-------------')
    }
    cb()
  })
  compiler.hooks.watchClose.tap('WatcherPlugin', () => {
    console.log('本次监听停止了哟~👋👋👋')
  })
}
module.exports = WatcherPlugin;

三、Clean-plugin

作用:每次打包之后,生成了新的文件,则需要把以前的文件给清理掉。

Clean-plugin.js:

const recursiveReadSync = require("recursive-readdir-sync");
const minimatch = require("minimatch");
const path = require("path");
const fs = require("fs");
const union = require("lodash.union");
function CleanPlugin (options) {
  this.options = options;
}
// 匹配文件
function getUnmatchFiles(fromPath, exclude = []) {
  const unmatchFiles = recursiveReadSync(fromPath).filter(file =>
    exclude.every(
      excluded => {
        return !minimatch(path.relative(fromPath, file), path.join(excluded), {
          dot: true
        })
      }
    )
  );
  return unmatchFiles;
}
CleanPlugin.prototype.apply = function (compiler) {
  const outputPath = compiler.options.output.path;
  compiler.hooks.done.tap('CleanPlugin', stats => {
    if (compiler.outputFileSystem.constructor.name !== "NodeOutputFileSystem") {
      return;
    }
    const assets = stats.toJson().assets.map(asset => asset.name);
    // 多数组合并并且去重
    const newAssets = union(this.options.exclude, assets);
    // 获取未匹配文件
    const unmatchFiles = getUnmatchFiles(outputPath, newAssets);
    // 删除未匹配文件
    unmatchFiles.forEach(fs.unlinkSync);
  })
}

module.exports = CleanPlugin;

具体可以看我的这篇文章:霖呆呆的六个自定义Webpack插件详解-自定义plugin篇(3)

HTTP3.0.1和HTTP3.0.2的区别

说一下Vue-Router的实现原理

Vue-Router初始化是发生在什么时候

你觉得我还有哪些问题没有问到的,也就是你可以说一下你的加分项

CVTE

5月14日

面试部门:希沃未来教育某部门(广州)

一面

面试形式:电话面

面试时常:1小时6分钟

面试感悟:面试官人很和善,基本全程都在"嗯",也会和你开玩笑,然后我答不出来的时候他也会说:"嗯,没关系,问题不大"。不过可能是因为自己在某些问题上回答的太繁重了,导致经常被面试官打断(大概有2,3次吧),所以大家在面试的时候也要抓重点回答不要偏离问题的方向。

离职原因

新的一份工作你主要看中的是什么?

平台、团队、薪资、地域

先说一下你的期望薪资吧

你为什么觉得你这次跳槽能有这么大的工资涨幅

我是从这几个角度来分析的:

  • 自己离职前本来应该还有一次加薪机会的,但是走了,所以我认为这次跳槽的薪资幅度也不算高
  • 对比于相同工作年限的同行来说在技术上可能会稍微强一些
  • 自己的社区影响力,喜欢写博客

如果给不到期望薪资你会怎么办

薪资只是我找工作的其中一个比较项,回到我最原始的想法,我更在意的是平台和团队。

说一下你们项目动态表单的实现

你遍历JSON树的具体过程

webpack构建流程

(当时是按欧巴的总结答的:「吐血整理」再来一打Webpack面试题(持续更新))

初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数

开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译

确定入口:根据配置中的 entry 找出所有的入口文件

编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理

完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系

输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会

输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统

webpack插件原理

webpack 在编译代码过程中,会触发一系列 Tapable 钩子事件,插件所做的,就是找到相应的钩子,往上面挂上自己的任务,也就是注册事件,这样,当 webpack 构建的时候,插件注册的事件就会随着钩子的触发而执行了。

具体可以看我的这篇文章:霖呆呆的六个自定义Webpack插件详解-自定义plugin篇(3)

webpack在配置插件的时候是一个数组那它有顺序吗

当时我回答的是:这个可能要从webpack插件设计者的角度来看吧,因为webpack插件它是可以指定在哪个生命周期来做某些处理的,所以我认为它主要是和设计时的调用时机有关,而和定义时数组里的顺序无关;但是loader我知道它是有执行顺序的,一般是从右到左,从下到上,但是也可以用一个叫做enforce的属性去改变执行顺序。

有用过哪些插件做项目的分析吗

当时答的:

webpack-dashboard:可以更友好的展示相关打包信息。

speed-measure-webpack-plugin:简称 SMP,分析出 Webpack 打包过程中 Loader 和 Plugin 的耗时,有助于找到构建过程中的性能瓶颈。

size-plugin:监控资源体积变化,尽早发现问题

webpack-bundle-analyzer:分析打包后整个项目中的体积结构,既可以看到项目中用到的所有第三方包,又能看到各个模块在整个项目中的占比。

有做过哪些webpack优化吗

当时答的:

  1. 使用purgecss-webpack-pluginglob插件去除无用样式(glob插件可以可以同步查找目录下的任意文件夹下的任意文件):
new PurgecssWebpackPlugin({
    // paths表示指定要去解析的文件名数组路径
    // Purgecss会去解析这些文件然后把无用的样式移除
    paths: glob.sync('./src/**/*', {nodir: true})
    // glob.sync同步查找src目录下的任意文件夹下的任意文件
    // 返回一个数组,如['真实路径/src/css/style.css','真实路径/src/index.js',...]
})
  1. 文件解析优化:
  • babel-loader编译慢,可以通过配置exclude来去除一些不需要编译的文件夹,还可以通过设置cacheDirectory开启缓存,转译的结果会被缓存到文件系统中
  • 文件解析优化:通过配置resolve选项中的aliasextensionsmodules来实现。alias创建import或者require的别名;加快webpack查找速度。extensions自动解析确定的扩展;modules解析模块时应该搜索的目录,通常建议使用绝对路径,避免层层查找祖先目录。
  1. 还有的话,从webpack-merge提取一些公共的配置项

有自己写过插件吗

必问...

让你从零开始构建一个webpack项目你可以吗

实不相瞒的告诉您,我之前有写过webpack相关的这一部分教程,包括从零开始搭建,然后再到基本的loader的使用,比如style-loadercss-loaderraw-loader等,然后到一些基本的插件的使用,比如html-webpack-pluginclean-webpack-plugin,以及像webpack-dev-server...

面试官:"好了好了,我大概清楚了,OK,这个问题我们过吧"

从输入URL到页面呈现的过程

必问...

为什么TCP要三次握手而不是两次

(这个大家应该都清楚)

为了保证双方都有发送和接收的能力,如果只有两次握手的话就只能保证发送方,一般就是我们的客户端有发送的能力,但是并不能保证它有接收的能力,所以必须是三次。

HTTP和TCP的区别

当时的答案:

HTTP的责任是去定义数据,在两台计算机相互传递信息时,HTTP规定了每段数据以什么形式表达才是能够被另外一台计算机理解。

而TCP所要规定的是数据应该怎么传输才能稳定且高效的传递与计算机之间。

你们项目一般是如何做缓存的

当时答的很乱...就提到了一些Expires以及cache-control等等。

  • 对于一些没有指纹信息的资源,例如index.html可以使用Cache-Control: no-cache开启协商缓存
  • 对于带有指纹信息的资源,一般会使用splitChunksPlugin进行代码分割,来保证造成最小范围的缓存失效,再设置Cache-Control: max-age=3153600

(答的时候,由于想给自己争取更多的思考时间,就佯装咳嗽,面试官问你还好吧,要不要喝口水😂)

什么情况会阻塞页面的加载

提到了script加载的时候会阻塞页面。JS属于单线程,当我们在加载script标签内容的时候,渲染(GUI)线程会被暂停,因为script标签里可能会操作DOM的,所以如果你加载script标签又同时渲染页面肯定就冲突了,因此说渲染线程(GUI)和js引擎线程互斥。

然后我还想说一下引用CSS的时候使用@import也会,然后就被面试官打断了。

script放在body头部就一定会阻塞吗

没想出来,然后反问了他是不是不操作DOM就不会?

面试官:呵呵呵,没事,你等会去了解一下

但后面有了解到,面试官想问的可能是asyncdefer吧:

正常模式

这种情况下 JS 会阻塞浏览器,浏览器必须等待 index.js 加载和执行完毕才能去做其它事情。

<script src="index.js"></script>

async(异步) 模式

async 模式下,JS 不会阻塞浏览器做任何其它的事情。它的加载是异步的,当它加载结束,JS 脚本会立即执行。

<script async src="index.js"></script>

defer(延缓) 模式

defer 模式下,JS 的加载是异步的,执行是被推迟的。等整个文档解析完成、DOMContentLoaded 事件即将被触发时,被标记了 defer 的 JS 文件才会开始依次执行。

<script defer src="index.js"></script>

从应用的角度来说,一般当我们的脚本与 DOM 元素和其它脚本之间的依赖关系不强时,我们会选用 async;当脚本依赖于 DOM 元素和其它脚本的执行结果时,我们会选用 defer。

添加删除了DOM节点会发生什么?(回流)

把回流的过程说一下就可以了

js中改变transform的left和right对比于css修改transform

我的回答:css中使用transform会促发CPU加速,使浏览器不会进行回流和重绘。但是如果用js改变的话我不确定会不会,这样吧,我盲猜一下我感觉它会,因为我记得我看过说要避免频繁的使用style,不过不知道自己说的对不对。所以相对于用js调用的话后者的性能肯定就会更好一些。

面试官:呵呵呵,这个你下去在了解一下吧

什么是GPU加速

在使用CSS3中的transformopacityfilter这些属性的时候,能跳过布局和绘制流程,直接进入非主线处理的部分,即交给合成线程。

  • 合成线程中会调用线程池进行GPU加速,因为GPU是比较擅长处理位图数据的
  • 且它没有占有主线程

进程和线程的区别

进程:资源分配的最小单位

线程:资源调度的最小单位

面试官;"你是不是回答反了?算了算了,我们不绕了,这个问题就这样吧"

(上面这个是正确的,当时应该是答反了)

做过H5页面没有?你怎样适配移动端的

做的少,然后我提到了在程序初始化的时候可以使用以下方式来修改根节点的字体大小:

(假设你需要的1rem = 20px)

(function () {
  var html = document.documentElement;
  function onWindowResize() {
    html.style.fontSize = html.getBoundingClientRect().width / 20 + 'px';
  }
  window.addEventListener('resize', onWindowResize);
  onWindowResize();
})();
  • document.documentElement:获取document的根元素
  • html.getBoundingClientRect().width:获取html的宽度(窗口的宽度)
  • 监听windowresize事件

一般还需要配合一个meta头:

<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-sacle=1.0, maximum-scale=1.0, user-scalable=no" />

HTTP/2对比HTTP1.1

  • 使用HPACK算法进行头部压缩,HTTP/2 当中废除了起始行的概念,将起始行中的请求方法、URI、状态码转换成了头字段,不过这些字段都有一个":"前缀,用来和其它请求头区分开。
  • 使用二进制分帧解决了HTTP层面的队头阻塞,即多个请求都通过一个TCP连接并发地完成,实现多路复用。
  • 服务器推送。服务器不再是完全被动地接收请求,响应请求,它也能新建 stream 来给客户端发送消息
  • 新的二进制格式。HTTP/2采用二进制格式传输数据,相比于HTTP/1.1的文本格式,二进制格式具有更好的解析性和拓展性。

HTTP/2是怎么解决队头阻塞的

(当时只答出了第一段话)

HTTP/2引入了一个二进制分帧层,客户端和服务端进行传输时,数据会先经过二进制分帧层处理,转化为一个个带有请求ID的帧,这些帧在传输完成后根据ID组合成对应的数据。

所以原本一个个完整的HTTP请求报文变成了现在的一堆乱序的二进制帧,这些二进制帧因为不存在先后顺序所以也就不会排队等待,也就没有了HTTP队头阻塞的问题。

通信双方都可以给对方发送二进制帧,这种二进制帧的双向传输的序列,就叫做流。HTTP/2在一个TCP连接上来进行多个数据帧的通信,这就是多路复用的概念。

(所谓的乱序,指的是不同 ID 的 Stream 是乱序的,但同一个 Stream ID 的帧一定是按顺序传输的。)

HTTP/2是如何压缩头部的

(回答参考:https://juejin.im/post/5e76bd516fb9a07cce750746#heading-91)

主要是通过HPACK算法来实现压缩头部的功能,其主要有以下两个亮点:

  • 首先是在服务器和客户端之间建立哈希表,将用到的字段存放在这张表中,那么在传输的时候对于之前出现过的值,只需要把索引(比如0,1,2,...)传给对方即可,对方拿到索引查表就行了。这种传索引的方式,可以说让请求头字段得到极大程度的精简和复用。
  • 其次是对于整数和字符串进行哈夫曼编码,哈夫曼编码的原理就是先将所有出现的字符建立一张索引表,然后让出现次数多的字符对应的索引尽可能短,传输的时候也是传输这样的索引序列,可以达到非常高的压缩率。

为什么说HTTPS比HTTP安全呢

  • 通过混合加密保证传输的数据不被窃听
  • 通过数字签名的方式保证数据不会被篡改
  • 通过数字证书保证服务器身份的真实性

说一下对称加密和非对称加密

对称密钥加密是最简单的一种加密方式,它的加解密用的都是相同的密钥,这样带来的好处就是加解密效率很快,但是并不安全,如果有人拿到了这把密钥那谁都可以进行解密了。

而非对称密钥会有两把密钥,一把是私钥,只有自己才有;一把是公钥,可以发布给任何人。并且加密的内容只有相匹配的密钥才能解。这样带来的一个好处就是能保证传输的内容是安全的,因为例如如果是公钥加密的数据,就算是第三方截取了这个数据但是没有对应的私钥也破解不了。不过它也有缺点,一是公钥因为是公开的,谁都可以过去,如果内容是通过私钥加密的话,那拥有对应公钥的黑客就可以用这个公钥来进行解密得到里面的信息;二来公钥里并没有包含服务器的信息,也就是并不能确保服务器身份的合法性;并且非对称加密的时候要消耗一定的时间,减低了数据的传输效率。

HTTP请求的什么时候用的对称加密什么时候非对称加密

在建立连接的时候为了保证双方都能拿到对称密钥,所以需要使用非对称加密

而双方都拿到了这把对称密钥之后,就可以用这把对称密钥进行对称加密了。

对称加密的原理

(面试官提示万变不离其宗...可我还是悟不出来)

如果让你去实现一个CSRF攻击你会怎做?

当时答的只知道CSRF的攻击原理,但是不知道怎么去做...

(毕竟呆呆是一个品行良好的网民)

还知道其它的攻击方式吗?

Vue中key的作用

key的作用是尽可能的复用 DOM 元素。

新旧 children 中的节点只有顺序是不同的时候,最佳的操作应该是通过移动元素的位置来达到更新的目的。

需要在新旧 children 的节点中保存映射关系,以便能够在旧 children 的节点中找到可复用的节点。key也就是children中节点的唯一标识。

如果我将key设置为了一个Math.random()可以吗

这道题我开始没太理解面试官的意思,然后他提示了我:回到key设计的初衷。之后开着玩笑的说了一句:"我在给你点时间思考一下,我挺希望你能答上来的。"

我:对不起我可能要让你失望了,我确实没想出来

面试官:嗯...没关系问题不大哈,这个问题我们过吧

(不过在最后面试完之后我也有问面试官这个问题,他说:key它就是为了保证我们的DOM节点能够复用,如果你都把它设置为了一个随机数的话,那每次重写渲染都会是另一个数了根本不可能达到复用的效果)

😂...绝了,原来想问的是这个意思,是我学艺不精了。

如果让你设计一个双向绑定你会如何设计

我:"我有了解Vue的双向绑定原理,如果你想听的话我可以说给你听"

面试官:"不用,你就大致说一下就可以了,让我知道你确实是了解了"

我:"巴拉巴拉"

如何实现if(a===1&&a===2&&a===3)

由于双向绑定的时候提到了Object.defineProperty()属性,面试官可能就想到了这道题。

这里其实我应该反思一下自己...

在面试官还没说完题目的时候我就急着先说自己知道甚至还想问面试官是不是这道题,这样显然是不好的,可能也是和面试官的面试氛围太好了...放飞了自我吧。但还好面试官不和我斤斤计较。

是有看过这道题吗?这么快的就回答上来了

😂😂😂,老实回答,在自己的文章中有写到这道题,所以很快就知道你想考的是什么了。

之后面试官说:"嗯呢,我昨天也有看了一下你的文章,lv4,望尘莫及..."

我:哈哈哈哈,没有没有,您自谦了,您只是没有写而已

(不能膨胀,谦虚)

你觉得我还有哪些问题没有问到的,也就是你可以说一下你的加分项

5月16日

二面

面试形式:电话面

面试时常:46分钟

面试感悟:面试官相对于一面的面试官来说会严肃一些,总体面试氛围也还可以。有很多问题,例如问到ngnix或者node,我表明了自己不熟之后也没有再问了。面到后面面试官的语气也和蔼了很多,我认为和一面一样都是比较好的面试体验吧。

说一下你们权限的控制,路由权限以及按钮权限

按钮权限除了用你现在的方式你还能想到更好的吗?比如封装一个组件

没有...

你们的token一般是放在哪里

token放在Cookie和放在localStorage、sessionStorage中有什么不同吗

Cookie存在哪些安全问题?如何预防?

SameSite设置为了lax之后是怎样来控制Cookie的发送的

如果顶级域名不同会发送吗

还有哪些前端安全性问题

如果使用jsonp的话会有什么安全问题吗?

你们有做过单点登陆吗

有用过ngnix吗?用它做过哪些事?

有用过node吗?比如用它写一些中间层

项目里做过哪些优化?

你认为你做的项目最大的亮点在哪里

项目中碰到的技术难点

是什么让你有动力写bpmn.js教材呢

有看过哪些源码呢

有给git上的比较好的项目提过pull吗

如果现在给你一个React的项目你觉得自己能多久上手呢

你的职业规划是怎样呢

说一下你的期望薪资吧?

有对比过深圳和广州这边的薪资吗?

5月18日

HR面

面试形式:微信视频

面试时长:50分钟

面试感悟:HR小姐姐很漂亮。然后问了我是否单身,那边也有很多单身的小姐姐。我说如果有缘的话要给我介绍一波。整个氛围还是比较轻松的。问了挺多问题的,我大概挑一些列举,其它的问题在别的面试官那里也有问到我就不重复了。另外在面试完之后,她告诉我,HR还会有二面,请等待通知...

有了解过我们公司吗?

之前不是在深圳吗?为什么打算来广州?

人生中有没有经历过什么对你触动比较大的事情?

有,失恋那次,但我总不能说这个吧。说点别的。

父母是如何看待你的工作的?

你现在单身吗?

单身!单身!我单身!(你要给我介绍女朋友吗?)

工作之余的兴趣爱好

期望薪资给不到怎么办

5月23日

HR二面

面试形式:微信视频

面试时长:58分钟

面试感悟:二面的HR小姐姐也很漂亮😂...开始的HR小姐姐并没有骗我,哈哈哈,开个玩笑,收。其实到了这一步,面试的公司基本就挺想要你了,所以问题也都是一些关于你自己对公司的看法,以及你未来的一个人生或者职业规划。关于这些问题,呆呆认为你如实回答就可以了。就比如HR问你:你是否打算在广州长期发展呀,会不会干了一年之后就又跳了呢?之类的。

编程猫

5月20日

编程猫是从上午10点面到了12点,一共是四轮面试,5位面试官(二面两名面试官一起),呆呆在二面之后就已经累的说不出话了...感觉身体还是不行啊,得加强锻炼。总体来说效率还是很高的,2小时搞定了。

一面

(前端精神小伙)

面试形式:现场面

面试时长:50分钟

面试感悟:由于一面的面试官年纪应该和我相仿,所以还算是聊的来,在自我介绍了之后就问了一些项目相关的问题,之后还是死磕基础知识问。期间也有提到过看了我的掘金文章,可是面试的时候竟然还是要我说一说数据转换类型(我怀疑他没有仔细看哦)。最后聊到福利待遇这一块他也还是挺尴尬的😅,因为并没有呆呆预期的那么好。

动态表单的实现

权限的控制

你们项目一般是如何做缓存的

  • 对于一些没有指纹信息的资源,例如index.html可以使用Cache-Control: no-cache开启协商缓存
  • 对于带有指纹信息的资源,一般会使用splitChunksPlugin进行代码分割,来保证造成最小范围的缓存失效,再设置Cache-Control: max-age=3153600
  • 不需要缓存的资源设置Cache-Control: no-store,即不强缓存也不进行协商缓存

项目中的环境变量是如何控制的?

有两种方式:

  • Shell语句中指定--mode

  • webpack.config.js中指定mode

假设有两个子项目,他们需要共用同一个用户体系如何保证关掉页面之后打开另一个项目用户还是登录状态?

Cookie跨站点访问就可以解决。把用户每次登录获取到的Token存储在Cookie中,因为Cookie是可以同站传输的。

(反正面试官点头了,我也不知道对不对)

项目如何优化

业务层面

打包层面

首屏加载优化

(白屏时间是指浏览器从响应用户输入网址地址,到浏览器开始显示内容的时间。首屏时间是指浏览器从响应用户输入网址地址,到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但在当前视窗的内容需要。白屏时间是首屏时间的一个子集。)

  • CDN分发
  • HTTP缓存方案:强缓存+hash文件
  • 前端做好缓存方案,例如存储在内存(即保存变量),另一个存储在LocalStorage
  • 前端的动态资源加载:路由动态加载、组件动态加载
  • 利用好asyncdefer两个属性
  • 页面使用骨架屏
  • 使用SSR渲染
  • 利用好HTTP压缩,比如指定Content-Encoding首部字段为gzip

SSR的使用场景

由于提到了SSR,但是在项目中没有用,不过面试官还是问了我它的使用场景。

很多网站是出于效益(seo)的考虑才启用服务端渲染,性能倒是在其次。

EventLoop

必问...

requestAnimationFrame属于宏任务还是微任务

它不属于宏任务也不属于微任务,因为它是独立于主线程之外的任务,不归主线程管。

输入URL到页面的呈现

必问...

script与css还有页面的渲染顺序

首先对于script标签正常情况下它会阻塞浏览器,必须得等script标签加载并执行完里面的脚本代码才能去做其他的事情,渲染线程(GUI)和js引擎线程互斥。

而css是由单独的下载线程异步下载的,由于DOM树的解析和构建这一步与css并没有关系,所以它并不会影响DOM的解析。不过最终的布局树是需要DOM树和DOM样式的,因此它会阻塞布局树的建立。

script标签的async是什么时候加载的

async 模式下,JS 不会阻塞浏览器做任何其它的事情。它的加载是异步的,当它加载结束,JS 脚本会立即执行。

说一下==数据类型转换吧

当使用==进行比较的时候,会有以下转换规则(判断规则):

  1. 两边类型如果相同,值相等则相等,如 2 == 3肯定是为false的了
  2. 比较的双方都为基本数据类型:
  • 若是一方为null、undefined,则另一方必须为null或者undefined才为true,也就是null == undefinedtrue或者null == nulltrue,因为undefined派生于null
  • 其中一方为String,是的话则把String转为Number再来比较
  • 其中一方为Boolean,是的话则将Boolean转为Number再来比较
  1. 比较的一方有引用类型:
  • 将引用类型遵循类似ToNumber的转换形式来进行比较(也就是toPrimitive(obj, 'defalut')
  • 两方都为引用类型,则判断它们是不是指向同一个对象

(具体可以看我的这篇文章呀:【精】从206个console.log()完全弄懂数据类型转换的前世今生(下))

说一下Vue的diff算法

简单来说,diff算法有以下过程

  • 先同级比较再比较子节点
  • 先判断一方有子节点和一方没有子节点的情况。如果新的一方有子节点,旧的一方没有,相当于新的子节点替代了原来没有的节点;同理,如果新的一方没有子节点,旧的一方有,相当于要把老的节点删除。
  • 再来比较都有子节点的情况,这里是diff的核心。首先会通过判断两个节点的key、tag、isComment、data同时定义或不定义以及当标签类型为input的时候type相不相同来确定两个节点是不是相同的节点,如果不是的话就将新节点替换旧节点。
  • 如果是相同节点的话才会进入到patchVNode阶段。在这个阶段核心是采用双端比较的算法,同时从新旧节点的两端进行比较,在这个过程中,会用到模版编译时的静态标记配合key来跳过对比静态节点,如果不是的话再进行其它的比较。

Vue3.x借鉴了ivi算法和inferno算法。

它在创建VNode的时候就确定了其类型,以及在mount/patch的过程中采用位运算来判断一个VNode的类型,在这个基础之上再配合核心的Diff算法,使得性能上较Vue2.x有了提升。

diff算法的缺点

因为采用的是同级比较,所以如果发现本级的节点不同的话就会将新节点之间替换旧节点,不会再去比较其下的子节点是否有相同。

二面

(架构师)

面试形式:现场面

面试时长:30分钟左右

面试感悟:这次二面是呆呆唯一一次同时被两名面试官面的情况。两名面试官一个问完就看看另一个(内心活动:该你了,你有什么要问的),还是挺逗的。而且架构师老哥我回答一句他就"嗯"一句,让呆呆贼飘...收不回来了。但是可以看到在他们那个层次还是能看到很多我看不到的东西的,问的问题也都是偏项目构建方面。

说下你们项目的亮点

  • 动态表单表格,它核心的价值在于解决了不同业务产生的差异化和个性化,让我们的作业效率有了很大的提升,这一点不仅仅是对于使用者,也同样提高了我们开发人员的效率。使得一份表单在不同的阶段,也就是生命周期能够呈现不同的内容。
  • 另一点就是工作流,因为一个进件(也就是订单)它是如何审批,由谁审批,这些流程是如何走下去的,这点在盘石系统中都能来进行一个配置。

你们的UI组件库怎么实现按需引入的呢?

(这里的按需引入的意思是,对于一些基础的组件,暴露给父级时,如果父级没有用到它就不引入进来)

可以写一个方法,判断传入进来的数据中用到了哪些类型的基础组件,只对用到的基础组件进行一个引入。

Webpack打包原理

(回答参考:「吐血整理」再来一打Webpack面试题(持续更新))

初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数

开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译

确定入口:根据配置中的 entry 找出所有的入口文件

编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理

完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系

输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会

输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统

Webpack打包有哪些优化

参考文章: https://juejin.im/post/5ea528496fb9a03c576cceac#heading-2

  • 先使用webpack-bundle-analyzer分析打包后整个项目中的体积结构,既可以看到项目中用到的所有第三方包,又能看到各个模块在整个项目中的占比。

  • Vue路由懒加载,使用() => import(xxx.vue)形式,打包会根据路由自动拆分打包。

  • 第三方库按需加载,例如lodash库、UI组件库

  • 使用purgecss-webpack-pluginglob插件去除无用样式(glob插件可以可以同步查找目录下的任意文件夹下的任意文件):

    new PurgecssWebpackPlugin({
        // paths表示指定要去解析的文件名数组路径
        // Purgecss会去解析这些文件然后把无用的样式移除
        paths: glob.sync('./src/**/*', {nodir: true})
        // glob.sync同步查找src目录下的任意文件夹下的任意文件
        // 返回一个数组,如['真实路径/src/css/style.css','真实路径/src/index.js',...]
    })
  • 文件解析优化:

    • babel-loader编译慢,可以通过配置exclude来去除一些不需要编译的文件夹,还可以通过设置cacheDirectory开启缓存,转译的结果会被缓存到文件系统中
    • 文件解析优化:通过配置resolve选项中的aliasalias创建import或者require的别名,加快webpack查找速度。
  • 使用webpack自带插件IgnorePlugin忽略moment目录下的locale文件夹使打包后体积减少278k

    • 问题原因:使用moment时发现会把整个locale语言包都打包进去导致打包体积过大,但是我们只需要用到中文包

    • webpack配置中使用webpack自带的插件IgnorePlugin忽略moment目录下的locale文件夹

    • 之后在项目中引入:

      // index.js
      // 利用IgnorePlugin把只需要的语言包导入使用就可以了,省去了一下子打包整个语言包
      import moment from 'moment';
      // 单独导入中文语言包
      import 'moment/locale/zh-cn';
  • 使用splitChunks进行拆包,抽离公共模块,一些常用配置项:

  • chunks:表示选择哪些 chunks 进行分割,可选值有:async,initial和all

    • minSize: 表示新分离出的chunk必须大于等于minSize,默认为30000,约30kb
    • minChunks: 表示一个模块至少应被minChunks个chunk所包含才能分割,默认为1
    • name: 设置chunk的文件名
    • cacheGroups: 可以配置多个组,每个组根据test设置条件,符合test条件的模块,就分配到该组。模块可以被多个组引用,但最终会根据priority来决定打包到哪个组中。默认将所有来自 node_modules目录的模块打包至vendors组,将两个以上的chunk所共享的模块打包至default组。
  • DllPlugin动态链接库,将第三方库的代码和业务代码抽离:

    • 根目录下创建一个webpack.dll.js文件用来打包出dll文件。并在package.json中配置dll指令生成dll文件夹,里面就会有manifest.json以及生成的xxx.dll.js文件
    • 将打包的dll通过add-asset-html-webpack-plugin添加到html中,再通过DllReferencePlugin把dll引用到需要编译的依赖。

HTTP/2对比HTTP/1.1

  • 使用HPACK算法进行头部压缩,HTTP/2 当中废除了起始行的概念,将起始行中的请求方法、URI、状态码转换成了头字段,不过这些字段都有一个":"前缀,用来和其它请求头区分开。
  • 使用二进制分帧解决了HTTP层面的队头阻塞,即多个请求都通过一个TCP连接并发地完成,实现多路复用。
  • 服务器推送。服务器不再是完全被动地接收请求,响应请求,它也能新建 stream 来给客户端发送消息
  • 新的二进制格式。HTTP/2采用二进制格式传输数据,相比于HTTP/1.1的文本格式,二进制格式具有更好的解析性和拓展性。

怎样看待你做的这几个项目呢

还有挺多问题的...当时脑子有点糊,面完后都忘了...

三面

(项目负责人)

面试形式:现场面

面试时长:10分钟左右

面试感悟:项目负责人老哥文文静静的,说话还是挺幽默的,不过感觉自带气场,给呆呆造成了一定的压力。

项目亮点

你在项目里负责什么

项目难点

你的优点

还有什么想要问的吗?

项目负责人主要都是问一些项目相关的问题,还有你自身的一些问题。

HR面

面试形式:现场面

面试时长:15分钟左右

面试感悟:HR小姐姐:看你面了三场了肯定特别累了吧,我们就简单的聊一下(不得不夸一下此小姐姐温柔体贴😝)。然后大概介绍了一下公司的情况,以及公司的优势。

非技术的自我介绍

住哪里?

工作之余的爱好

其它的就是上家公司薪资、期望薪资、还有什么想要了解的之类的...

滴滴

5月20日

面试部门:杭州某部门

一面

面试形式:视频面试

面试时长:50分钟

面试感悟:面试官给人的感觉...唔...人畜无害?哈哈哈,很和蔼。但是问的问题都很有规律,比如问完项目,就说我看你会用Vue,接下来我问点Vue相关的吧,然后就是双向绑定原理、nextTick原理、虚拟DOM等等;之后又问了HTTP相关。最后他也说了他们现在也是做这种动态表单相关的项目,所以看看我们之前是怎样设计的...呆呆自我感觉滴滴面的还是挺好的,但是没有了后续...之后拖内推的老哥帮忙打听原因,那边也没有回复...仔细想了一下,可能还是项目不够硬吧,因为对比来说亮点还不够闪亮。

说一下你们的第一个项目

说一下你们的UI组件库

动态表格的实现

动态表格由两部分组成:

  • 一个是上面的索引内容(搜索条件),索引内容因为和动态表单很像,又有输入框,又有下拉框,所以上面的索引我也是基于动态表单UI组件库进行开发的
  • 二是下面具体的表格查询的内容,这个是基于Ant Design of Vue中的Table进行封装的,它的结构其实很简单,只要传入需要的表头字段列表,也就是columns,还有具体的数据进去就能显示出表格。不过这里因为表头字段的数量不确定,所以可能需要用到Vue的计算属性来进行一个计算,例如如果表头字段的数量是小于8的话则表格的x方向不需要滚动,否则整个x方向的长度就为数量 X 105px类似这样的值。以及还有涉及到是否分页,每个字段的排序呀等操作。

如果有一个组件需要和前面的组件相关联你会怎么做

我主要是提供三种做法:

  1. 类似于省份城市这种关联的情况我是有专门为它们定制一个组件的
  2. 类似于行业/职业这种不确定有几层的情况我也有专门为它们定制一个组件,当每次选择了前一个下拉框中的选项之后更加选项的内容动态生成一个新的下拉框
  3. 与后台配合,如果后一个子组件和前面一个相关联的话,在选择了前一个组件之后发送请求给后台,后台返回你需要改动的子组件的信息,然后前端再进行更新
  4. 每种子组件都会有一个回调事件,可以在业务代码中判断是哪个子组件发起的事件,然后根据这个来判断。

说一下你们的工作流

说一下你在项目中碰到的难点

Vue的双向绑定原理

Object.defineProperty()有什么缺点?Vue3为什么用Proxy?

nextTick实现原理

nextTick中的waiting是什么时候变为true的呢

在下次DOM更新循环结束之后

说一下虚拟DOM的作用以及实现原理

Vue3有哪些新的API或者有做哪些优化?

performance

首先在性能(performance)上有了更多的优化,一方面表现在virtual dom的生成上更快了,另外在底层还做了一些监听的缓存,也就是事件在被创建的时候会被推进一个缓存中,后续没有改变会直接取缓存。

tree-shaking

tree-shaking它表示的是在打包的时候会去除一些无用的代码。而在Vue3中对它的支持更加友好了,例如像transition、v-model、computed等功能没有用到的话,那么最后打包产生的代码就会将它们去除。也就是说,如果你的Vue项目只写了一个Hello Word的话,那么最后打包的代码中就只有一些核心的代码,如更新算法、响应式等,打包生成的文件可能就只有13.5kb。

Fragments

碎片(Fragments),原本在Vue2.x中每个template下只能允许有一个根节点,但是在Vue3中它可以允许你有多个,用尤大大的话来说就是会将这些内容自动变为一个碎片。

TS

再者就是对TS的支持度很好。虽然Vue3本来就是用TS写的,但是不一定要用TS。另外它也支持Class Component,不过不是第一推荐。

Component API

语法上,对模版语法是零改变的。只不过更加推荐用Component API来写JS部分。Component API它并不是语法,而是新增的API。它带来的好处一个是逻辑重用,方便我们把一些功能的部分抽离出来。另一个它相对于options来说更加集中,用options来写代码想要追寻一个变量的变化比较麻烦。

关于兼容性

目前的Vue3.beta版本是不支持IE11的,因为核心的响应式原理用到了ES6的Proxy,但是以后会去兼容IE11。后面我们在创建一个Vue项目的时候,可以选择不同的版本,支持IE11和不支持IE11的。

有关HTTP缓存的首部字段说一下

必问...

HTTP中的keep-alive有了解吗?

Connection的一个属性值,HTTP/1.1才被标准化,之前一直为Close

作用是建立长连接, 建立一次TCP请求即可发送多次HTTP请求。

在一次传输中它是如何保证每个数据包之间的顺序的?

TCP的报文中有序号和确认号来保证每个数据包之间的顺序。

HTTP/2有了解过吗?

  • 使用HPACK算法进行头部压缩,HTTP/2 当中废除了起始行的概念,将起始行中的请求方法、URI、状态码转换成了头字段,不过这些字段都有一个":"前缀,用来和其它请求头区分开。
  • 使用二进制分帧解决了HTTP层面的队头阻塞,即多个请求都通过一个TCP连接并发地完成,实现多路复用。
  • 服务器推送。服务器不再是完全被动地接收请求,响应请求,它也能新建 stream 来给客户端发送消息
  • 新的二进制格式。HTTP/2采用二进制格式传输数据,相比于HTTP/1.1的文本格式,二进制格式具有更好的解析性和拓展性。

具体说一下HTTP/2中的多路复用

HTTP/2引入了一个二进制分帧层,客户端和服务端进行传输时,数据会先经过二进制分帧层处理,转化为一个个带有请求ID的帧,这些帧在传输完成后根据ID组合成对应的数据。

所以原本一个个完整的HTTP请求报文变成了现在的一堆乱序的二进制帧,这些二进制帧因为不存在先后顺序所以也就不会排队等待,也就没有了HTTP队头阻塞的问题。

通信双方都可以给对方发送二进制帧,这种二进制帧的双向传输的序列,就叫做流。HTTP/2在一个TCP连接上来进行多个数据帧的通信,这就是多路复用的概念。

(所谓的乱序,指的是不同 ID 的 Stream 是乱序的,但同一个 Stream ID 的帧一定是按顺序传输的。)

你认为你的优点是什么?

  1. 从领导以及同事的评价来说,都说我是一个很靠谱的人,因为我对答应的事或者分配的任务我都做到或者很好的完成,所以第一点对于工作我是比较认真负责的。
  2. 因为自己呆的团队都不大,所以很多时候自己不仅仅是前端,也会站在用户的角度去看用户在使用我们项目的时候会有哪些痛点,或者如何去做一些优化,提出一些合理的意见并最终被采纳。举几个例子:一、我们项目中在订单页面会有一个审批记录的列表,这个审批记录的列表是紧靠在页面的右侧可进行收缩折叠的。而这个东西最开始里面显示的内容会很多,比如每个阶段审批的人,时间,备注,同意还是拒绝等等信息。而我当时想的是,站在用户的角度来说,他看到这个记录的时候最关心的应该是审批的人,以及审批时间还有到了哪个阶段,而一些无关紧要的信息没必要开始就带出来。所以我当时就有一个提议:每条记录只显示出用户最关心的东西,而不重要的可以等用户鼠标移动上去的时候再进行一个显示。并且对整个审批记录做了一些精简的处理,使它不那么冗长,比如判断时间,如果是今年的话,就把2020-08-08设置为08-08。二、因为考虑到用户可能会频繁的切换某些页面,而项目中的页面菜单的层级有时候可能会比较深,不方便来回切换。我就有提议在当前页面的顶部实现一个导航栏,每当进入一个新的页面的时候,导航栏就会新增一个标签,记录当前页面,这样相当于留了一个快捷的入口给用户,方便他们进行页面的切换,同时导航栏也会有例如删除左侧的全部标签,删除右侧,只保留当前页等功能。
  3. 我是一个比较喜欢总结和分享的人,所以在工作之余也会经常写博客,目前的更新频率是会保持每周有一篇比较高质量的文章。也一直保持着自己对前端的这份热情。

阿里

5月21日

面试部门:财务钉钉

一面

面试形式:电话面试

面试时长:30分钟

面试感悟:阿里的面试是子弈小哥哥帮忙内推的,呆呆算是放在最后来了,和之前了解的一样,面试官可能会针对于你说的内容然后深挖,所以你说的每一句话都必须考虑清楚才好。呆呆是一面就挂了,归根到底的原因不解释,技术还不到家,对一些知识点的理解还不够深刻。但是抛开了技术以外的原因,我认为面试时状态的不佳也是导致这次面试失败的一个原因。因为当时是晚上7点进行的电话面试,一整天呆呆都在忙于复习没有休息,并匆匆的吃了晚饭,所以到了面试那个点自身的精神状态已经非常不好了,导致面试时没有一点精神劲。举个例子,面试官问我的第一个问题,能说一下position属性有哪些值吗?我把position这个单词重复了好几遍大脑一片空白,然后说了一句:"不好意思面试官,您能把这个单词拼一下吗"...

不要说呆呆很屌...我都服我自己。奠。

这听起来可能特别的好笑🤭,但是是真实发生在呆呆身上,所以也是想要告诉各位小伙伴,面试前的准备确实要充分,但是自己的状态以及心态也非常的重要,不要因为一些非技术以外的原因让自己遗憾...

position属性有哪些值分别介绍一下

  • 静态定位(static):默认的定位,也就是说网页中所有元素默认都是静态定位的,它始终会处于文档流给予的位置。
  • inherit:规定应该从父元素继承 position 属性的值。但是任何的版本的IE(包括 IE8)都不支持属性值 inherit
  • 相对定位(relative):相对于该元素在文档中的初始位置进行定位。通过 “left”、”top”、”right” 以及 “bottom” 属性来设置此元素相对于自身位置的偏移。它不会脱离标准的文档流。
  • 绝对定位(absolute):相对的元素是它最近的一个祖先,该祖先满足:position的值必须是:relative、absolute、fixed,若没有这样的祖先则相对于body进行定位。盒子的偏移位置不影响常规流中的任何元素,其margin不与其他任何margin折叠。
  • 固定定位(fixed):生成绝对定位的元素。默认情况下,可定位于相对于浏览器窗口的指定坐标。元素的位置通过 “left”, “top”, “right” 以及 “bottom” 属性进行规定。不论窗口滚动与否,元素都会留在那个位置。但当祖先元素具有transform属性且不为none时,就会相对于祖先元素指定坐标,而不是浏览器窗口。

relative的定位规则

  • 相对于该元素在文档中的初始位置进行定位。通过 “left”、”top”、”right” 以及 “bottom” 属性来设置此元素相对于自身位置的偏移。
  • 如果他原来在常规流的默认位置改变了,那他也会跟着变位置,永远围着整个body自己原来的那一小块老地方转。所以说相对定位没有脱离文档流。

脱离文档流是会呈现什么样的效果呢?

脱离文档流,也就是将元素从普通的布局排版中拿走,其他盒子在定位的时候,会当做脱离文档流的元素不存在而进行定位。

而在CSS中,使用float和设置position:absolute都会使得元素脱离文档流。只不过它两的区别是:

使用float脱离文档流时,其他盒子会无视这个元素,但其他盒子内的文本依然会为这个元素让出位置,环绕在周围。而对于使用position:absolute脱离文档流的元素,其他盒子与其他盒子内的文本都会无视它。

常规流(文档流)是个怎样的排列关系

将窗体自上而下分成一行一行,并在每行中按从左至右的挨次排放元素。

inline-block的使用场景

  1. 要设置某些子元素在一行或者多行内显示,尤其是排列方向一致的情况下,应尽量用inline-block
  2. 希望若干个元素平行排列,且在父元素中居中排列,此时可以用inline-block,且给父元素设text-align: center
  3. inline-block可以用一排a {display: inline-block}实现横向导航栏,无论是居左的导航栏还是居右的都适用。

对于第一种和第三种情况虽然都可以使用float来实现,不过inline-block会比它好一些,原因如下:

  • 浮动导致父元素高度塌陷的问题

(望补充)

GET和POST的区别

从缓存的角度上说,GET会被浏览器主动缓存下来,留下历史记录,但是POST不会。

从编码的角度上说,GET只能进行URL编码,它只能接收ASCII字符,但是POST没有限制。

从参数的角度上说,GET一般放在URL上传递参数,POST放在请求体里,更适合传递敏感信息。

从幂等的角度上说,GET是幂等的,而POST不是。

不过据我了解的,其实GET和POST本质上都是TCP连接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致它们在应用过程中体现出一些不同。

还有可以从TCP的角度上说,GET请求会把请求报文一次性发出去,但是POST会分为两个TCP数据包。首先发送的是header部分,若是服务器响应100(continue),则会发送body部分,当然火狐浏览器除外,它的 POST 请求只发一个 TCP 包。

为什么说GET会留下历史记录?

浏览器规定的

GET可以上传图片吗?

get可以通过URL传参上传图片,可以将图片转为base64格式再进行传输,不过由于get请求的参数是有大小限制的,最多不超过4kb。

GET和POST的安全性问题为什么说POST相对安全一些

首先get只能使用URL编码,post支持各种类型的编码。然后get参数在URL上,容易暴露,所以说post相对安全一些。

GET就一定是幂等的吗?

不一定,我们平常说的幂等只是一种约定,最终幂等还是不幂等取决于服务器怎么处理请求。

说一下你所知道的缓存方案

你们为什么要做这个动态表单?(业务痛点)

动态表单具体的难点在哪?

有没有了解行业内其它做动态表单这一块的

问卷网:https://www.wenjuan.com/list

功能主要是一些问卷调查的形式,例如员工培训满意度、投票评选之类的。

魔方网表:http://www.mf999.com/index.html

UI样式较旧,类似老套的系统。

你还有什么想要问我的吗?

深圳开思

5月25日

面试部门:某业务部门

一面

面试形式:现场面

面试时长:40分钟

面试感悟:面试开始前,看到面试官在面另一个小哥,隐约听到小哥被面试官怼的好惨...所以面试前内心还有点坎坷。但是当真正去面试的时候,面试官好像没有那么凶了,甚至自我感觉眼神中透露着对我的不怀好意...原来他说他有看过我写的文章,说得想点我不知道的考考我...😅

说一说原型链

(面试官叫我详细描述一下,然后说自己也学习一下...我:"不不不,您太谦虚了。")

null为什么被typeof错误的判断为了'object'

说一说几种this指向

  • 默认绑定(非严格模式下this指向全局对象, 严格模式下this会绑定到undefined)

  • 隐式绑定(当函数引用有上下文对象时, 如 obj.foo()的调用方式, foo内的this指向obj)

  • 显示绑定(通过call()或者apply()方法直接指定this的绑定对象, 如foo.call(obj))

  • new绑定

  • 箭头函数绑定(this的指向由外层作用域决定的)

使用箭头函数时需要注意什么?

  1. 不能用于构造函数

    let Person = (sex) => {
      this.sex = sex
    }
    console.log(new Person('boy'))

    结果为:

    Uncaught TypeError: Person is not a constructor
    
  2. 不要用于事件绑定

    例如:

    const btn = document.getElementById('btn');
    btn.addEventListener('click', function() {
      console.log(this) // window
    })
    
    const btn = document.getElementById('btn');
    btn.addEventListener('click', () => {
      console.log(this) // <button id="btn">按钮</button>
    })

    可以看到,使用箭头函数进行事件绑定,会丢失原本的this指向。

  3. 原型链上添加方法也不能用箭头函数

说一下JS内置对象

构造器内置对象

  • Object
  • Boolean
  • String
  • Number
  • Function
  • Array
  • RegExp
  • Date
  • Error

其它对象:

  • Math
  • JSON
  • 全局对象
    • NaN
    • parseInt
    • encodeURIComponent

描述一下作用域链

(答案参考:《面试分享:两年工作经验成功面试阿里P6总结》)

当代码在一个环境中创建时,会创建变量对象的一个作用域链(scope chain)来保证对执行环境有权访问的变量和函数。作用域第一个对象始终是当前执行代码所在环境的变量对象(VO)。如果是函数执行阶段,那么将其activation object(AO)作为作用域链第一个对象,第二个对象是上级函数的执行上下文AO,下一个对象依次类推。

在《JavaScript深入之变量对象》中讲到,当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。

闭包的使用场景

闭包产生的本质就是,当前环境中存在指向父级作用域的引用。

  • 函数防抖
  • 使用闭包设计实现单例模式
  • 为多个组件独立属性,例如在闭包中进行id的累加
  • 设置私有变量
  • 解决经典问题for循环和setTimeout

具体可以看:闭包实际场景应用

使用闭包需要注意什么

闭包有三个特性:

  1. 函数嵌套函数;

  2. 内部函数使用外部函数的参数和变量;

  3. 参数和变量不会被垃圾回收机制回收。

所以可以看到它的缺点:

  1. 常驻内存,增加内存使用量;

  2. 使用不当造成内存泄漏。

因此我们在使用时需要注意不用的变量要及时的清理掉。

EventLoop

用过哪些ES6的功能

谈谈你对模块的理解

(答案参考:ES6专题—class与模块化(10))

模块可以理解为函数代码块的功能,是封装对象的属性和方法的javascript代码,它可以是某单个文件、变量或者函数。

模块实质上是对业务逻辑分离实现低耦合高内聚,也便于代码管理而不是所有功能代码堆叠在一起,模块真正的魔力所在是仅导出和导入你需要的绑定,而不是将所有的东西都放到一个文件。

在理想状态下我们只需要完成自己部分的核心业务逻辑代码,其他方面的依赖可以通过直接加载被人已经写好模块进行使用即可。

二面

(项目负责人)

面试官:你好,你可以脱下口罩,我们面对面交流一下。

我:...(难道颜值加分吗?那我明天就可以来上班了...)

都是项目相关的问题,然后:

  • 之前的团队人员分配情况
  • 职业规划
  • 期望薪资
  • 有什么想问的

HR面

常规问题...和上面问的差不多...

开思也算是深圳的一家比较大的汽车金融公司吧,呆呆这里面的也是3~5年的岗位,其实从一面之后呆呆就有预感对方挺中意我的了,但是在最后HR面的时候他们可能也觉得达不到我的期望薪资,所以也是在我回家之后与我电话沟通,表明如果以后有想继续留在深圳的话可以再考虑一下。应该说虽无缘但也算互相结交了一下吧。