#前端面试题
2019各个公司前端面试题总汇
http相关
1.https与http区别(百度)
1 |
|
2.http请求过程及拿到响应后的渲染过程?(苏宁)
1 |
|
3.http缓存机制。(百度、滴滴都有问)
点击查看详细浏览器缓存技术,面试难不倒1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30强缓存:
响应报文头:
catch-control:max-age=212121; 最长生效时常。
expires:服务器绝对时间
客户端发请求前,会用当前客户端时间与 (上次请求时间+max-age)进行比较,命中则走缓存。
expires是一个绝对时间,采用的是服务器的。客户端发送请求前,用客户端时间与这个时间进行比较,命中则走缓存。
max-age优先级比expires高,并且更加靠谱;
弱缓存(协商缓存):
响应报文头:
last-modified:最后一次修改时间
Etag:当前请求的资源生成的一个唯一标识,只要资源不一样这个串就不一样
请求报文头:
if-modified-since:缓存中文件的最后修改时间
if-None-Match:缓存中文件的etag
客户端发出请求,携带if-modified-since,if-None-Match字段,服务器收到请求 与当前文件的最后修改时间和
重新生成etag进行比较,命中则返回304;
如果命中if-none-match,返回304的响应头中,会携带服务器最新生成的etag;
如果命中if-modified-since则304响应头中不会携带last-modified,因为最后修改时间没有改变。
注意:etag优先级比 last-modified要高。但是各有优缺点;
分布式系统里多台机器文件的last-modified必须保持一致,要不负载均衡到不同机器导致对比失败。
分布式系统计量关闭掉etag,因为每台服务器生成的etag都不一样。
使用:协商缓存需要配合强缓存使用,
4.http get和post区别?(马蜂窝)
1 |
|
5.http状态码(百度,水滴筹,今日头条)1
2
3
4
5
6
7
8
9200 成功
5** 服务器
304 缓存
301 永久重定向
302 临时重定向
400 Bad Request,通用的客户端错误状态,当其他4XX响应代码不适用时,就采用400
401 权限不够访问该资源。
404 not found 当客户端所请求的URI不对应于任何资源时,发送此响应代码,找不到资源。
403 Forbidden 服务器拒绝访问
6.列举你所知道的请求头中的字段和响应头中的字段(美团,小米)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33请求头
Accept: application/json, text/javascript, */*; q=0.01 //告诉服务器,客户端支持的数据类型
Content-Type: application/x-www-form-urlencoded; charset=UTF-8 //告诉服务器,发送的数据类型
Origin: http://dev-mrytuan.chuchujie.com //源
Referer: http://dev-mrytuan.chuchujie.com/ //源页面
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36
// 浏览器信息
cookie:'', //cookie信息
if-modified-since:xxxxx //上次请求响应头返回的最后修改时间
if-none-match:xxx //上次请求响应头返回的etag信息
options请求
Access-Control-Request-Method:,用来列出浏览器的CORS请求会用到哪些HTTP方法
Access-Control-Request-Headers:浏览器CORS请求会额外发送的头信息字段
响应头:
Access-Contronl-Allow-origin:* ,//允许源访问
access-control-allow-methods:get ,post //允许请求方式
access-control-allow-headers:'name' //允许携带的请求头
access-control-expose-headers:'name' //允许浏览器读取的头信息
access-control-allow-credentials:true //表示服务器允许浏览器携带cookie发送
content-type: application/json 响应数据格式
date: Tue, 02 Apr 2019 16:30:02 GMT //服务器时间
catch-control:max-age=36000 //强缓存最强生效时长
expires:xx-xx-xx //服务器失效绝对时间
last-modified:xxx-xxx-xx //服务器最后修改时间
etag:xxxxxx //服务器资源唯一标示符
options
Access-Control-Max-Age:秒, 本次预检请求的有效期
7.复杂请求的与简单请求的区别
阮一峰详解
复杂请求 会比 简单请求多一个options请求(预检查请求)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49cors请求分两种:简单请求 和非简单请求
==============================================
简单请求:
浏览器请求头中会默认增加origin字段,表示来自哪个源
服务器接收请求后,根据指定的源作出回应。
响应头中:Access-Control-Allow-Origin:* || domain
Access-Control-Allow-Credentials:true, //表示是否允许跨域携带cookie。(后续进行讲解)
Access-Control-Expose-Headers:xxx //指定浏览器可以获取到的 自定义响应头
=============================================
非简单请求:
出现条件:请求方法是PUT或DELETE,
Content-Type字段的类型是application/json
增加额外的header字段;
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"options请求(preflight)
浏览器发出options请求头:
origin:源
Access-Control-Request-Method:,用来列出浏览器的CORS请求会用到哪些HTTP方法
Access-Control-Request-Headers:浏览器CORS请求会额外发送的头信息字段
服务器返回options响应头:
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT 服务器支持的所有跨域请求的方法
Access-Control-Allow-Headers: X-Custom-Header 表示服务器支持的所有头信息字段
Access-Control-Max-Age:秒, 本次预检请求的有效期
服务器如果否定了options请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。浏览器就
会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。
====================================
withCredentials属性
cors请求不发送cooki,如果要发送cookie到服务器,一方面要服务器同意。
Access-Control-Allow-Origin:不能设置*号,必须指定和明确的、与请求网页一致的域名
Access-Control-Allow-credentials:true;//这个只能设为true;如果不需要cookie,则删除该字段即可。
另一方面,浏览器ajax需要打开 withCredentials
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
特点:Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,
且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。
a.com 请求b.com ,设置了此属性,携带的cookie也只能是b.com 这个域下的cookie;
8.http2 和http1.1的区别
1 | [区别详解](https://juejin.im/post/5a4dfb2ef265da43305ee2d0) |
js基础相关
1.js继承?原型链查找原理。(蚂蚁金服)1
2
3
4
5
6
7
8function inheritPrototype(subType, superType){
// 继承父类的原型
var prototype = Object.create(superType.prototype);
// 重写被污染的construct
prototype.constructor = subType;
// 重写子类的原型
subType.prototype = Object.assign(prototype, subType.prototype);
}
2.es6 class继承如何实现?(蚂蚁金服)
3.var let const 区别 (基本每个公司都问)
4.promise的特点,使用及原理。(基本每个公司都问)
手写promise1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
特点:then链式调用
catch捕获
all方法
race方法
promise对象三种状态 pendding,fulfilled,rejected;
状态改变,只能改一次;
promise对象已经是成功状态或是失败状态时,都可以继续通过then传入函数,会通过当前的状态,来决定执行成功还失败,并且把结果或是错误传给相应的函数。
原理:状态机 + then方法返回新的promise2对象实现链式调用;
使用
new promise((resolve,reject)=>{
setTimeout(()=>{
resolve('成功')
})
}).then(()=>{},()=>{}).catch(e=>{
})
Promise.all([promise1,promise2]).then();
Promise.race([promise1,promise2]).then();
Promise.resolve(1)
Promise.reject(1)
Promise.deferred();
5.generator函数的使用及原理 (蚂蚁金服)
1 |
|
6.es6 解构赋值和数组的操作方法
7.讲一下js循环事件池event loop,微任务和宏任务 (boss直聘、小米,苏宁)
浏览器event loop 和node event-loop
浏览器的event loop1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17js执行会存在一个执行栈,当前执行的同步代码都会放到执行栈中执行(宏任务);
同步代码执行过程中,如果遇到异步代码(settIimeout、promise等),会将他们挂起,继续执行同步代码;
同步代码执行完毕后,执行栈清空,会去查看微任务队列是否有可执行函数,然后拿到执行栈执行;
当微任务队列执行完成后, 会去宏任务取一个任务放到执行栈执行,执行完成后再去查看微任务是不是有可执行函数,
有的话取到执行栈执行;如此循环下去,就是event loop;
挂起的异步代码,可执行后,会放到异步任务队列;异步任务队列分为两种队列,一种是微任务(promise,process.nextTick)
一种是宏任务(setTimeout等);
微任务包括 process.nextTick ,promise ,MutationObserver。
宏任务包括 script , setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering。
过程:
执行完主执行线程中的任务。
取出Microtask Queue中任务执行直到清空。
取出Macrotask Queue中一个任务执行。
取出Microtask Queue中任务执行直到清空。
重复3和4。
node环境 event loop 是区分阶段的;
1 |
|
1 |
|
8.js的垃圾回收处理机制的原理?(马蜂窝,美团)
(垃圾处理机制)[https://www.cnblogs.com/guoyongfeng/p/3907994.html]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25不再用到的内存(一个不用到的值)没有及时释放,就叫做内存泄漏(memory leak)。
垃圾回收机制:引用计数
最常使用的方法叫做"引用计数"(reference counting):语言引擎有一张"引用表",
保存了内存里面所有的资源(通常是各种值)的引用次数。如果一个值的引用次数是0,就
表示这个值不再用到了,因此可以将这块内存释放
JavaScript的解释器可以检测到何时程序不再使用一个对象了,当他确定了一个对象是无用的时候,
他就知道不再需要这个对象,可以把它所占用的内存释放掉了
var arr = [1,2,3,4]
数组[1, 2, 3, 4]是一个值,会占用内存。变量arr是仅有的对这个值的引用,因此引用次数
为1。尽管后面的代码没有用到arr,它还是会持续占用内存;就会造成内存泄漏
arr = null;
如果增加一行代码,解除arr对[1, 2, 3, 4]引用,这块内存就可以被垃圾回收机制释放了。
垃圾回收机制:标记清除:
js中最常用的垃圾回收方式就是标记清除。
当变量进入环境时,例如,在一个函数中声明一个变量,就将这个变量标记为"进入环境",
从逻辑上讲,永远不能释放进入环境变量所占用的内存,因为只要执行流进入相应的环境,
就可能会用到它们。而当变量离开环境时,则将其标记为"离开环境"
9.localstorage、sessionStorage 、cookie区别?(水滴)
二级域名是否可以取到一级域名的数据?
cookie是可以的 ,只要在一个主域即可
localstorage不可以,他是跨站点的,只要域名变了就取不到了。
10.什么是闭包,说下项目中应用场景?(水滴)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39闭包就是能够读取其他函数内部变量的函数,或者子函数在外调用,子函数所在的父函数的作用域不会被释放
一个闭包就是当一个函数返回时,一个没有释放资源的栈区。
场景:
1.可以实现公有变量,函数外或在其他函数中访问某一函数内部的参数
function add() {
var num = 0;
function demo(){
num++;
console.log(num);
}
return demo;
}
var test = add();
test();//1
test();//2
2.为节点循环绑定click事件,在事件函数中使用当次循环的值或节点,而不是最后一次循环的值或节点
3.计数器
function checkCount(target){
let count = 0;
let callbacks = [];
return function(fn){
count++;
if(count == target){
callbacks.forEach(fn=>fn())
}else{
callbacks.push(fn)
}
}
}
let d = checkCount(3);
d(fn);
d(fn);
d(n)
4.函数节流和防抖
11.ajax工作原理?
1 |
|
12.js事件代理?(传智播客,58,瓜子二手车)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
事件委托指的是,不在事件的发生地(直接dom)上设置监听函数,而是在其父元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发,通过判断事件发生元素DOM的类型,来做出不同的响应。
举例:最经典的就是ul和li标签的事件监听,比如我们在添加事件时候,采用事件委托机制,不会在li标签上直接添加,而是在ul父元素上添加。
好处:比较合适动态元素的绑定,新添加的子元素也会有监听函数,也可以有事件触发机制。
jquery:
$("#nav").on('click',function(e){
$(e.target) =>是每个点击的项目
$(this) =>nav
})
$('#nav').on('click','li',function(){
$(this) =>点击的每一项
});
扩展 jsdom事件流阶段:
捕获阶段->目标阶段->冒泡阶段
外内 -> 当前 ->冒泡
如何执行冒泡 在捕获?
在捕获阶段的函数暂缓执行,等冒泡执行完后在执行捕获函数。
13.js中this是如何工作的(基本都问)
14.怎么判断引用数据类型?(为什么能用Object.prototype.toString.call()去判断?)(水滴)
1 | 1.instanceof 判断不准确 |
15.js变量提升和作用域查找?1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26console.log(type a) //undefined 变量提升
console.log(type b) //err:b is not defined ,后续代码不执行
console.log(type c)
var a = function(){return true}; //变量提升
window.b = function(){ //给window创建属性,不存在变量提升
return true;
}
console.log(a() && b() && c())
function c(){return c}
var o = {
a:1,
f(){
return this.a;
}
}
console.log(o.f()); //1
var o1 = o;
console.log(o1.f());//1
var o2 = o.f;
console.log(o2()); //undefined =>this是window,window.a undefined;
o.a= 5;
console.log(o1.f());//5 堆内存 和 栈内存
var o3 = {a:5};
console.log(o.f.call(o3)) //5
16.call apply bind 区别?如何实现?(基本都问)
17.实现跨域的方法?说一下JSOP的原理?(水滴)
(nginx服务器反向代理)[https://segmentfault.com/a/1190000012859206]
(postMessage + iframe实现页面跨域)[http://www.cnblogs.com/zhouzme/p/5758386.html]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66jsonp;
利用script标签src属性不受同源策略影响,可实现跨域;
通过与api方,对接好 回调参数字段~ cb=fn,fn在前端是一个全局的方法。后端返回script代码"fn({name:1})",浏览器会执行;
前端代码:
function b(data) {
console.log(data)
}
let script = document.createElement('script')
script.src = "http://localhost:3002/jsonp?cb=b";
document.body.appendChild(script)
node模拟后端:
http.createServer((req, res) => {
let {pathname, query} = url.parse(req.url, true);
if (pathname == '/jsonp') {
let school = JSON.stringify({name:'zfpx'});
// res.end(`var a = ${school}`) //在全局就可以获取到 变量a
// res.end(`b(${school})`)
res.end(`${query.cb}(${school})`)
return ;
}
}).listen(port, () => {
console.log(`${port} 服务器启动`)
});
CORS
服务端响应:
res.setHeader('Access-Control-Allow-Origin',"*");//设置允许访问的源
res.setHeader('Access-Control-Allow-Methods','GET,POST,PUT,DELETE,OPTIONS')
res.setHeader('Access-Control-Allow-headers','name')
nginx服务器反向代理
//前端a.com 请求 后端api b.com/login 会出现跨域,
//在a.com所在服务器启动一个nginx代理服务器,监听a.com 的80端口;
//配置代理规则 ^/apis/ => b.com/ ,
//这让我们在请求a.com/apis/login 的时候,被nginx拦截,代理到 b.com/login
处理页面之间的跨域
window.name+iframe 需要目标服务器响应window.name。
window.location.hash+iframe 同样需要目标服务器作处理。
html5的 postMessage+ifrme 这个也是需要目标服务器或者说是目标页面写一个postMessage,主要侧重于前端通讯。
a.com/index.html
<iframe src="b.com/index.html" id="a">
var data= {name:1}
document.getElementById("a").contentWindow.postMessage(data, '*');
window.addEventListener('message',function(data){
})
b.com/index.html
注册监听消息事件:
window.addEventListener('message',function(data){
})
//向父页面post消息
window.parent.postMessage({
}, '*');
18.正则相关(小米,boss,头条)1
2
3
4a.正则捕获规则
b.如果是嵌套捕获,得到的顺序是什么样的。
c.正则捕获的引用和反向引用是什么
d.正则的匹配默认是贪婪匹配么
19.[].toString()为啥会返回逗号隔开的字符串
20.数据proxy劫持与defineProperty对比。
defineProperty 和 proxy 对比1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109es5
defineProperty:修改或者定义一个对象的新属性, 返回这个对象
1.不能监听数组变化
push, pop, shift, unshift,splice, sort, reverse,这些能修改原数组的方法成为变异方法;
vue的做法是把这些方法重写来实现数组的劫持;
2.必须遍历对象的每个属性,单独设置;
Object.keys(obj).forEach(key => {
Object.defineProperty(obj, key, {
// ...
})
})
3.必须深层遍历嵌套对象
vue的深度劫持;
知识:
基础描述符
configrable:false,默认false,表示该属性是否可以修改 描述符配置;
enumerable:false,默认false,表示该属性是否可以枚举
设置和获取描述符
set(newval){}
get(){}
赋值描述符
value:'',
writable:false,表示值是否可被修改,默认false
设置和获取描述符和赋值描述符,只能有一个出现,基础描述符可以一直存在。
============================
es6
proxy 代理,将一个对象进行包装,从而可以代理 操作对象的一些行为;
1.针对对象:针对整个对象,而不是对象的某个属性; 不用 遍历属性;
let obj = {
name: 'Eason',
age: 30
}
let handler = {
get (target, key, receiver) {
console.log('get', key)
return Reflect.get(target, key, receiver)
},
set (target, key, value, receiver) {
console.log('set', key, value)
return Reflect.set(target, key, value, receiver)
}
}
let proxy = new Proxy(obj, handler)
proxy.name = 'Zoe' // set name Zoe
proxy.age = 18 // set age 18
// Reflect.get 和 Reflect.set 可以理解为类继承里的 super,即调用原来的方法
Reflect.get():获取对象身上某个属性的值,类似于 target[name]。
Reflect.set():将值分配给属性的函数,返回一个Boolean,如果更新成功,则返回true。
2.支持数组:不需要对数组的方法进行重载,省去了众多 hack
let arr = [1,2,3]
let proxy = new Proxy(arr, {
get (target, key, receiver) {
console.log('get', key)
return Reflect.get(target, key, receiver)
},
set (target, key, value, receiver) {
console.log('set', key, value)
return Reflect.set(target, key, value, receiver)
}
})
proxy.push(4)
// 能够打印出很多内容
// get push (寻找 proxy.push 方法)
// get length (获取当前的 length)
// set 3 4 (设置 proxy[3] = 4)
// set length 4 (设置 proxy.length = 4)
3.嵌套支持: get 里面递归调用 Proxy 并返回
let obj = {
info: {
name: 'eason',
blogs: ['webpack', 'babel', 'cache']
}
}
let handler = {
get (target, key, receiver) {
console.log('get', key)
// 递归创建并返回
if (typeof target[key] === 'object' && target[key] !== null) {
return new Proxy(target[key], handler)
}
return Reflect.get(target, key, receiver)
},
set (target, key, value, receiver) {
console.log('set', key, value)
return Reflect.set(target, key, value, receiver)
}
}
let proxy = new Proxy(obj, handler)
// 以下两句都能够进入 set
proxy.info.name = 'Zoe'
proxy.info.blogs.push('proxy')
21.js为什么要有protoType?如果没有会怎么样?(阿里)
22.generator函数?跟async有什么区别?(阿里)
1 | generator函数?跟async有什么区别? |
HTML+CSS相关
1.meta标签常用有哪些,列举?viewport属性有哪些分别代表什么(水滴)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
忽略页面中数字识别为电话 忽略email识别
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
关键字
<meta name="keywords" content="楚楚推,楚楚推平台">
网页描述
<meta name="description" content="楚楚推,分享生活,分享爱。">
允许加载哪个域的资源,防止xss攻击
<meta http-equiv="Content-Security-Policy">
viewport虚拟窗口
width:控制 viewport 的大小,可以指定的一个值,如果 600,或者特殊的值,如 device-width 为设备的宽度(单位为缩放为 100% 时的 CSS 的像素)。
height:和 width 相对应,指定高度。
initial-scale:初始缩放比例,也即是当页面第一次 load 的时候缩放比例。
maximum-scale:允许用户缩放到的最大比例。
minimum-scale:允许用户缩放到的最小比例。
user-scalable:用户是否可以手动缩放
<meta name="viewport"content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" />
2.清除浮动方法?(水滴)
1
2
3
4
5
6
7
8
9
10
11
12
13a.添加空div,增加clear:both属性
b.给父元素定义overflow:hidden,氟元素必须设置width
c:给父元素增加伪元素
.clearfix:after {
clear: both;
content: '';
font-size: 0;
display: block;
visibility: hidden;
height: 0;
}
3.口述css盒子模型(水滴)
1 | content-box: |
4.实现左右固定宽度中间自适应布局有哪几种?(水滴)
a. 最外层元素 {padding:0 200px};
所有子元素都向左浮动
第一个子元素:width:100%;float:left
第二个子元素:width:200px;float:left;margin-left:-100%;position:relative;left:-200px;
第三个子元素:width:200px;float:left;margin-left:-200px;position:relative;left:200px;
b.
前两个子元素浮动,第三个子元素通过margin来控制宽度;
c.
flex布局;
5.position属性有哪几种,区别?(水滴)
static、relative、absolute、fixed。
6.左侧固定宽度,右侧自适应? 多种。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62a.左侧设置固定宽并且左浮动,右侧加个margin-left;
.box1{
background: red;
width: 200px;
float: left;
}
.box2{
background: darkcyan;
margin-left: 200px;
}
b.calc 计算右侧宽度,右浮动
.box1{
background:red;
width:200px;
float:left;
}
.box2{
background:green;
width:cacl(100% - 200px)
float:right;
}
c.flex布局
.outer{
display:flex;
flex-direction:row;
}
.box1{
width:200px;
background:red
}
.box2{
flex:1;
background:green;
}
d.左侧设置固定宽并且左浮动,右侧加overflow:hidden;
.box1{
background:red;
width:200px;
float:left;
}
.box2{
background:green;
overflow:hidden;
}
e.absolute+margin
左侧设置固定宽并且绝对定位,右侧加margin-left;
.box1{
background:red;
width:200px;
position:absolute;
left:0;
}
.box2{
background:green;
margin-left:200px;
}
7.用css实现一个三角形,为什么?1
2
3
4
5
6.triangle {
width : 0;
height: 0;
border : 100px solid transparent;
border-top : 100px solid blue; /*这里可以设置border的top、bottom、left、right四个方向的三角*/
}
8.div垂直水平居中,多种方式(宽高不限)
1 | .box1{ |
9.img i iframe video canvas q ol nav form em dl dt dd br hr audio abbr span
strong sub textarea 那些是块?
10.块元素 和行内元素有哪些?1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16常用的
div ul li p h1
span i img input a em textarea
快元素:独占一行;自上至下;可设置狂傲
行内元素:不独占一行;自左至右;设置宽高无效;设置margin padding,上下方向无效
可继承:font相关 font-size family weight font-style
line-height
text-align
text-indent 文本缩进
color 字体颜色
word-spacing字间隔
11.css高度宽度分别为视窗的20%的盒子1
2第一种:width:20%;padding-bottom:20%;
第二种:width:20vw;height:20vw
前端框架相关
1.什么是mvvm?mvvm实现原理?
1 | 双向数据绑定 |
2.vue兄弟组件传递数据?1
2
3
4
5
6
7
8
9
10
11父传子:通过给子组件绑定属性传值,:data=""。子组件接收通过props设置接收的属性。
子传父:通过事件传值,给子组件绑定事件,@事件名="父组件方法",子组件改变了数据,
通过this.$emit(事件名,data)通知父组件。
兄弟组件传值:中间件
1.
创建一个单独的vm实例。
通过vm.$on('事件名',方法)绑定事件
通过vm.$emit(事件名,data)通知兄弟组件修改数据。
2.合并到同一个父组件下,通过父组件进行传值。
3.vuex
3.vuex的使用场景?如何使用?原理?(阿里)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121应用场景:非父子关系的组件通信及共享数据,例如兄弟组件、祖孙组件。
当你打算开发大型单页应用(SPA),会出现多个视图组件依赖同一个状态,来自不同视图的行为需要变更同一个状态
1.组件销毁,仍想保留数据,再进渲染组件直接获取数据。
举例:form表单弹窗组件,用户填写信息后,关闭弹窗。可在组件destroyed
时将用户填写信息保存到state中。在created时,读取state中数据进行渲染
2.有些组件构建和加载是依赖异步数据。v-if="{{store.userInfo.vip}}"
举例:有几个组件是与用户等级挂钩的,用户等级不同,展示的组件不同,并且这些组件
都在不同的父组件中。
可以将异步请求数据的方法和请求回来的数据保存到state中,集中管理调用。
这样异步请求结束,所有的组件就都能得到状态,从而进行渲染。
3.多处共用数据,多处可更改数据,互相影响。
举例:购物车,详情页添加、删除,购物车页面添加删除
消息列表消息数量
订单相关跳转路由,订单列表跳转订单详情,订单详情跳转订单评价,
不同路由之间订单数据共享。
============================================
如何使用:
============================================
原理:
1.全局注入store:
vuex 利用插件机制调用install进行安装vuex,install过程中利用mixin混入,全局注册混入对象,将会影响所有之后创建的Vue实例。
混入 beforeCreate 钩子,每个实例生成都会先调用混入的beforeCreate钩子(vuexInit),vueInit检查当前options中是否有store对象,
如果没有则会去父组件中查找。
a. Vue.use(Vuex) =>安装Vuex插件,调用Vuex的install方法。
b. Vuex.install = function(){
Vue.mixin({ beforeCreate: vuexInit });
}
store注入 vue的实例组件的方式,是通过vue的 mixin机制,借助vue组件的生命周期钩子beforeCreate 完成的。
即 每个vue组件实例化过程中,会在 beforeCreate 钩子前调用 vuexInit 方法。
c.vuexInit核心代码如下:
this.$store = typeof options.store === 'function'
? options.store()
: options.store
this的指向,得益于mixin机制,this将指向 vue组件实例!最终,我们可以再vue组件实例上获得vuex的store
对象的引用 $store!
源码:
function applyMixin (Vue) {
var version = Number(Vue.version.split('.')[0]);
if (version >= 2) {
Vue.mixin({ beforeCreate: vuexInit });
} else {
var _init = Vue.prototype._init;
Vue.prototype._init = function (options) {
if ( options === void 0 ) options = {};
options.init = options.init
? [vuexInit].concat(options.init)
: vuexInit;
_init.call(this, options);
};
}
function vuexInit () {
var options = this.$options;
if (options.store) {
this.$store = typeof options.store === 'function'
? options.store()
: options.store;
} else if (options.parent && options.parent.$store) {
this.$store = options.parent.$store;
}
}
}
2.数据响应式,state 和 getter数据更新如何映射到各个组件实例中自动更新,并update组件。
某一组件store更新时,如何通知其他组件进行数据更新,和UI更新!通过简单分析可知,问题的根本就是组件通信的问题!
原理:vuex的state是借助vue的响应式data实现的,getter是借助vue的computed属性实现的。
在 new Vuex.Store({state,getters})实例过程中,
Store的构造函数执行了这样一个函数 resetStoreVM(this, state); this->store实例,就是我们用到的this.$store
作用:
初始化了一个vue实例_vm。
将state注入到该实例中,由于vue的data是响应式的,所以$$state 是响应式的。当我们在更改this.$store.state.xxx时,基于vue的data的响应式机制,所有相关的state.xxx的值都会自动更新,ui自动更新。
将store.getters作为computed属性注入到_vm上,然后将store.getter映射出一个新的对象,定义get拦截去获取_vm.xxx;
实际应用中,我们修改this.$store.state.xxx => this.$store._vm.data.$$state => store._vm.$$state
get state () {
return this._vm._data.$$state
}
实际应用中,我们获取getter属性, this.$store.getters.xxx ,实际上取得的 this.$store._vm.xxx 取得是_vm的计算属性
源码:
function resetStoreVM (store, state, hot) {
//处理getter
store.getters = {};
var wrappedGetters = store._wrappedGetters;
var computed = {};
forEachValue(wrappedGetters, function (fn, key) {
computed[key] = function () { return fn(store); };
Object.defineProperty(store.getters, key, {
get: function () { return store._vm[key]; },
enumerable: true // for local getters
});
});
//创建vue实例,响应数据
store._vm = new Vue({
data: {
$$state: state
},
computed: computed
});
}
}
4.用vue-router实现导航切换 类似tab栏的那种;(一个tab有三个页,下面展示的内容怎么组件实现)
5.vue和react区别
6.vue 的watch中可以使用箭头函数吗
1 | 不能使用,箭头函数在声明时就确定了this指向,指向的是父级作用域上下文。 |
7.vue自定义组件的双向绑定怎么实现?
1 | 一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件; |
8.mvc和mvvm的区别,手画一个原理流程图,类似view model viewmodel的一个流程交互图
9.v-if 和 v-show的区别1
2v-show指令是通过修改元素的displayCSS属性让其显示或者隐藏,首次渲染消耗大,适合频繁切换
v-if指令是直接销毁和重建DOM达到让元素显示和隐藏的效果,。具备懒惰性,不适合频繁切换,首次消耗小。
10.v-if跟 v-else,如果太多了你可以怎么优化,可以用什么设计模式
11.1
2
3
4
5
6
7
8
9
10<keep-alive></keep-alive>
包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。
用来对组件进行缓存,从而节省性能,由于是一个抽象组件,所以在v页面渲染完毕后不会被渲染成一个DOM元素
大白话: 比如有一个列表和一个详情,那么用户就会经常执行打开详情=>返回列表=>打开详情…这样的话列表和详情都是
一个频率很高的页面,那么就可以对列表组件使用<keep-alive></keep-alive>进行缓存,这样用户每次返回列表的时候,
都能从缓存中快速渲染,而不是重新渲染.
使用了 keep-alive组件的两种状态
actived 组件被激活时调用,
deactived 组件被移除时调用
12.mixin 全局混入
vue官方混入1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
混入规则:
混入内容值为对象的选项,例如 methods, components 和 directives,将被混合为同一个对象。两个对象键名冲突时,取组件对象的键值对。
混入的是钩子函数时 created...等,同名钩子函数将混合为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。
特点:mixin中的this指的 当前vue实例。
全局混入: 每个vue实例都混入该对象,包括组件的实例。
Vue.mixin({
created:function(){
this.$options.xxx
}
})
new Vue({
el:xxx
})
局部混入:只在当前实例混入
var mixin = {
data(){
return {
name:1
}
}
}
new Vue({
mixins:[mixin]
})
13.vue插件
1 |
|
14.Vue.extend 和 Vue.component区别
1 |
|
15.vue插件机制 封装组件
1 | 只用于渲染页面的组件 |
16.vue 在计算属性的时候,computed和用method里的方法计算有什么区别
1 |
|
17.vue中render,nextTick介绍一下?
18.component中data:function(){} 和data:{}区别?
1 |
|
19.vue生命周期钩子?
beforeCreate 实例创建之前
created 实例创建完成
beforeMount 挂载前
mounted 挂载后
beforeUpdate 更新前
updated 更新后
beforeDestory 销毁前
destoryed 销毁后
20.vue-routers的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
a.hash /#/ + onhashChange 事件
改变hash,浏览器的请求不包括hash部分,不会刷新页面;通过hashchange事件监听路由变化
b.history操作
history.back() history.go() history.forward(),这些前进后退的操作会触发popstate事件;
window.addEventListener('popstate',fn)
pushState,replaceState 并不会触发popstate事件,这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。
只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求,也就不会刷新;
但是这两个方法不会触发popstate事件,需要我们手动去修改pushstate方法,增加消息通知;
//1.对方法进行加工,并且发布消息;
function apo(type){
var source = window.history[type];
return function(){
var event = new Event(type);
event.arguments = arguments;
window.disptchEvent(event);
return source.apply(this,arguments);
}
}
window.history.pushState = aop('pushState');
window.history.replaceState = aop('replaceState');
window.addEventListener('pushState',function(){console.log(1)})
window.addEventListener('replaceState',function(){console.log(2)})
2.发布订阅模式,创建event bus来实现;拦截pushState 和replaceState ,发布消息;
前端工程化相关
1.webpack基本配置有哪些?(都问)
1 |
|
2.vue-cli3脚手架的基本配置?
3.webpack作用?
1 | 1、合并js文件,压缩代码;处理图片资源等静态资源,减少http请求; |
4.你对脚手架有研究过么?(小米)
5.项目打包部署你都是怎么弄的?(小米)1
jekins部署,具体环境是运维来搭建的。
应用场景开发相关
1.简述微信公众号授权过程?(每日优鲜)
2.客户端与前端的交互的原理 ,怎么交互?(瓜子二手车)
安卓:webview
1.js全局调用客户端方法:java向前端注入了全局对象ccjBridgeInstance,这个对象上面有一些客户端提供的方法 供前端调用;
url传参,native拦截;
协议拦截,与客户端定义协议,通过动态向html插入 iframe,请求协议地址传带参数;native进行拦截
shareFriends://com.culiu.JiuKuaiJiu###分享标题###分享地址
2.客户端调用js:window上声明响应的回调地址,window.方法名_callback,提供给客户端调用;
ios:UIwebview wkwebview
UIwebview:
1.js全局调用客户端方法:java向前端注入了全局对象ccjBridgeInstance,这个对象上面有一些客户端提供的方法 供前端调用;
url传参,native拦截
2.客户端调用js:window上声明响应的回调地址,window.方法名_callback,提供给客户端调用;
WKwebview:
js调用客户端:window.webkit.messageHandlers[方法名].possmessage(data);去调用
url传参,native拦截
客户端调用js:window上声明响应的回调地址,window.方法名_callback,提供给客户端调用;
回调方法遇到的问题:
连续调用相同的bridge,native只调用一次回调;
而且当时我们的回调地址都是直接赋给window的一个属性了,导致我们的回调一次次覆盖,只保留了最后一次;
解决方案:将回调收拢,每个bridge方法,对应一个回调函数池,提供给native的方法就是遍历这个回调函数池中的回调,依次执行;
调用brigde时,就是向回调函数池中添加 函数;
#场景算法题
1.将aabbcccda 字符传替换成abcda?(将连续重复的字符去重)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18将连续重复的字符去重,这种会有重复项,aabbcccdc=>字符传替换成abcdc
reduce拼接,
function switchStr(str) {
let arr = str.split('');
return arr.reduce((prev,next,index,origin)=>{
if(prev.slice(prev.length-1) == next){
return prev;
}else{
return prev + next;
}
},'')
}
function switchStr(str){
return str.replace(/(.)(/1)+/,function(...args){
retrun args[1]
})
}
2.js实现字符串trim方法1
2
3
4
5
6
7
8
9
10原生:
var a = ' 123 '
a.trim();
js实现:
String.prototype.trim = function(){
return this.replace(/(^\s+|\s+$)/g,function(){
return ""
})
}
2.原生js实现字符串split方法?
1 |
3.函数节流?防抖?有何区别?
1 | // 函数节流 |
4.[a,b,c],写个方法将数组全排列 ,变成[abc,acb,bac,bca,cab,cba] (百度)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56思路:每次都取出一个,然后将除去这一个的其他数进行全排列。然后在将取出的那个数拼接到开头
距离:取出a,然后b和c的全排列有两种:bc,cb; 这样就可以算出abc,acb;
取出b,然后a和c的全排列有两种:ac,ca; 这样就可以算出bac,bca;
取出c,然后a和b的全排列有两种:ab,ba; 这样就可以算出cab,cba;
递归千万条,出口第一条:出口就是当数组为两项时,就可以自己写出 全排列。
// [a,b,c]=>[abc,acb,bac,bca,cab,cba]
function permute(ary){
if(ary.length <= 2){
if(ary.length == 2){
return [ary.join(''),ary.reverse().join("")]
}else{
return [ary[0]]
}
}
let result = [];
ary.forEach((item,index)=>{
let all = permute(ary.slice(0,index).concat(ary.slice(index+1)));
all = all.map(val=>{
return item+val+'';
})
result = result.concat(all);
})
return result
}
console.log(permute(["a","b","c"]));
字符串全排列 abc =>[abc,acb,bac,bca,cab,cba]
const anagrams = str => {
if (str.length <= 2) return str.length === 2 ? [str, str[1] + str[0]] : [str];
return str.split('').reduce((acc, letter, i) =>
acc.concat(anagrams(str.slice(0, i) + str.slice(i + 1)).map(val => letter + val)), []);
};
console.log(anagrams('1234'));
数组全排列 [a,b,c]=>[[a,b,c],[a,c,b]...]
function permute(arr) {
if(arr.length <= 2){
if(arr.length == 2){
return [arr,[arr[1],arr[0]]];
}else{
return [arr];
}
}
return arr.reduce((prev,next,i)=>{
let pool= permute(arr.slice(0,i).concat(arr.slice(i+1)));
pool = pool.map(item=>{
item.unshift(next);
return item;
});
return prev.concat(pool)
},[])
}
console.log(permute([ 1, 2, 3]));
5.数组对象去重(vipkid)
1 |
|
6.你所用到的排序方法
7.实现一个方法,能够把多重数组变成一个一维数组,flat方法。[1,[1,2],[3,4,5]]转换为[1,1,2,3,4,5] (vipkid)1
2
3
4
5
6
7
8
9
10
11
12function flat(arr) {
let result = [];
for (let i=0;i<arr.length;i++){
if(Object.prototype.toString.call(arr[i]) == '[object Array]'){
result = result.concat(flat(arr[i]))
}else{
result.push(arr[i])
}
}
return result;
}
8.12345数组混排 (水滴)
1 | 利用随机数 |
9.将字符串中重复出现的字符,去重;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
161.转成数组,去重,然后拼接;
function uqinue(str){
let ary = str.split("");
return Array.from(new Set(ary)).join("")
}
2.遍历
function uqinue(str){
let result = "";
for(let i = 0;i<str.length;i++){
if(result.indexOf(str[i])==-1){
result = result + str[i]
}
}
return result;
}
10.abcaaaaaabcabcabcabbbabc 获取abc的重复次数 和索引
1 |
|
11.Function instanceof Object (阿里,头条)
Object instanceof Function1
2
3
4
5
6
7
8
9 Function instanceof Object 返回true。
原理:Function 是一个类同时也是一个函数,是Function类的一个实例;
Function instanceof Function 是true。
Function.__proto__ == Function.prototype;
而Function.prototype.__proto__ 是Object.prototype;
Object instanceof Function 返回true;
原理:Object是一个类也是一个函数,所以是Function的一个实例。
12.用二分查找递归方法,插入一个值并返回索引;有序数组[1,2,3,5,7,8],插入4,返回索引3;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//非递归,遍历找到第一个出现大于他的值,然后记住索引,向他前面插入进去。如果没找到,则直接push进去
function insert(ary,n) {
let index;
for(var i = 0 ;i<ary.length;i++){
if(ary[i]>n){
index = i;
break;
}
}
if(i>=ary.length){
ary.push(n)
return i;
}else{
ary.splice(index,0,n)
return index;
}
}
console.log(insert([1, 2, 4], 3));
function insert(arr,n) {
var l = [];
var r = [];
for(let i = 0 ; i<arr.length;i++){
if(arr[i]<n){
l.push(arr[i])
}else{
r.push(arr[i])
}
}
return {
index:l.length,
ary:l.concat([n]).concat(r)
}
}
//二分插入,递归。
//思路:二分,用左侧最后一项进行比较,如果比插值大,说明应该插入到左侧数组中;
// 递归,继续二分记性比较。
//递归出口:当数组中只有一项时,就可以进行比较,就知道插入到哪了。
function insert(ary,num) {
if(ary.length == 1){
if(ary[0]>num){
ary.unshift(num);
}else{
ary.push(num);
}
return ary;
}
let m = Math.floor(ary.length/2);
let l = ary.slice(0,m);
let r = ary.slice(m);
if(l[l.length-1]>num){
return insert(l,num).concat(r)
}else{
return l.concat(insert(r,num))
}
}
console.log(insert([1, 2, 34,48,68], 34));
13.计算出数组的最大差值 12,5,11,7,33,91
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28function adjective(arr) {
let max = Math.max(...arr);
let min = Math.min(...arr);
return max - min
}
//假设法
function adjective(arr) {
let min = arr[0];
let max = arr[0];
for(let i = 1; i<arr.length;i++){
if(arr[i]<=min){
min = arr[i];
}
if(arr[i]>=max){
max = arr[i]
}
}
return max - min ;
}
//先排序
function fn(arr) {
arr.sort((a,b)=>a-b)
return arr[arr.length-1] - arr[0];
}
14.请写出检查元素是否在屏幕可视区域的关键代码;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18图片懒加载的原理:
方案一:1.获取图片 距离 滑动内容盒子 顶部的offsetTop
(需要给滑动内容盒子增加position:relative属性,使他成为父级参照物)
offsetParent:父级参照物
第一个具有position属性且非static的父级,没有的话,最终是body
一般情况下页面中所有元素的父级参照物都是body;
document.body.offsetParent =>null//body 的父级参照物是null)
2.获取滚动窗口的scrollTop值。
3.获取滚动窗口的clientHeight值。
监听滚动事件:当clientHeight+scrollTop >=offsetTop时,则进入了窗口。
方案二:dom.getBoundingClientRect()是获取某个元素相对于视窗的位置集合;
{top,left,right,bottom},四个值都是距离左边或者顶部的距离。
1.通过getBoundingclientRect().top,获取当前原理距离视窗顶部的距离
2.获取可视窗的高度document.body.clientHeight。
3.当元素距离视窗顶部距离 < 视窗高度,则出现在视窗内
15.将片段1 用正则表达式替换成片段2 (boss直聘)
1 |
|
16.计算”abcadadacvabc”中出现最多的字符?
1 | //预设一个最大次数,利用对象存储次数 key是字符,value是次数;每次遍历都与最大次数进行比较; |
17.输入a=2 b=3 ,输出a=3,b=2
1 |
|
18.找出整形数组中乘积最大的三位数 [-10,7,29,30,5,-10,-70]
思路:先排序由大到小排序,取前三位的乘机 和 末尾两位 * 首位的乘机进行比较。
末尾两位可能是 负负得正,所以才要和前三位进行比较
1 | function get1(ary){ |
19.假设有n级台阶,每次最多允许跨m步(m<=n),那么有多少种跨越方式?
该题解析
1 | 思路:采用自顶向下的思考方式 |
20.写一个函数
输入:{ a:{
b:{
c:{
d:’h’,
j:’l’,
o:{
p:’q’,
r:”s”
}
t:’u’
}
},
v:{
w:{
x:{
y:’z’
}
}
}
}
e:{
f:{
i:k
},
m:’n’
}
}
输出结果,按照层数,把同一层的属性放在同一个子数组内;
[[a,e],[b,v,f,m],[c,w,i],[d,j,o,t,x],[p,r,y]]
1 |
|
21.有两个链表,如何判断两个链表有交叉?
DOM相关
1.如何判断一个dom是否在视窗中
1 | 图片懒加载的原理: |
2.dom事件中 DOMContentLoaded和window.onload的触发实际是什么 ?图片加载完成后是一个什么情况?(苏宁)
1 |
|
3.原生js实现jq的after方法
1 | 思路:获取当前元素的氟元素节点 parentNode |
性能优化相关?
1.列举你能想到的前端性能问题和安全问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
安全问题:
a.xss攻击,就是攻击者想尽一切办法将可以执行的代码注入到网页中。
评论功能,写入脚本内容,入库了。 get参数后面拼接了key=脚本; 这种很被容易写进页面;
防御:
转义字符:对于用户的输入应该是永远不信任的。最普遍的做法就是转义输入输出的内容,对于引号、尖括号、斜杠进行转义
利用js-xss 将内容进行过滤,转义;
csp:建立白名单,配置规则,高速浏览器哪些外部资源可以加载;
通常可以通过两种方式来开启 CSP:
设置 HTTP Header 中的 Content-Security-Policy
设置 meta 标签的方式 <meta http-equiv="Content-Security-Policy">
只允许加载本站资源
Content-Security-Policy: default-src ‘self’
只允许加载 HTTPS 协议图片
Content-Security-Policy: img-src https://*
允许加载任何来源框架
Content-Security-Policy: child-src 'none'
b. CSRF攻击?跨站点请求伪造如何防范
原理就是攻击者构造出一个后端请求地址,诱导用户点击或者通过某些途径自动发起请求。如果用户是在登录状态下的话,
后端就以为是用户在操作,从而进行相应的逻辑。
防御:请求时post附带验证信息,比如验证码或者 Token;
get请求不对数据进行修改
服务端验证referer
c.点击劫持,攻击的网站通过 iframe 嵌套的方式嵌入自己的网页中,并将 iframe 设置为透明,在页面中透出一个按钮诱导用户点击
防御:当通过 iframe 的方式加载页面时,攻击者的网页直接不显示所有内容了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
前端性能优化:(都问)
前端代码的压缩 合并,减少http请求,和文件大小
图片处理设置最大边界,base64;雪碧图;
少操作dom减少dom回流,或者创建文档碎片;
缓存dom节点,减少查找次数;
应用节流和防抖处理函数;比如说滚动发出请求;
preload资源预加载属性;
图片懒加载;
静态资源放到cdn上;
离线存储:配置manifest配置应用程序缓存:CACHE MANIFEST,NETWORK,FALLBACK
优点//
离线浏览 - 用户可在应用离线时使用它们
速度 - 已缓存资源加载得更快
减少服务器负载 - 浏览器将只从服务器下载更新过或更改过的资源。
// 更新缓存
用户清空浏览器缓存
manifest 文件修改,可以配置一个版本号,
由程序来更新应用缓存
// 其他 //
站点离线存储的容量限制是5M
浏览器缓存机制;
楚楚推:图片max-age:24小时;
js css文件max-age:15分钟;
协商缓存;
webpack性能优化:
有哪些方式可以减少 Webpack 的打包时间?
1.优化loader,通过exclude和include,优化loader的文件搜索范围;
module.exports = {
module: {
rules: [
{
// js 文件才使用 babel
test: /\.js$/,
loader: 'babel-loader',
// 只在 src 文件夹下查找
include: [resolve('src')],
// 不会去查找的路径
exclude: /node_modules/
}
]
}
}
2.使用HappyPack插件;
受限于 Node 是单线程运行的,所以 Webpack 在打包的过程中也是单线程的,特别是在执行 Loader 的时候,长时间编译的任务很多,这样就会导致等待的情况。
HappyPack 可以将 Loader 的同步执行转换为并行的,这样就能充分利用系统资源来加快打包效率了
module: {
loaders: [
{
test: /\.js$/,
include: [resolve('src')],
exclude: /node_modules/,
// id 后面的内容对应下面
loader: 'happypack/loader?id=happybabel'
}
]
},
plugins: [
new HappyPack({
id: 'happybabel',
loaders: ['babel-loader?cacheDirectory'],
// 开启 4 个线程
threads: 4
})
]
3.webpack.DllPlugin + webpack.DllReferencePlugin
DllPlugin 可以将特定的类库提前打包然后引入。这种方式可以极大的减少打包类库的次数,只有当类库更新版本
才有需要重新打包,并且也实现了 将公共代码抽离成单独文件的优化方案。
4.resolve.extensions:用来表明文件后缀列表,默认查找顺序是 ['.js', '.json'],如果你的导入文件没有添加后缀就会按照这个顺序查找文件。我们应该尽可能减少后缀列表长度,然后将出现频率高的后缀排在前面
5.resolve.alias:可以通过别名的方式来映射一个路径,能让 Webpack 更快找到路径
有哪些方式可以让 Webpack 打出来的包更小?
1.按需加载,
原理:当使用时候再去下载对应文件,返回一个promise;
2.代码压缩;uglifyJS-webpack-plugin webpack-parallel-uglify-plyugin 并行压缩js
3.Tree Shaking可以实现删除项目中未被引用的代码,
// test.js
export const a = 1
export const b = 2
// index.js
import { a } from './test.js'
test 文件中的变量 b 如果没有在项目中使用到的话,就不会被打包到文件中
4 Scope Hoisting Scope Hoisting 会分析出模块之间的依赖关系,尽可能的把打包出来的模块合并到一个函数中去
module.exports = {
optimization: {
concatenateModules: true
}
Webpack 4 的话,开启生产环境就会自动启动这个优化功能。
}
2.项目优化方法
3.错误检测上报日志怎么实现的?
//a.通过onerror 捕获代码运行错误,捕获到错误信息后可以向目标服务器img发一个请求,get传递错误信息;
window.onerror = function(msg, url, line, col, error){
}
//b.页面埋点,监听用户交互和pv/uv,请求服务器进行上报
//c. performance.getEntriesByType(‘navigation’) 针对一些复杂页面,获取页面的性能相关信息,进行上报;
4.fis3 和 webpack的区别是什么?1
2
3
4
5
6
7
8
9
10
11
12
13a.入口,
webpack是从entry出发,将依赖的文件提取编译打包,通过commonschunkplugin、dll等提取公共代码;
fis3 是以file对象为中心构建编译的,所有文件同等对待,都会去分析文件依赖关系,生成一个静态资源表,
资源表记录每个文件的依赖关系;
fis3可以针对每个文件做出相应的配置,更加细致;
b.fis3的配置文件,fis.match到某些文件,然后对这些文件定义各自的各个阶段的插件处理、发布规则,是针对文件进行匹配的;
webpack配置文件,entry、output、plugins、module、resolve;
c.fis3能对任何文件使用hash控制,在引用它的任何地方的路径会被自动替换为hash路径。
d.fis3,由于独有的静态资源标记,导致npm生态不太好;
5.请列举最重要的前端优化点(按重要性)
6.:我们为什么一再强调将css放在头部,将js文件放在尾部
浏览器解析1
2
3
4
5
6
7
8
9在面试的过程中,经常会有人在回答页面的优化中提到将js放到body标签底部,原因是因为浏览器生成Dom树的时候是一行一行读HTML代
码的,script标签放在最后面就不会影响前面的页面的渲染。那么问题来了,既然Dom树完全生成好后页面才能渲染出来,浏览器又必须读
完全部HTML才能生成完整的Dom树,script标签不放在body底部是不是也一样,因为dom树的生成需要整个文档解析完毕。
我们再来看一下chrome在页面渲染过程中的,绿色标志线是First Paint的时间。纳尼,为什么会出现firstpaint,页面的paint
不是在渲染树生成之后吗?其实现代浏览器为了更好的用户体验,渲染引擎将尝试尽快在屏幕上显示的内容。它不会等到所有HTML解析之前
开始构建和布局渲染树。部分的内容将被解析并显示。也就是说浏览器能够渲染不完整的dom树和cssom,尽快的减少白屏的时间。
假如我们将js放在header,js将阻塞解析dom,dom的内容会影响到First Paint,导致First Paint延后。所以说我们会将js放在
后面,以减少First Paint的时间,但是不会减少DOMContentLoaded被触发的时间。
7.vue首屏加载很慢,怎么进行优化?1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84a.组件异步加载;
局部注册异步组件(`import` 函数会返回一个 `Promise` 对象。)
new Vue({
// ...
components: {
'my-component': () => import('./my-async-component')
}
})
=>
new Vue({
// ...
components: {
'my-component': Promise.resolve({...组件定义对象})
}
})
全局注册异步组件
Vue.component("custom",() => import('./my-async-component'))
=>Vue.component("custom",Promise.resolve({...组件定义对象}))
或者
Vue.component('async-example', function (resolve, reject) {
resolve({
template: '<div>I am async!</div>'
})
})
Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 `require` 语法将会告诉 webpack
// 自动将你的构建代码切割成多个包,这些包
// 会通过 Ajax 请求加载
require(['./my-async-component'], resolve)
})
2.路由懒加载+分组打包;
没有指定webpackChunkName,每个组件打包成一个js文件;
指定了相同的webpackChunkName,会合并打包成一个js文件。
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')
const router = new VueRouter({
routes: [
{ path: '/foo',
component: Foo,
children:[
{path:'/bar',component:Bar}
{path:'/baz',component:Baz}
]
}
]
})
3.组织复杂页面的代码时,可以考虑对于打开首屏时不需要渲染的子组件,使用v-if控制其只在需要的时候被渲染。
4.cdn加载外部的css js文件,浏览器并发请求加载。
html直接引入外部的js 和css文件,比如vue vuex
利用webpack的externals忽略打包文件;key是导入的键,value是项目中用到的变量
externals: {
jquery: 'jQuery' ,
vue:'Vue'
//将需要忽略打包的都写在这个里面,但前提是index.html文件里面必须script引入
}
使用:import $ from 'jquery',正常使用
5.开启gzip压缩支持
npm install compression-webpack-plugin —save-dev
将config/index.js 开启压缩
productionGzip: true,
productionGzipExtensions: ['js', 'css'],
注:想要线上使用还需还得配置后端。
扩展常用webpak插件
copy-webpack-plugin :复制文件到目标文件夹。在开发时使用热模替换,(没有生成dist 文件夹,都在内存中),如果想引用某一个js文件,直接写script标签是找不到的,因为服务器内存中没有这个文件。所以复制这个文件,到dist中。
compression-webpack-plugin: 生产环境时可选择让代码压缩gzip.
html-webpack-plugin : 生成index.html 并自动注入打包后的js css 等
webpack.DefinePlugin: 可以生成配置常量。编译时就有的常量。
extract-text-webpack-plugin: 提取使用文件的css 组成一个或多个css 文件。
webpack.optimize.CommonsChunkPlugin: 让多个出口文件组成一个文件
webpack-dev-server: 开发时使用,静态服务器,并且有热替换等功能。
uglifyjs-webpack-plugin: 删除警告,压缩代码等
扩展知识?能聊就行~
1.简述CI/CD?
2.多态和重载?
1 | 重载:相同函数名,不同参数,则认为是两个不同的函数; |
3.了解服务端的反向代理吗?
4.订餐功能 如何实现的?
利用express框架搭建node服务,对前端提供api请求接口;
利用express-router + app.use 实现父子路由管理,实现用户订餐的增删改;
利用node-schedule 包实现定时任务,每天定时抓取用户订餐状态,生成订餐名单;每天定时清除,所有用户的 订餐状态;
利用nodemailer向行政发送 订餐人员名单;
let dinner = express.Router();
dinner.get(‘/list’,function(){
})
dinner.post(‘/set’,function(){
})
dinner.post(‘/delete’,function(){})
app.use(‘/dinner’,dinner)
表结构:
主键:是用户id ,用工号代替;姓名;部门;订饭状态;
用户进入前端页面,post提交工号,然后将该同学的订饭状态改成订餐状态;
设计模式
1.观察者模式 和 订阅发布模式的区别
1
2
3// 观察者模式和发布订阅模式最大的区别就是发布订阅模式有个事件调度中心。
// 观察者模式由具体目标调度,每个被订阅的目标里面都需要有对观察者的处理,会造成代码的冗余。而发布订阅模式则统一由调度中心处理,
// 消除了发布者和订阅者之间的依赖。
程序执行题
1.代码执行结果(boss直聘)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28var data = {
name:'boss',
age:3
}
console.log(data.age);//3
observe(data);
console.log(data.age);//4
data.age = 5; //val = 6
console.log(data.age)//7
function observe(data) {
Object.keys(data).forEach(function (key) {
defineReactive(data,key,data[key])
})
}
function defineReactive(data,key,val) {
Object.defineProperty(data,key,{
enumerable:true,
configurable:true,
get(){
return val +1;
},
set(newVal){
val = newVal+1;
}
})
}
2.代码执行结果1
2
3
4
5const arr = [1,2,3,4,5,6];
const res = arr.filter(value =>value%2).map(item=>item*item).reduce((a,b)=>a+b);
console.log(res);//35
console.log(arr.pop());//6
console.log(arr);//[1,2,3,4,5]
3.程序输出结果1
2
3
4
5
6
7const arr = [1,2,3,4,5,6]
for(var i = 0;i<arr.length;i++){
setTimeout(_=>{
console.log(arr[i])
},0)
}
//输出6个undefined
#node
1.express的设计思想?
路由控制
中间件
静态文件服务
模板解析
let app = express();
app.get(‘’,(req,res,next)=>{
},(req,res,next)=>{}).post(‘’,(req,res.next)=>{
})
app.use(‘’,(req,res,next)=>{
})
let user = express.Router();
user.use(‘/login’,(req,res,next)=>{
})
user.get(‘’,(req,res)=>{
})
app.use(‘/user’,user)
设计思想
1.路由:二维数组的二维数据形式
[[cb,cb,cb],[cb,cb,cb],[cb,cb,cb],[usecb,usecb,usecb]]
最外层是Router,[cb,cb,cb] 表示的route,cb指的是每个route中的回调;[usecb,usecb,usecb]表示中间件的回调;
2.路由服务
express.router(),创建路由,在利用app.use中间件,形成父子级别中间路由;
express.router返回的一个router函数,router函数复用了app的一些方法,
也同样具备handler/post/get/use等方法;这样就利用方法复用,实现了一层一层的套用;
3.静态资源服务,调用node原生利用 http服务起的服务;
2.express和koa的区别?(阿里)
express 自带路由控制,express-router ;koa 需要单独引入
express 基于回调函数,koa是基于generator思想的;koa是不会出现回调地狱的;
3.node 思想?
特点:
线程是单线程异步。通过callback实现异步。
非阻塞、异步I/o,能用异步 绝不同步
event-driven事件驱动-发布订阅模式
思想:基于事件驱动,异步io,通过callback实现异步
commonjs模块化,一个js就是一个模块;
自由回答题:
在一个异世界里,老虎可以吃羊,也可以吃草,并且羊比草好吃。但是吃了羊就会变成羊。假设现在有100只老虎和1只羊,且所有老虎都非常理性,那么最终会剩下多少老虎和多少羊?(头条)