- 正则
- 数据结构
答题思路
具体问题抽象性
抽象问题具体化
HTML
(一)你是如何理解 HTML 语义化的?
- 举例法
HTML 语义化就是使用正确的标签。段落就写 p 标签,标题就写 h1 标签,文章就写article标签,视频就写video标签,等等。
- 阐述法
- 首先以前的后台开发人员使用table布局,但是后期维护很麻烦
- 然后美工人员使用div+css布局,但是不够语义化
- 最后专业的前端会使用正确的标签进行页面开发。该用p就用p;该用h1就用h1
(二)meta viewport 是做什么用的,怎么写?
由于最近手机端的兴起,前端的工作重心逐渐从PC转向移动端,所以出现了meta viewport。
手机浏览器是把页面放在一个虚拟的“窗口”(viewport)中,通常这个虚拟的“窗口”(viewport)比屏幕宽,这样就不用把每个网页挤到很小的窗口中(这样会破坏没有针对手机浏览器优化的网页的布局),用户可以通过平移和缩放来看网页的不同部分。
- 举例法
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">然后逐个解释每个单词的意思。
- width:控制 viewport 的大小,可以指定的一个值,如果 600,或者特殊的值,如 device-width 为设备的宽度(单位为缩放为 100% 时的 CSS 的像素)。
- height:和 width 相对应,指定高度。
- initial-scale:初始缩放比例,也即是当页面第一次 load 的时候缩放比例。
- maximum-scale:允许用户缩放到的最大比例。
- minimum-scale:允许用户缩放到的最小比例。
- user-scalable:用户是否可以手动缩放
(三)你用过哪些 HTML 5 标签?+追问:哪些属性怎么用
1、答题思路:举例法
平时如果只用div写页面你就完了,把你平时用到的html5标签列举出来即可,但是要注意如果这个标签的用法比较复杂,你要先看一下MDN的文档再说这个标签;如果你说出一个标签,却不知道它有哪些API,那么你就会被扣分
2、答案
-
header整个页面的头部
-
main整个页面的主要内容
-
footer整个页面的尾部
-
article文章 大段的内容都可以用
-
section 部分
-
ol+li 有序列表
-
canvas 那canvas如何进行绘制
//先获取
const canvas = document.getElementById('canvas');
//获得canvas的2d上下文
const ctx = canvas.getContext('2d');
//设置笔刷颜色
ctx.fillStyle = 'green';
//设置笔刷的范围
ctx.fillRect(10, 10, 150, 100);
- video 用哪些属性?
//视频地址 自动播放 视频封面
<video src="videofile.ogg" autoplay poster="posterimage.jpg">
抱歉,您的浏览器不支持内嵌视频,不过不用担心,你可以 <a href="videofile.ogg">下载</a>
并用你喜欢的播放器观看!
</video>
<!-- Video with subtitles -->
<video src="foo.ogg">
//加字幕
<track kind="subtitles" src="foo.en.vtt" srclang="en" label="English">
<track kind="subtitles" src="foo.sv.vtt" srclang="sv" label="Svenska">
</video>
(四)H5 是什么?
1、答题思路
,H5表示移动端页面,反正不是HTML5。
CSS
(一)两种盒模型分别说一下。为什么border box更好
(1)content-box(内容盒)
- 内容就是盒子的边界
- content-box width=内容宽度
(2)border-box (边框盒)
- 边框才是盒子的边界
- border-box width=内容宽度+padding+border
- border-box更好用 写起来更方便
(一)如何垂直居中?
如果 .parent 的 height 不写,你只需要 padding: 10px 0; 就能将 .child 垂直居中;
如果 .parent 的 height 写死了,就很难把 .child 居中,以下是垂直居中的方法。
忠告:能不写 height 就千万别写 height。
-
table自带功能,里面的(tr)td垂直居中
-
装成table。用display: table和display: table-cell;
-
给孩子加一个哥哥和弟弟,他们100% 高度并且 inline block
或者给爸爸加两个伪元素before和after,他们100% 高度并且 inline block
-
父元素相对定位,子元素绝对定位。子元素top50%,margin-top:-50%
-
父元素相对定位,子元素绝对定位。子元素top50%,transform: translate(-50%,-50%)y轴方向移动-50%
-
父元素相对定位,子元素绝对定位。子元素 margin: auto; top: 0; bottom: 0; left: 0; right: 0;
-
使用flex布局, justify-content: center; align-items: center;
(二)flex 怎么用,常用属性有哪些?
(三)BFC 是什么?
1、答题思路
举例!
背 ,MDN 写了。 但是不用全部背下来,面试官只知道其中几个:
2、答案
- BFC中文翻译是块级格式化上下文,BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
- BFC 触发条件
- 浮动元素(元素的 float 不是 none)
- 绝对定位元素(元素的 position 为 absolute 或 fixed)
- 行内块元素
- overflow 值不为 visible 的块元素
- 弹性元素(display为 flex 或 inline-flex元素的直接子元素)
- 例子:我们如果给一个div写一个
overflow:hidden,那么这个div里面的浮动元素就会被他包裹起来。这就是BFC
(四)CSS 选择器优先级
- 先回答
- 越具体优先级越高
- 同样优先级写在后面的覆盖写在前面的
- !important 优先级最高,但是要少用
- 面试官脸色不对,在回答错误答案
(五)清除浮动说一下
.clearfix加到容器上,里面的子元素的浮动就被清除了
.clearfix:after{
content: '';
display: block; /*或者 table*/
clear: both;
}
.clearfix{
zoom: 1; /* IE 兼容*/
}
原生 JS
(一☆)ES6语法知道哪些,分别怎么用?
1、解题思路
举例法:let const 箭头函数 Promise 展开操作符 默认参数 import export
2、答题
(二☆)Promise、Promise.all、Promise.race 分别怎么用?
1、背代码 Promise 用法
function fn(){
return new Promise((resolve, reject)=>{
成功时调用 resolve(数据)
失败时调用 reject(错误)
})
}
fn().then(success, fail).then(success2, fail2)
2、背代码 Promise.all 用法
Promise.all([promise1, promise2]).then(success1, fail1)
promise1和promise2都成功才会调用success1
3、背代码 Promise.race 用法
Promise.race([promise1, promise2]).then(success1, fail1)
promise1和promise2只要有一个成功就会调用success1
(三☆)手写函数防抖和函数节流
1、节流 :函数节流就相当于技能冷却,执行一次后在一段时间内不能在执行了。
伪代码示例
function fn() {} //技能
var cd = false //冷却状态:不在
button.onclick = function() { //点击发送技能按钮
if (cd) { //在冷却状态就啥也不做
//
}else { //不在冷却状态
fn () //就发送技能
cd = true //发送好就得冷却了
var timerId = setTimeout ( ()=>{cd = false} , 3000) //三秒后才能解除冷却状态
}
}
function throttle(fn, delay){
let canUse = true
return function(){
if(canUse){
fn.apply(this, arguments)
canUse = false
setTimeout(()=>canUse = true, delay)
}
}
}
const throttled = throttle(()=>console.log('hi'))
throttled()
throttled()
注意,有些地方认为节流函数不是立刻执行的,而是在冷却时间末尾执行的(相当于施法有吟唱时间),那样说也是对的。
2、防抖:一段时间会等,然后带着一起做了
- 第一次来外卖订单了,设置闹钟提醒自己:五秒后送第一次外卖然后关掉闹钟。
- 如果五秒期间又来了一个外卖订单,就把之前的闹钟关掉,在设置个闹钟提醒自己:五秒后送第一次和第二次的外卖然后关掉闹钟。
- 五秒钟到了,没有新的外卖订单了,那就去送第一次和第二次的外卖了,把闹钟关掉。
拖动窗口,等全都拖动完了在干些事情。
var timerId = null
button. onclick = function() { //来外卖订单了
if (timerId) { window.clearTimeout (timerId) } //如果有闹钟就就关掉闹钟
timerId = setTimeout ( ()=>{ //设置闹钟:五秒后送外卖然后关掉闹钟
fn ()
timerId = null
} , 5000)
}
function debounce(fn, delay){
let timerId = null
return function(){
const context = this
if(timerId){window.clearTimeout(timerId)}
timerId = setTimeout(()=>{
fn.apply(context, arguments)
timerId = null
},delay)
}
}
const debounced = debounce(()=>console.log('hi'))
debounced()
debounced()
(四☆)手写AJAX
背代码,完整版
var request = new XMLHttpRequest()
request.open('GET', '/a/b/c?name=ff', true);
request.onreadystatechange = function () { //监听onreadystatechange
if(request.readyState === 4 && request.status === 200) { //这个函数会检查readyState和status,如果这样就请求成功
console.log(request.responseText);
}};
request.send();
背代码,简化版
var request = new XMLHttpRequest()
request.open('GET', '/a/b/c?name=ff', true)
request.onload = ()=> console.log(request.responseText)
request.send()
(五☆)这段代码里的 this 是什么?
背代码
fn()
this => window/global
obj.fn()
this => obj
fn.call(xx)
this => xx
fn.apply(xx)
this => xx
fn.bind(xx)
this => xx
new Fn()
this => 新的对象
fn = ()=> {}
this => 外面的 this
看调用 《this 的值到底是什么?一次说清楚》
(六☆)必考:闭包/立即执行函数是什么?
定义:如果一个函数用到了定义在该函数作用域之外的变量,那么这个函数加这个变量,就叫做闭包
作用:闭包常常用来「间接访问一个变量」。换句话说,「隐藏一个变量」 因为我们不想这个变量被谁都可以随意赋值改变,也就是这个变量不能是全局变量,只能是局部变量。 那之后该怎么对这个局部变量操作?写一个函数,这个函数可以对这个局部变量进行操作。这时你就会发现,自己写了个闭包(这个局部变量+这个函数)
定义:首先声明一个匿名函数 function(){alert('我是匿名函数')}。 然后在匿名函数后面接一对括号 (),调用这个匿名函数。
作用:创建一个独立的作用域。这个作用域里面的变量,外面访问不到(即避免「变量污染」)。
(七☆)什么是 JSONP,什么是 CORS,什么是跨域?
(八)async/await 怎么用,如何捕获异常?
我的博客 try catch 记住MDN例子 阮一峰的书讲了
(九)如何实现深拷贝?
- 进行拷贝操作时,须加以区分引用类型和基本数据类型的值,且拷贝引用类型的值时,不是简简单单的复制其保存在栈中的指针,(即浅拷贝)而是应该复制其存储在堆中的数据。
- 必须提到的要点:
- 递归
- 判断类型(不同的类型有不同的拷贝方法)
- 检查环(也叫循环引用) 如果一个对象引用了自己,再深拷贝递归的时候就可能出不来。所以要检查循环引用
- 需要忽略原型:不可能拷贝原型
-
解题思路 (1)方法一:分别对数组、对象、基本数据类型值进行判断、考虑,使用递归的方式复制值 (2)方法二:利用了 JSON.parse()、JSON.stringify() 这两个方法实现深拷贝
-
代码
/* 方法一 */
function deepClone (val) {
var res;
if (val && Array.isArray(val)) {
res = [];
val.forEach(function (item) {
res.push(deepClone(item));
});
} else if (typeof val === 'object' && val !== null) {
res = {};
for (let key of Object.keys(val)) {
res[key] = deepClone(val[key]);
}
} else {
res = val;
}
return res;
}
/* 方法二 */
function deepClone (val) {
if (typeof val === 'undefined') {
return;
}
var temp = JSON.stringify(val);
return JSON.parse(temp);
}
/* 测试代码 */
// 深拷贝基本数据类型值
var val = 'hello world';
console.log('基本数据类型值', deepClone(val));
// 深拷贝数组
var arr = [9, 'cat', true, null, undefined, [1, 2], {foo: 'bar'}];
console.log('数组', deepClone(arr));
// 深拷贝对象
var obj = {
name: 'wang',
age: 18,
isStudent: true,
nums: [3, 4],
score: {
math: 95
}
};
console.log('对象', deepClone(obj));
(十)如何用正则实现 trim()?
背代码
String.prototype.trim = function(){
return this.replace(/^\s+|\s+$/g, '') //把前面和后面的空格去掉
}
//或者
function trim(string){
return string.replace(/^\s+|\s+$/g, '')
}
(十一)不用 class 如何实现继承?用 class 又如何实现?
背代码,不用 class 这样实现
//先写一个Animal类
function Animal(color){
this.color = color
}
Animal.prototype.move = function(){} // 动物可以动
//再写一个Dog类,Dog类继承了Animal类
function Dog(color, name){
Animal.call(this, color) // 或者 Animal.apply(this, arguments)
this.name = name
}
// 下面三行实现让Dog的原型继承Animal的原型
// Dog.prototype.__proto__ = Animal.prototype
function temp(){}
temp.prototye = Animal.prototype
Dog.prototype = new temp()
Dog.prototype.constuctor = Dog // 这行看不懂就算了,面试官也不问
Dog.prototype.say = function(){ console.log('汪')}
var dog = new Dog('黄色','阿黄')
背代码,用 class 就简单了
class Animal{
constructor(color){
this.color = color
}
move(){}
}
class Dog extends Animal{
constructor(color, name){
super(color)
this.name = name
}
say(){}
}
(十二)如何实现数组去重?
- 使用hash:利用对象的思想。让数组里的元素成为对象里的属性名并且给一个属性值1。如果对象里没有这个属性的话就会返回undefined 利用这个原理当返回的是undefined时 让其放入数组 然后在给这个属性赋值
function norepeat3(arr) {
var obj = {};
var newArr = [];
for(var i = 0; i < arr.length; i++) {
if(obj[arr[i]] == undefined) {
newArr.push(arr[i]);
obj[arr[i]] = 1;
}
}
return newArr;
}
-
[... new Set(array)]:去搜 先把数组放到set里就会变成没有重复元素的集合,再把这个集合变成数组。 -
创建一个新数组 通过indexOf方判断当前元素在数组中的索引如果与循环的下标相等则添加到新数组中
var arr = [1,23,1,1,1,3,23,5,6,7,9,9,8,5,5,5];
console.log(arr);
function norepeat(arr){
var temp=[];
for(var i=0;i<arr.length;i++){
if(arr.indexOf(arr[i]) == i){
temp.push(arr[i]);
}
}
return temp;
}
var arr2=norepeat(arr);
console.log(arr2); //[1, 23, 3, 5, 6, 7, 9, 8]
(十三)放弃:== 相关题目(反着答)
我从来不记==,我只用===
不要背,记不住,太复杂且没有规律
(十四☆)送命题(觉得你厉害才会问你):手写一个 Promise
提前写一遍,放在博客里,参考 别人的
DOM
☆(一)事件委托
1、错误版(但是可能能过)(这是我半年前的写法):bug 在于,如果用户点击的是 li 里面的 span,就没法触发 fn,这显然不对。
<ul>
<li>
<span></span>
</li>
</ul>
ul.addEventListener('click', function(e){
if(e.target.tagName.toLowerCase() === 'li'){
fn() // 执行某个函数
}
})
2、高级版: 一直判断父元素,一直到底ul 思路是点击 span 后,递归遍历 span 的祖先元素看其中有没有 ul 里面的 li。
function delegate(element, eventType, selector, fn) {
element.addEventListener(eventType, e => {
let el = e.target
while (!el.matches(selector)) {
if (element === el) {
el = null
break
}
el = el.parentNode
}
el && fn.call(el, e, el)
})
return element
}
曾考:用 mouse 事件写一个可拖曳的 div 参考代码
var dragging = false //拖拽状态:不在
var position = null //坐标
xxx.addEventListener('mousedown',function(e){ //当用户按下鼠标
dragging = true //在拖拽状态了
position = [e.clientX, e.clientY] //获取坐标
})
//应该监听document的鼠标移动事件,如果xxx的鼠标移动事件,那鼠标移动太快,就会掉下来
document.addEventListener('mousemove', function(e){ //当用户移动鼠标
//那必须是按着移动鼠标
if(dragging === false){return}
console.log('hi')
//获取现在的坐标
const x = e.clientX
const y = e.clientY
//那么位移就是现在的坐标减去之前的坐标
const deltaX = x - position[0]
const deltaY = y - position[1]
//那就知道咋移动了。
//坐标是坐标,但是移动的话是css样式!是位置!应该用left和top定位置
const left = parseInt(xxx.style.left || 0)
const top = parseInt(xxx.style.top || 0)
xxx.style.left = left + deltaX + 'px'
xxx.style.top = top + deltaY + 'px'
position = [x, y]
})
document.addEventListener('mouseup', function(e){
dragging = false
})
HTTP
☆(一)HTTP 状态码知道哪些?分别什么意思?
- 100
- 2xx 表示成功
- 3xx 表示没什么问题,但是需要进一步操作
- 301 302 303
- 304 Not Modified 未修改。所请求的资源未修改,
- 4xx 表示浏览器方面出错
- 400 Bad Request 客户端请求的语法错误,服务器无法理解
- 401 Unauthorized 请求要求用户的身份认证
- 402 Payment Required 保留,将来使用
- 403 Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求
- 404 Not Found 服务器无法根据客户端的请求找到资源(网页)。也就是网址输错了。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
- 405 Method Not Allowed 客户端请求中的方法被禁止
- 414 Request-URI Too Large 请求的URI过长(URI通常为网址),服务器无法处理
- 5xx 表示服务器方面出错
- 502 Bad Gateway 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
☆(二)HTTP 缓存有哪几种?
需要详细的了解 ETag、CacheControl、Expires 的异同 参考
答题要点:
- ETag 是通过对比浏览器和服务器资源的特征值(如MD5)来决定是否要发送文件内容,如果一样就只发送 304(not modified)
- 有请求的
- Expires 是设置过期时间(绝对时间),但是如果用户的本地时间错乱了,可能会有问题
- 在某个时间点后过期
- CacheControl: max-age=3600 是设置过期时长(相对时间),跟本地时间无关。
- 多少秒内过期
- 无请求的,从本地缓存,从浏览器的
☆(二)GET和POST的区别
- 先回答错解,但是能过面试
- GET在浏览器回退时是无害的,而POST会再次提交请求。
- GET产生的URL地址可以被加入收藏栏,而POST不可以。
- GET请求会被浏览器主动cache,而POST不会,除非手动设置。
- GET请求只能进行url编码,而POST支持多种编码方式。
- GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
- GET请求在URL中传送的参数是有长度限制的,而POST么有。
- 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
- GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
- GET参数通过URL传递,POST放在Request body中。
- GET只需要一个报文,POST需要两个以上
- GET幂等,POST不幂等(获取一个东西两次不会改变数据库,提交两次可能会改变数据库)
- 表情不对,回答正解 就一个区别:语义——GET 用于获取资源,POST 用于提交资源。 想装逼请参考 zhuanlan.zhihu.com/p/22536382
☆(三)Cookie V.S. LocalStorage V.S. SessionStorage V.S. Session
1. Cookie V.S. Session
- Cookie是服务器发给浏览器的一张票,浏览器在每次访问对应域名的时候都要把这张票带着
- Session是会话,表示浏览器与服务器一段时间内的会话
- Cookie 存在浏览器的文件里,Session 存在服务器的文件里
- Session 是基于 Cookie 实现的,具体做法就是把 SessionID 存在 Cookie 里
2. Cookie V.S. LocalStorage
- Cookie是用来存用户信息的(比如SessionID),LocalStorage存不重要的数据的
- 主要区别是 Cookie 会被发送到服务器,而 LocalStorage 不会
- Cookie 一般最大 4k,LocalStorage 可以用 5Mb 甚至 10Mb(各浏览器不同)
3. LocalStorage V.S. SessionStorage
- LocalStorage 一般不会自动过期(除非用户手动清除)
- 而 SessionStorage 在Session结束时过期(如关闭浏览器)
☆(四)HTTP1和HTTP2的区别
多路复用 HTTP2强制开启HTTPS
VUE
☆(一)watch 和 computed 和 methods 区别是什么?
1. 思路
先翻译单词,再阐述作用,最后强行找不同。
要点:
computed 和 methods 相比,最大区别是
- computed 有缓存:如果 computed 属性依赖的属性没有变化,那么 computed 属性就不会重新计算。
- methods 则是看到一次计算一次。
watch 和 computed 相比,
- computed 是计算出一个属性(废话)
- 而 watch 则可能是做别的事情(如上报数据)
☆(二)Vue 有哪些生命周期钩子函数?分别有什么用?
- beforeCreate在创建组件之前做些事情
- created -- 实例出现在内存中后触发,做一些创建时的初始化
- mounted-- 实例出现在页面中(挂载了)后触发,可以做数据请求
- updated -- 实例更新了后触发,用来做更新之后的事情,上报一些数据变化
- destroyed -- 实例从页面和内存中消亡了后触发
☆(三)Vue 如何实现组件间通信?
- 父子组件:使用
v-on和$emit一个监听,一个触发来通过事件通信 - 爷孙组件:使用两次 v-on 通过爷爷爸爸通信,爸爸儿子通信实现爷孙通信
- 任意组件:使用
eventBus = new Vue()来通信,eventBus.$on和eventBus.$emit是主要API - 任意组件:使用 Vuex 通信
☆(二)Vue 数据响应式怎么做到的?
先看我的博客理解 要点
- 使用 Object.defineProperty 把这些属性全部转为 getter/setter
- Vue 不能检测到对象属性的添加或删除,解决方法是手动调用 Vue.set 或者 this.$set
☆(二)Vue.set 是做什么用的?
Vue 不能检测到对象属性的添加或删除,解决方法是手动调用 Vue.set 或者 this.$set
☆(二)Vuex 你怎么用的?
- Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式
- 有5个核心概念的名字:State/Getter/Mutation/Action/Module
- State:单一状态树,对象,唯一数据源,数据写在里面。
- Getter:(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。Getter 会暴露为 store.getters 对象,所以
store.getters.doneTodos可以以属性形式使用它。 - Mutation:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。用
store.commit('increment')方法调用mutation的回调函数。 - action:
-
Action 类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态。 Action 可以包含任意异步操作。
-
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
actions: {
increment (context) {
context.commit('increment')
}
}
☆(二)VueRouter 你怎么用的?
-
Vue Router 是 Vue.js 官方的路由管理器。
-
说出核心概念的名字和作用:重定向和别名/History 模式/导航守卫/路由懒加载(
import('组件路径')) -
重定向和别名: “重定向”的意思是,当用户访问 /a时,URL 将会被替换成 /b,然后匹配路由为 /b,用
redirect实现。那么“别名”又是什么呢?/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样。用alias来实现
//重定向
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
//别名
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})
- 导航守卫
- 说出常用 API:
router-link/router-view/this.$router.push/this.$router.replace/this.$route.params
this.$router.push('/user-admin')
this.$route.params
☆(二)路由守卫是什么?
看官方文档的例子,背里面的关键的话 看
TypeScript
(一)never 类型是什么?
不应该出现的类型
☆(二)TypeScript 比起 JavaScript 有什么优点?
提供了类型约束,因此更可控、更容易重构、更适合大型项目、更容易维护
Webpack
☆(一)有哪些常见 loader 和 plugin,你用过哪些?
☆(三)loader 和 plugin 的区别是什么?
☆(四)如何按需加载代码?
import('组件路径')
☆(五)如何提高构建速度?
- 多入口情况下,使用CommonsChunkPlugin来提取公共代码
- 通过externals配置来提取常用库
- 利用DllPlugin和DllReferencePlugin预编译资源模块 通过DllPlugin来对那些我们引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。
- 使用Happypack 实现多线程加速编译
☆(六)转义出的文件过大怎么办?
单页应用的按需加载 别人的博客
安全
☆(一)T什么是 XSS?如何预防?
比较复杂,看我的文章
☆(二)T什么是 CSRF?如何预防?
比较复杂,看若愚的文章
开放问题
☆(一)你遇到最难的问题是怎样的?
回答一
- 首先我使用
svg-sprite-loader来引入icon,把所有的icon变成<svg>标签里作为一个<symbol>标签。 - 之后我发现了一个bug:webstorm永远无法
@import一个scss,总是有红波浪线报错。 - 那这到底为什么出现这个bug呢,于是我花了好几个小时去找原因,终于发现是因为webstorm在遇到
svg-sprite-loader发现,这个svg-sprite-loader实现有问题,我没有办法解决这个loader,那我webpack的配置也解析不了了。 - 那我就想着去看看
svg-sprite-loader的github issue列表,发现issue里面也有人说了这个问题,然后我就去看github pr,发现有人贡献了代码,既然有人都贡献代码了,svg-sprite-loader官方还是没有合并到源代码了。所以我只能自己解决。于是我把别人的解决代码fork了一份,fork之后把别人的解决代码merge到了我的源代码里,然后使用npm publish把我这个版本分布到npm上然后自己再去下载自己的改过的版本来用,而不是原始的官方发的版本。
回答二
还有个我们无法用css给svg变色。我就去看了svg源文件,原来里面有个fill属性,让svg默认带了颜色,而且我们还改不了。所以在网上找了一个插件svgo-loader可以解决这个问题。
回答三
我们曾经对于创建一个东西成功返回什么存在一些疑问。一开始觉得只要返回true和false;但是失败的情况有很多种,所以又想着能不能用数字表示。于是列了一个数组列表定义了不同的数组分别代表不同的错误。最后发现数字还是容易忘记。最后直接返回字符串表示错误。那又想着中文也是字符串啊,所以干脆返回中文表示错误得了。
☆(二) 最近在关注什么新技术
书、博客、推特、知乎,不要说 CSDN、百度。
☆(三) 有没有看什么源码,看了后有什么记忆深刻的地方,有什么收获
看过源码说源码,推荐看 underscore.js 的源码 没看过源码就说同事的代码,代码烂就说哪里烂,代码好就说哪里好 收获:命名规范、设计模式
刁钻题目
- 代码
[1,2,3].map(parseInt)
// [1,NaN,NaN]
其实会多传一个参数。把1当做0进制,那就是没有进制。把2当做1进制,1进制又没有2.所以NaN。把3当做2进制,2进制又没有3.所以NaN。
- 代码
var a = {name: 'a'}
a.x = a = {}
问 a.x 是多少? 答案 undefined
- (a ==1 && a== 2 && a==3) 可能为 true 吗?
(1)利用 == 会调用 valueOf() 的特性
var a = {
value: 1,
valueOf(){
return this.value++
}
}
a ==1 && a== 2 && a==3 // true
(2)利用 a 会读取 window.a 的特性
var value = 1;
Object.defineProperty(window, 'a', {
get(){
return value++;
}
})
a ==1 && a== 2 && a==3 // true
// 或者
a ===1 && a=== 2 && a===3 // true
超纲题
(一)JS 垃圾回收机制
看图讲解 javascript.info/garbage-col…
1、什么是垃圾
- 没有被引用的对象很可能是垃圾。
- 之所以说可能是因为有特例,那就是如果三个对象互相被引用,也只是他们仨之间互相被引用,形成一个环一个孤岛,虽然又莹莹这样也算是垃圾
2、如何捡垃圾(遍历和计数,只是不同的算法而已)
(1)标记清除算法
- 从全局对象开始,把它引用的所有对象都标记一下。
- 遍历所有被标记的对象,去标记他们引用的对象。
- 以此类推,直到找不到新的可以被标记的对象。这样,标记过程就算结束了。
- 可以开始清除了:把所有没有标记过的对象都清除掉。
(2)引用计数 Reference Counting
- 引用计数,就是记录每个对象被引用的次数,每次新建对象、赋值引用和删除引用的同时更新计数器,如果计数器值为0则直接回收内存。 * 很明显,引用计数最大的优势是暂停时间短