5月14日
面试部门:广州某部门
一面
面试形式:牛客网视频面试
面试时长:50分钟
面试感悟:YY和CVTE都是托树酱小哥哥帮忙找的朋友内推,另外YY算是呆呆第一个面的比较大的厂了吧,也是第一次在牛客网上视频面试,所以还是有些激动的。面试官稍微晚到了2分钟,不过问题不大。呆呆一般在面试前喜欢听些自己喜欢的歌,算是能让自己放松下来吧。整个面试过程也并没有之前以为的那么压抑,虽然面试官问的问题基本都回答上来了,但是面试官并没有告诉我正确与否。
动态表格由两部分组成:
- 一个是上面的索引内容(搜索条件),索引内容因为和动态表单很像,又有输入框,又有下拉框,所以上面的索引我也是基于动态表单UI组件库进行开发的
- 二是下面具体的表格查询的内容,这个是基于Ant Design of Vue中的Table进行封装的,它的结构其实很简单,只要传入需要的表头字段列表,也就是
columns
,还有具体的数据进去就能显示出表格。不过这里因为表头字段的数量不确定,所以可能需要用到Vue的计算属性来进行一个计算,例如如果表头字段的数量是小于8的话则表格的x
方向不需要滚动,否则整个x
方向的长度就为数量 X 105px
类似这样的值。以及还有涉及到是否分页,每个字段的排序呀等操作...
盘石中配置的,后台生成。
我们后台有用到微服务,但是前端这边没有用到。
(兼容性这一块呆呆做的不多,参考: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
同上
必问...
这个在上篇面试文章中已经总结过喽,可以戳这里👇:
霖呆呆的近期面试128题汇总(含超详细答案) | 掘金技术征文
之前自己工作之余有玩过一些,但是没有实际运用在工作中。
针对于这种要我们口述实现的问题,大家可以从这么几个方面去回答:
- 需要实现的
API
的功能点 - 该
API
的参数及返回值 - 是否需要挂载在某个原型对象上(例如
Array.prototype
),或者就是一个单独的函数(例如实现new
) - 具体的实现方式
根据MDN
上的要求,呆呆这里贴一个简易版的实现方式。
需要满足的条件:
findIndex
方法对数组中的每个数组索引0..length-1
(包括)执行一次callback
函数,直到找到一个callback
函数返回真实值(强制为true
)的值。- 如果找到这样的元素,
findIndex
会立即返回该元素的索引。 - 如果回调从不返回真值,或者数组的
length
为0,则findIndex
返回-1。 - 回调函数调用时有三个参数:元素的值,元素的索引,以及被遍历的数组。
- 如果一个
thisArg
参数被提供给findIndex
, 它将会被当作this
使用在每次回调函数被调用的时候。如果没有被提供,将会使用undefined
。 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
...
这个没有唯一的答案,还希望有大佬能够补充一下,后期呆呆更新到文章中来。
这边有的一个方案是:
首先保证在移动端上显示正常,然后设置一个最大的max-width
来限定死。
参考文章: https://juejin.im/post/5ea528496fb9a03c576cceac#heading-2
-
先使用
webpack-bundle-analyzer
分析打包后整个项目中的体积结构,既可以看到项目中用到的所有第三方包,又能看到各个模块在整个项目中的占比。 -
Vue
路由懒加载,使用() => import(xxx.vue)
形式,打包会根据路由自动拆分打包。 -
第三方库按需加载,例如
lodash
库、UI
组件库 -
使用
purgecss-webpack-plugin
和glob
插件去除无用样式(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
选项中的alias
。alias
创建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,约30kbminChunks
: 表示一个模块至少应被minChunks个chunk所包含才能分割,默认为1name
: 设置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引用到需要编译的依赖。
- 根目录下创建一个
写过一些简单的插件。
一、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)
5月14日
面试部门:希沃未来教育某部门(广州)
一面
面试形式:电话面
面试时常:1小时6分钟
面试感悟:面试官人很和善,基本全程都在"嗯"
,也会和你开玩笑,然后我答不出来的时候他也会说:"嗯,没关系,问题不大"
。不过可能是因为自己在某些问题上回答的太繁重了,导致经常被面试官打断(大概有2,3次吧),所以大家在面试的时候也要抓重点回答不要偏离问题的方向。
平台、团队、薪资、地域
我是从这几个角度来分析的:
- 自己离职前本来应该还有一次加薪机会的,但是走了,所以我认为这次跳槽的薪资幅度也不算高
- 对比于相同工作年限的同行来说在技术上可能会稍微强一些
- 自己的社区影响力,喜欢写博客
薪资只是我找工作的其中一个比较项,回到我最原始的想法,我更在意的是平台和团队。
(当时是按欧巴的总结答的:「吐血整理」再来一打Webpack面试题(持续更新))
初始化参数
:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数
开始编译
:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译
确定入口
:根据配置中的 entry 找出所有的入口文件
编译模块
:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
完成模块编译
:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
输出资源
:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
输出完成
:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
webpack 在编译代码过程中,会触发一系列 Tapable 钩子事件,插件所做的,就是找到相应的钩子,往上面挂上自己的任务,也就是注册事件,这样,当 webpack 构建的时候,插件注册的事件就会随着钩子的触发而执行了。
具体可以看我的这篇文章:霖呆呆的六个自定义Webpack插件详解-自定义plugin篇(3)
当时我回答的是:这个可能要从webpack插件设计者的角度来看吧,因为webpack插件它是可以指定在哪个生命周期来做某些处理的,所以我认为它主要是和设计时的调用时机有关,而和定义时数组里的顺序无关;但是loader我知道它是有执行顺序的,一般是从右到左,从下到上,但是也可以用一个叫做enforce
的属性去改变执行顺序。
当时答的:
webpack-dashboard
:可以更友好的展示相关打包信息。
speed-measure-webpack-plugin
:简称 SMP,分析出 Webpack 打包过程中 Loader 和 Plugin 的耗时,有助于找到构建过程中的性能瓶颈。
size-plugin
:监控资源体积变化,尽早发现问题
webpack-bundle-analyzer
:分析打包后整个项目中的体积结构,既可以看到项目中用到的所有第三方包,又能看到各个模块在整个项目中的占比。
当时答的:
- 使用
purgecss-webpack-plugin
和glob
插件去除无用样式(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
选项中的alias
、extensions
、modules
来实现。alias
创建import
或者require
的别名;加快webpack
查找速度。extensions
自动解析确定的扩展;modules
解析模块时应该搜索的目录,通常建议使用绝对路径,避免层层查找祖先目录。
- 还有的话,从
webpack-merge
提取一些公共的配置项
必问...
实不相瞒的告诉您,我之前有写过webpack相关的这一部分教程,包括从零开始搭建,然后再到基本的loader
的使用,比如style-loader
、css-loader
、raw-loader
等,然后到一些基本的插件的使用,比如html-webpack-plugin
、clean-webpack-plugin
,以及像webpack-dev-server
...
面试官:"好了好了,我大概清楚了,OK,这个问题我们过吧"
必问...
(这个大家应该都清楚)
为了保证双方都有发送和接收的能力,如果只有两次握手的话就只能保证发送方,一般就是我们的客户端有发送的能力,但是并不能保证它有接收的能力,所以必须是三次。
当时的答案:
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
也会,然后就被面试官打断了。
没想出来,然后反问了他是不是不操作DOM就不会?
面试官:呵呵呵,没事,你等会去了解一下
但后面有了解到,面试官想问的可能是async
和defer
吧:
正常模式
这种情况下 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。
把回流的过程说一下就可以了
我的回答:css
中使用transform
会促发CPU
加速,使浏览器不会进行回流和重绘。但是如果用js
改变的话我不确定会不会,这样吧,我盲猜一下我感觉它会,因为我记得我看过说要避免频繁的使用style
,不过不知道自己说的对不对。所以相对于用js
调用的话后者的性能肯定就会更好一些。
面试官:呵呵呵,这个你下去在了解一下吧
在使用CSS3
中的transform
、opacity
、filter
这些属性的时候,能跳过布局和绘制流程,直接进入非主线处理的部分,即交给合成线程。
- 合成线程中会调用线程池进行GPU加速,因为GPU是比较擅长处理位图数据的
- 且它没有占有主线程
进程:资源分配的最小单位
线程:资源调度的最小单位
面试官;"你是不是回答反了?算了算了,我们不绕了,这个问题就这样吧"
(上面这个是正确的,当时应该是答反了)
做的少,然后我提到了在程序初始化的时候可以使用以下方式来修改根节点的字体大小:
(假设你需要的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
的宽度(窗口的宽度)- 监听
window
的resize
事件
一般还需要配合一个meta
头:
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-sacle=1.0, maximum-scale=1.0, user-scalable=no" />
- 使用
HPACK
算法进行头部压缩,HTTP/2
当中废除了起始行的概念,将起始行中的请求方法、URI、状态码转换成了头字段,不过这些字段都有一个":"
前缀,用来和其它请求头区分开。 - 使用二进制分帧解决了HTTP层面的队头阻塞,即多个请求都通过一个TCP连接并发地完成,实现多路复用。
- 服务器推送。服务器不再是完全被动地接收请求,响应请求,它也能新建 stream 来给客户端发送消息
- 新的二进制格式。HTTP/2采用二进制格式传输数据,相比于HTTP/1.1的文本格式,二进制格式具有更好的解析性和拓展性。
(当时只答出了第一段话)
HTTP/2引入了一个二进制分帧层,客户端和服务端进行传输时,数据会先经过二进制分帧层处理,转化为一个个带有请求ID的帧,这些帧在传输完成后根据ID组合成对应的数据。
所以原本一个个完整的HTTP请求报文变成了现在的一堆乱序的二进制帧,这些二进制帧因为不存在先后顺序所以也就不会排队等待,也就没有了HTTP队头阻塞的问题。
通信双方都可以给对方发送二进制帧,这种二进制帧的双向传输的序列,就叫做流。HTTP/2在一个TCP连接上来进行多个数据帧的通信,这就是多路复用的概念。
(所谓的乱序,指的是不同 ID 的 Stream 是乱序的,但同一个 Stream ID 的帧一定是按顺序传输的。)
(回答参考:https://juejin.im/post/5e76bd516fb9a07cce750746#heading-91)
主要是通过HPACK算法来实现压缩头部的功能,其主要有以下两个亮点:
- 首先是在服务器和客户端之间建立哈希表,将用到的字段存放在这张表中,那么在传输的时候对于之前出现过的值,只需要把索引(比如0,1,2,...)传给对方即可,对方拿到索引查表就行了。这种传索引的方式,可以说让请求头字段得到极大程度的精简和复用。
- 其次是对于整数和字符串进行哈夫曼编码,哈夫曼编码的原理就是先将所有出现的字符建立一张索引表,然后让出现次数多的字符对应的索引尽可能短,传输的时候也是传输这样的索引序列,可以达到非常高的压缩率。
- 通过混合加密保证传输的数据不被窃听
- 通过数字签名的方式保证数据不会被篡改
- 通过数字证书保证服务器身份的真实性
对称密钥加密是最简单的一种加密方式,它的加解密用的都是相同的密钥,这样带来的好处就是加解密效率很快,但是并不安全,如果有人拿到了这把密钥那谁都可以进行解密了。
而非对称密钥会有两把密钥,一把是私钥,只有自己才有;一把是公钥,可以发布给任何人。并且加密的内容只有相匹配的密钥才能解。这样带来的一个好处就是能保证传输的内容是安全的,因为例如如果是公钥加密的数据,就算是第三方截取了这个数据但是没有对应的私钥也破解不了。不过它也有缺点,一是公钥因为是公开的,谁都可以过去,如果内容是通过私钥加密的话,那拥有对应公钥的黑客就可以用这个公钥来进行解密得到里面的信息;二来公钥里并没有包含服务器的信息,也就是并不能确保服务器身份的合法性;并且非对称加密的时候要消耗一定的时间,减低了数据的传输效率。
在建立连接的时候为了保证双方都能拿到对称密钥
,所以需要使用非对称加密
而双方都拿到了这把对称密钥
之后,就可以用这把对称密钥进行对称加密了。
(面试官提示万变不离其宗...可我还是悟不出来)
当时答的只知道CSRF的攻击原理,但是不知道怎么去做...
(毕竟呆呆是一个品行良好的网民)
key
的作用是尽可能的复用 DOM 元素。
新旧 children 中的节点只有顺序是不同的时候,最佳的操作应该是通过移动元素的位置来达到更新的目的。
需要在新旧 children 的节点中保存映射关系,以便能够在旧 children 的节点中找到可复用的节点。key也就是children中节点的唯一标识。
这道题我开始没太理解面试官的意思,然后他提示了我:回到key
设计的初衷。之后开着玩笑的说了一句:"我在给你点时间思考一下,我挺希望你能答上来的。"
我:对不起我可能要让你失望了,我确实没想出来
面试官:嗯...没关系问题不大哈,这个问题我们过吧
(不过在最后面试完之后我也有问面试官这个问题,他说:key
它就是为了保证我们的DOM节点能够复用,如果你都把它设置为了一个随机数的话,那每次重写渲染都会是另一个数了根本不可能达到复用的效果)
😂...绝了,原来想问的是这个意思,是我学艺不精了。
我:"我有了解Vue的双向绑定原理,如果你想听的话我可以说给你听"
面试官:"不用,你就大致说一下就可以了,让我知道你确实是了解了"
我:"巴拉巴拉"
由于双向绑定的时候提到了Object.defineProperty()
属性,面试官可能就想到了这道题。
这里其实我应该反思一下自己...
在面试官还没说完题目的时候我就急着先说自己知道甚至还想问面试官是不是这道题,这样显然是不好的,可能也是和面试官的面试氛围太好了...放飞了自我吧。但还好面试官不和我斤斤计较。
😂😂😂,老实回答,在自己的文章中有写到这道题,所以很快就知道你想考的是什么了。
之后面试官说:"嗯呢,我昨天也有看了一下你的文章,lv4,望尘莫及..."
我:哈哈哈哈,没有没有,您自谦了,您只是没有写而已
(不能膨胀,谦虚)
5月16日
二面
面试形式:电话面
面试时常:46分钟
面试感悟:面试官相对于一面的面试官来说会严肃一些,总体面试氛围也还可以。有很多问题,例如问到ngnix或者node,我表明了自己不熟之后也没有再问了。面到后面面试官的语气也和蔼了很多,我认为和一面一样都是比较好的面试体验吧。
没有...
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
- 前端的动态资源加载:路由动态加载、组件动态加载
- 利用好
async
和defer
两个属性 - 页面使用骨架屏
- 使用SSR渲染
- 利用好HTTP压缩,比如指定
Content-Encoding
首部字段为gzip
由于提到了SSR,但是在项目中没有用,不过面试官还是问了我它的使用场景。
很多网站是出于效益(seo)的考虑才启用服务端渲染,性能倒是在其次。
必问...
它不属于宏任务也不属于微任务,因为它是独立于主线程之外的任务,不归主线程管。
必问...
首先对于script
标签正常情况下它会阻塞浏览器,必须得等script
标签加载并执行完里面的脚本代码才能去做其他的事情,渲染线程(GUI
)和js引擎线程互斥。
而css是由单独的下载线程异步下载的,由于DOM树的解析和构建这一步与css并没有关系,所以它并不会影响DOM的解析。不过最终的布局树是需要DOM树和DOM样式的,因此它会阻塞布局树的建立。
async 模式下,JS 不会阻塞浏览器做任何其它的事情。它的加载是异步的,当它加载结束,JS 脚本会立即执行。
当使用==
进行比较的时候,会有以下转换规则(判断规则):
- 两边类型如果相同,值相等则相等,如
2 == 3
肯定是为false
的了 - 比较的双方都为基本数据类型:
- 若是一方为
null、undefined
,则另一方必须为null或者undefined
才为true
,也就是null == undefined
为true
或者null == null
为true
,因为undefined
派生于null
- 其中一方为
String
,是的话则把String
转为Number
再来比较 - 其中一方为
Boolean
,是的话则将Boolean
转为Number
再来比较
- 比较的一方有引用类型:
- 将引用类型遵循类似
ToNumber
的转换形式来进行比较(也就是toPrimitive(obj, 'defalut')
- 两方都为引用类型,则判断它们是不是指向同一个对象
(具体可以看我的这篇文章呀:【精】从206个console.log()完全弄懂数据类型转换的前世今生(下))
简单来说,diff算法有以下过程
- 先同级比较再比较子节点
- 先判断一方有子节点和一方没有子节点的情况。如果新的一方有子节点,旧的一方没有,相当于新的子节点替代了原来没有的节点;同理,如果新的一方没有子节点,旧的一方有,相当于要把老的节点删除。
- 再来比较都有子节点的情况,这里是
diff
的核心。首先会通过判断两个节点的key、tag、isComment、data同时定义或不定义以及当标签类型为input的时候type相不相同
来确定两个节点是不是相同的节点,如果不是的话就将新节点替换旧节点。 - 如果是相同节点的话才会进入到
patchVNode
阶段。在这个阶段核心是采用双端比较的算法,同时从新旧节点的两端进行比较,在这个过程中,会用到模版编译时的静态标记配合key
来跳过对比静态节点,如果不是的话再进行其它的比较。
Vue3.x
借鉴了ivi
算法和inferno
算法。
它在创建VNode
的时候就确定了其类型,以及在mount/patch
的过程中采用位运算
来判断一个VNode
的类型,在这个基础之上再配合核心的Diff算法,使得性能上较Vue2.x有了提升。
因为采用的是同级比较,所以如果发现本级的节点不同的话就会将新节点之间替换旧节点,不会再去比较其下的子节点是否有相同。
二面
(架构师)
面试形式:现场面
面试时长:30分钟左右
面试感悟:这次二面是呆呆唯一一次同时被两名面试官面的情况。两名面试官一个问完就看看另一个(内心活动:该你了,你有什么要问的),还是挺逗的。而且架构师老哥我回答一句他就"嗯"
一句,让呆呆贼飘...收不回来了。但是可以看到在他们那个层次还是能看到很多我看不到的东西的,问的问题也都是偏项目构建方面。
- 动态表单表格,它核心的价值在于解决了不同业务产生的差异化和个性化,让我们的作业效率有了很大的提升,这一点不仅仅是对于使用者,也同样提高了我们开发人员的效率。使得一份表单在不同的阶段,也就是生命周期能够呈现不同的内容。
- 另一点就是工作流,因为一个进件(也就是订单)它是如何审批,由谁审批,这些流程是如何走下去的,这点在盘石系统中都能来进行一个配置。
(这里的按需引入的意思是,对于一些基础的组件,暴露给父级时,如果父级没有用到它就不引入进来)
可以写一个方法,判断传入进来的数据中用到了哪些类型的基础组件,只对用到的基础组件进行一个引入。
(回答参考:「吐血整理」再来一打Webpack面试题(持续更新))
初始化参数
:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数
开始编译
:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译
确定入口
:根据配置中的 entry 找出所有的入口文件
编译模块
:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
完成模块编译
:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
输出资源
:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
输出完成
:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
参考文章: https://juejin.im/post/5ea528496fb9a03c576cceac#heading-2
-
先使用
webpack-bundle-analyzer
分析打包后整个项目中的体积结构,既可以看到项目中用到的所有第三方包,又能看到各个模块在整个项目中的占比。 -
Vue
路由懒加载,使用() => import(xxx.vue)
形式,打包会根据路由自动拆分打包。 -
第三方库按需加载,例如
lodash
库、UI
组件库 -
使用
purgecss-webpack-plugin
和glob
插件去除无用样式(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
选项中的alias
。alias
创建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,约30kbminChunks
: 表示一个模块至少应被minChunks个chunk所包含才能分割,默认为1name
: 设置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引用到需要编译的依赖。
- 根目录下创建一个
- 使用
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组件库进行开发的
- 二是下面具体的表格查询的内容,这个是基于Ant Design of Vue中的Table进行封装的,它的结构其实很简单,只要传入需要的表头字段列表,也就是
columns
,还有具体的数据进去就能显示出表格。不过这里因为表头字段的数量不确定,所以可能需要用到Vue的计算属性来进行一个计算,例如如果表头字段的数量是小于8的话则表格的x
方向不需要滚动,否则整个x
方向的长度就为数量 X 105px
类似这样的值。以及还有涉及到是否分页,每个字段的排序呀等操作。
我主要是提供三种做法:
- 类似于省份城市这种关联的情况我是有专门为它们定制一个组件的
- 类似于行业/职业这种不确定有几层的情况我也有专门为它们定制一个组件,当每次选择了前一个下拉框中的选项之后更加选项的内容动态生成一个新的下拉框
- 与后台配合,如果后一个子组件和前面一个相关联的话,在选择了前一个组件之后发送请求给后台,后台返回你需要改动的子组件的信息,然后前端再进行更新
- 每种子组件都会有一个回调事件,可以在业务代码中判断是哪个子组件发起的事件,然后根据这个来判断。
在下次DOM更新循环结束之后
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的。
必问...
Connection
的一个属性值,HTTP/1.1
才被标准化,之前一直为Close
。
作用是建立长连接, 建立一次TCP请求即可发送多次HTTP请求。
TCP的报文中有序号和确认号来保证每个数据包之间的顺序。
- 使用
HPACK
算法进行头部压缩,HTTP/2
当中废除了起始行的概念,将起始行中的请求方法、URI、状态码转换成了头字段,不过这些字段都有一个":"
前缀,用来和其它请求头区分开。 - 使用二进制分帧解决了HTTP层面的队头阻塞,即多个请求都通过一个TCP连接并发地完成,实现多路复用。
- 服务器推送。服务器不再是完全被动地接收请求,响应请求,它也能新建 stream 来给客户端发送消息
- 新的二进制格式。HTTP/2采用二进制格式传输数据,相比于HTTP/1.1的文本格式,二进制格式具有更好的解析性和拓展性。
HTTP/2引入了一个二进制分帧层,客户端和服务端进行传输时,数据会先经过二进制分帧层处理,转化为一个个带有请求ID的帧,这些帧在传输完成后根据ID组合成对应的数据。
所以原本一个个完整的HTTP请求报文变成了现在的一堆乱序的二进制帧,这些二进制帧因为不存在先后顺序所以也就不会排队等待,也就没有了HTTP队头阻塞的问题。
通信双方都可以给对方发送二进制帧,这种二进制帧的双向传输的序列,就叫做流。HTTP/2在一个TCP连接上来进行多个数据帧的通信,这就是多路复用的概念。
(所谓的乱序,指的是不同 ID 的 Stream 是乱序的,但同一个 Stream ID 的帧一定是按顺序传输的。)
- 从领导以及同事的评价来说,都说我是一个很靠谱的人,因为我对答应的事或者分配的任务我都做到或者很好的完成,所以第一点对于工作我是比较认真负责的。
- 因为自己呆的团队都不大,所以很多时候自己不仅仅是前端,也会站在用户的角度去看用户在使用我们项目的时候会有哪些痛点,或者如何去做一些优化,提出一些合理的意见并最终被采纳。举几个例子:一、我们项目中在订单页面会有一个审批记录的列表,这个审批记录的列表是紧靠在页面的右侧可进行收缩折叠的。而这个东西最开始里面显示的内容会很多,比如每个阶段审批的人,时间,备注,同意还是拒绝等等信息。而我当时想的是,站在用户的角度来说,他看到这个记录的时候最关心的应该是审批的人,以及审批时间还有到了哪个阶段,而一些无关紧要的信息没必要开始就带出来。所以我当时就有一个提议:每条记录只显示出用户最关心的东西,而不重要的可以等用户鼠标移动上去的时候再进行一个显示。并且对整个审批记录做了一些精简的处理,使它不那么冗长,比如判断时间,如果是今年的话,就把
2020-08-08
设置为08-08
。二、因为考虑到用户可能会频繁的切换某些页面,而项目中的页面菜单的层级有时候可能会比较深,不方便来回切换。我就有提议在当前页面的顶部实现一个导航栏,每当进入一个新的页面的时候,导航栏就会新增一个标签,记录当前页面,这样相当于留了一个快捷的入口给用户,方便他们进行页面的切换,同时导航栏也会有例如删除左侧的全部标签,删除右侧,只保留当前页等功能。 - 我是一个比较喜欢总结和分享的人,所以在工作之余也会经常写博客,目前的更新频率是会保持每周有一篇比较高质量的文章。也一直保持着自己对前端的这份热情。
5月21日
面试部门:财务钉钉
一面
面试形式:电话面试
面试时长:30分钟
面试感悟:阿里的面试是子弈小哥哥帮忙内推的,呆呆算是放在最后来了,和之前了解的一样,面试官可能会针对于你说的内容然后深挖,所以你说的每一句话都必须考虑清楚才好。呆呆是一面就挂了,归根到底的原因不解释,技术还不到家,对一些知识点的理解还不够深刻。但是抛开了技术以外的原因,我认为面试时状态的不佳也是导致这次面试失败的一个原因。因为当时是晚上7点进行的电话面试,一整天呆呆都在忙于复习没有休息,并匆匆的吃了晚饭,所以到了面试那个点自身的精神状态已经非常不好了,导致面试时没有一点精神劲。举个例子,面试官问我的第一个问题,能说一下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时,就会相对于祖先元素指定坐标,而不是浏览器窗口。
- 相对于该元素在文档中的初始位置进行定位。通过 “left”、”top”、”right” 以及 “bottom” 属性来设置此元素相对于自身位置的偏移。
- 如果他原来在常规流的默认位置改变了,那他也会跟着变位置,永远围着整个body自己原来的那一小块老地方转。所以说相对定位没有脱离文档流。
脱离文档流,也就是将元素从普通的布局排版中拿走,其他盒子在定位的时候,会当做脱离文档流的元素不存在而进行定位。
而在CSS
中,使用float
和设置position:absolute
都会使得元素脱离文档流。只不过它两的区别是:
使用float
脱离文档流时,其他盒子会无视这个元素,但其他盒子内的文本依然会为这个元素让出位置,环绕在周围。而对于使用position:absolute
脱离文档流的元素,其他盒子与其他盒子内的文本都会无视它。
将窗体自上而下分成一行一行,并在每行中按从左至右的挨次排放元素。
- 要设置某些子元素在一行或者多行内显示,尤其是排列方向一致的情况下,应尽量用
inline-block
。 - 希望若干个元素平行排列,且在父元素中居中排列,此时可以用
inline-block
,且给父元素设text-align: center
。 inline-block
可以用一排a {display: inline-block}
实现横向导航栏,无论是居左的导航栏还是居右的都适用。
对于第一种和第三种情况虽然都可以使用float
来实现,不过inline-block
会比它好一些,原因如下:
- 浮动导致父元素高度塌陷的问题
(望补充)
从缓存的角度上说,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可以通过URL传参上传图片,可以将图片转为base64格式再进行传输,不过由于get请求的参数是有大小限制的,最多不超过4kb。
首先get只能使用URL编码,post支持各种类型的编码。然后get参数在URL上,容易暴露,所以说post相对安全一些。
不一定,我们平常说的幂等只是一种约定,最终幂等还是不幂等取决于服务器怎么处理请求。
问卷网:https://www.wenjuan.com/list
功能主要是一些问卷调查的形式,例如员工培训满意度、投票评选之类的。
魔方网表:http://www.mf999.com/index.html
UI样式较旧,类似老套的系统。
5月25日
面试部门:某业务部门
一面
面试形式:现场面
面试时长:40分钟
面试感悟:面试开始前,看到面试官在面另一个小哥,隐约听到小哥被面试官怼的好惨...所以面试前内心还有点坎坷。但是当真正去面试的时候,面试官好像没有那么凶了,甚至自我感觉眼神中透露着对我的不怀好意...原来他说他有看过我写的文章,说得想点我不知道的考考我...😅
(面试官叫我详细描述一下,然后说自己也学习一下...我:"不不不,您太谦虚了。"
)
-
默认绑定(非严格模式下this指向全局对象, 严格模式下
this
会绑定到undefined
) -
隐式绑定(当函数引用有上下文对象时, 如
obj.foo()
的调用方式,foo
内的this
指向obj
) -
显示绑定(通过
call()
或者apply()
方法直接指定this
的绑定对象, 如foo.call(obj)
) -
new绑定
-
箭头函数绑定(
this
的指向由外层作用域决定的)
-
不能用于构造函数
let Person = (sex) => { this.sex = sex } console.log(new Person('boy'))
结果为:
Uncaught TypeError: Person is not a constructor
-
不要用于事件绑定
例如:
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
指向。 -
原型链上添加方法也不能用箭头函数
构造器内置对象:
- 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
具体可以看:闭包实际场景应用
闭包有三个特性:
-
函数嵌套函数;
-
内部函数使用外部函数的参数和变量;
-
参数和变量不会被垃圾回收机制回收。
所以可以看到它的缺点:
-
常驻内存,增加内存使用量;
-
使用不当造成内存泄漏。
因此我们在使用时需要注意不用的变量要及时的清理掉。
(答案参考:ES6专题—class与模块化(10))
模块可以理解为函数代码块的功能,是封装对象的属性和方法的javascript代码,它可以是某单个文件、变量或者函数。
模块实质上是对业务逻辑分离实现低耦合高内聚,也便于代码管理而不是所有功能代码堆叠在一起,模块真正的魔力所在是仅导出和导入你需要的绑定,而不是将所有的东西都放到一个文件。
在理想状态下我们只需要完成自己部分的核心业务逻辑代码,其他方面的依赖可以通过直接加载被人已经写好模块进行使用即可。
二面
(项目负责人)
面试官:你好,你可以脱下口罩,我们面对面交流一下。
我:...(难道颜值加分吗?那我明天就可以来上班了...)
都是项目相关的问题,然后:
- 之前的团队人员分配情况
- 职业规划
- 期望薪资
- 有什么想问的
HR面
常规问题...和上面问的差不多...
开思也算是深圳的一家比较大的汽车金融公司吧,呆呆这里面的也是3~5年的岗位,其实从一面之后呆呆就有预感对方挺中意我的了,但是在最后HR面的时候他们可能也觉得达不到我的期望薪资,所以也是在我回家之后与我电话沟通,表明如果以后有想继续留在深圳的话可以再考虑一下。应该说虽无缘但也算互相结交了一下吧。