引言
金三银四,特地整理一份面试题,现介绍本文特色:
1、适合前端,需要面试找工作
2、即将毕业面临实习,积累经验
3、从务实基础到彻底弄懂
4、探索框架源码,研究前端必备算法
5、直击阿里、腾讯、美团、今日头条等大厂原题,逐步引入
6、学完即准备投简历
BAT/TMD这样的大公司是如何面试的
注意嗷,在这里TMD可不是骂人的话哦,可能你知道BAT,但TMD你知道么?(不知道赶紧去百度!)
T(今日头条)M(美团)D(滴滴)成为了BAT之后互联网江山的新巨头
相关文章
【金三银四】一个问题就知道你会不会JS了 阿里、头条真题解析
上期遗漏的知识拓展
在上一篇关于JS真题,最后忘记对知识进行拓展了,因此在这里进行补充
第一题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面试真题</title>
</head>
<body>
<script>
var a = ?; //该题意思是变量a应该等于什么值 可以让条件成立
if(a == 1 && a==2 && a==3){
console.log(1);
}
</script>
</body>
</html>
相关知识点:
== 数据类型不一样
例如: 对象 == 字符串 会通过对象.toString()变为字符串
null == undefined 相等 但是和其它值进行比较时不相等
NaN == NaN 不相等 因为NaN和任何都不相等,包括自己
剩下的都是转化成数字,例如:
第一种解决办法,自己写一个toString()( 也可以用valueof() )
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面试真题</title>
</head>
<body>
<script>
var a = {
i: 0,
toString(){
return ++this.i;
}
}
if(a == 1 && a==2 && a==3){
console.log(1);
}
</script>
</body>
</html>
第二种解决办法,采用数据劫持
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面试真题</title>
</head>
<body>
<script>
var i=0;
Object.defineProperty(window,'a',{
get(){
return ++i;
}
})
if(a == 1 && a==2 && a==3){
console.log(1);
}
</script>
</body>
</html>
第三种解决办法,用数组的shift()方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面试真题</title>
</head>
<body>
<script>
var a=[1,2,3];
a.toString=a.shift;
if(a == 1 && a==2 && a==3){
console.log(1);
}
</script>
</body>
</html>
当然,方法还有很多很多,这里就例举容易想到的三个
第二题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面试真题</title>
</head>
<body>
<script>
function A(){
alert(1);
}
function Func() {
A=function(){
alert(2);
}
return this;
}
Func.A=A;
Func.prototype={
A:()=>{
alert(3);
}
};
A(); //'1'
Func.A(); //'1'
Func().A(); //'2'
new Func.A(); //'1'
new Func().A(); //'3'
new new Func().A(); //'bug'
</script>
</body>
</html>
相关知识点:
箭头函数无法new
箭头函数和普通函数的区别:
- 箭头函数没有this主体,this都是继承上下文的this。
- 箭头函数无法被new的原因是箭头函数没有原型链,也就没有构造器函数constructor
第三题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面试真题</title>
</head>
<body>
<script>
var x=0,y=1;
function fn(){
x+=2;
fn=function(y){
console.log(y+ (--x) );
};
console.log(x,y);
}
fn(3); //2 1
fn(4); //5
console.log(x,y); // 1 1
</script>
</body>
</html>
相关知识点:
重写fn函数
第四题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>面试真题</title>
</head>
<body>
<script>
setTimeout(() => {
console.log(1);
}, 20);
console.log(2);
setTimeout(() => {
console.log(3);
}, 10);
console.log(4);
console.time('AA');
for (let i = 0; i < 90000000; i++) {
//do something
}
console.timeEnd('AA'); //97ms左右
console.log(5);
setTimeout(() => {
console.log(6);
}, 8);
console.log(7);
setTimeout(() => {
console.log(8);
}, 15);
console.log(9);
</script>
</body>
</html>
相关知识点:
宏任务: 定时器例如setTimeout(异步)、事件绑定
面试题引入
1.Vue2.0双向绑定的实现原理
强烈推荐阅读:Vue 技术栈 手写响应式原理 到 探索设计模式(通过源码精通双向绑定)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
name:<span id="spanName"></span>
<br>
<input type="text" id="inpName">
<script>
let obj={
name:''
};
let newObj={
...obj
};
Object.defineProperty(obj,'name',{
get(){
return newObj.name;
},
set(val){
if(val===newObj.name) return;
newObj.name=val;
observer();
}
});
function observer(){
spanName.innerHTML=obj.name;
inpName.value=obj.name;
}
observer();
setTimeout(()=>{
obj.name="超逸の博客!";
},2000);
inpName.oninput = function() {
obj.name=this.value;
};
</script>
</body>
</html>
2.0版本defineProperty存在的问题:
- 需要对原始数据进行拷贝
- 需要分别给对象中的每一个属性设置监听,会需要for in循环以及递归操作
2.Vue3.0双向绑定的实现原理
强烈推荐阅读:Vue 技术栈 手写响应式原理 到 探索设计模式(通过源码精通双向绑定)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
name:<span id="spanName"></span>
<br>
<input type="text" id="inpName">
<script>
let obj={
name:''
};
obj=new Proxy(obj,{
get(target,prop){
return target[prop];
},
set(target,prop,value){
target[prop]=value;
observer();
}
})
function observer(){
spanName.innerHTML=obj.name;
inpName.value=obj.name;
}
observer();
setTimeout(()=>{
obj.name="超逸の博客!";
},2000);
inpName.oninput = function() {
obj.name=this.value;
};
</script>
</body>
</html>
总结:为什么Vue 3中使用proxy
- defineProperty只能监听某个属性,不能对全对象进行监听
- 可以省去for in遍历找对象中的属性,提高效率,省去很多代码
- 可以监听数组,不用再去单独的对数组进行特异性操作
- 不会污染原对象,会返回一个新的代理对象,原对象依旧是原对象
- 只需对代理对象进行操作
3.MVC和MVVM的区别
关于这个问题呢,就是关于vue和react的区别,你可以参考如下回答:
没有很大区别,知识MVC是单向数据绑定,只绑定了数据更改然后视图跟着渲染,而MVVM实现了双线数据绑定,在基础上多了一层视图更改,对应数据绑定也跟着更改。要从MVC到MVVM其实不难,一个onchange事件搞定,所以没很大区别。
4.跨域问题的解决方案和实现原理
跨域问题的产生及其价值意义
知识点拓展:
http默认端口 80
https默认端口 443
ftp默认端口 21
在过去,前后端分离之前,不存在跨域问题,是因为那时候前端和后端都是部署在同一个服务器上,同一个域下,同一个端口下,代码都放一起就不存在跨域问题了。
后来,区分Web服务器和数据服务器之后,就存在了跨域问题。
JSONP跨域解决方案的底层原理
客户端
get请求问题:
- 不安全
- 有缓存
- 大小限制
- 需要服务器的支持 拼接数据等
发现问题挺多,JSONP就过时了,于是尝试用post请求
基于iframe的跨域解决方案
- window.name
- document.domin
- location.hash
- post message
但上述都是存在一些问题的,后来呢,能不能让服务器进行跨域请求呢?
于是,出现了如下解决方式:
CORS跨域资源共享
客户端
import axios from 'axios';
import qs from 'qs';
axios.defaults.baseURL = "http://127.0.0.1:3000";
axios.defaults.timeout = 10000;
axios.defaults.withCredentials = true;
/**
* 设置请求传递数据的格式(看服务器要什么格式)
* x-www-form-urlencoded
*/
axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.transformRequest = data =>qs.stringify(data);
/**
* 设置请求拦截
* TOKEN校验(JWT):接受服务器返回的token,存储到vuex/本地存储中,
* 每一次向服务器发送请求,应该把token带上
*/
axios.interceptors.request.use(config => {
let token = localStorage.getItem('token');
token && (config.headers.Authorization = token);
return config;
}, error => {
return Promise.reject(error);
});
/**
* 响应拦截器
*/
axios.interceptors.response.use(response => {
return response.data;
},error => {});
export default axios;
服务器端
app.use((req,res,next) => {
res.header("Access-Control-Allow-Origin","");
res.header("Access-Control-Allow-Credentials",true);
res.header("Access-Control-Allow-Headers","PUT,POST,GET,DELETE,OPTIONS,HEAD");
res.header("Access-Control-Allow-Methods","Content-Type,Content-Length,Authorization,Accept,X-Requested-With");
req.method === 'OPTIONS' ? res.send('CURRENT SERVICES SUPPORT CROSS DOMIN REQUESTS!') : next();
});
CORS跨域与客户端没啥关联,主要是看服务端,但是也存在一点问题:
例如在服务端设置"Access-Control-Allow-Origin","*" (设置这里为 * ) ,允许所有源,此时就不能携带证书凭证了。
于是,后面又出现了新的一种跨域方式:
基于http proxy实现跨域请求(开发时使用)
nginx反向代理(部署时使用)
5.Vue框架中关于组件信息通信问题
(还在学习中,拖更…)
- 属性传递
- 发布订阅(EventBus):$on / &emit
- Provide / inject
- slot
- $parent / $Children
- vuex
客户端
但是,想实现任意组件之间都能通信,只能通过本地存储
方案。包括vuex / redux公共状态管理,不过存储在虚拟机内存里面。
另外,还有一个能够存储在浏览器
里面的就是localStroage
vuex和localStorage两者区别:
vuex一刷新就没了,而localStorage一直存在(持久化存储),不过需要进行时间限制
因此引申到cookie,为什么不用cookie呢,因为大小限制,只有4k,而localStorage有5M
而且cookie容易被干掉。
那么例如用户是否登录,基本信息就不用localStorage存储了,就考虑使用vuex / redux公共状态管理,因此对性能进行了优化
服务端
与cookie相对应的就是session
,当服务器设置session,服务器返回给客户端的信息,在响应头中,会携带set-cookie="content_sid"
客户端会把信息种植到本地的cookie中,并且是http公认的,只可读不可写。
客户端再次向服务器发送请求的时候,会默认在请求头的cookie中,把content_sid传递发送给服务器,
学如逆水行舟,不进则退