今天总结一下鹅厂初面的经历,是远程视频面,本来最晚面试时间是七点的,但因为我还在实习晚上六点半才下班,面试官帮我延迟到了八点,挺不好意思的。总的来说面试问的问题不难,面试官也挺好的,虽然面试前做好了被虐的准备,但是真正面试的时候,心态没有摆正还是紧张了,回答的不好,秋招首战算是凉凉了。。
目录
- 异步处理发展的过程
- 前端跨域问题处理方法
- 什么是预请求,何时会触发
- ES6类声明与构造器原型继承的区别
- 子类怎样调用父类同名方法
- 说一下你的项目,具体做了哪些模块
- 有什么问题想问的吗
异步处理发展的过程
我们都知道,js是一个单线程的执行环境,原因是js最初只是用来做简单的浏览器交互,操作一下DOM,如果设计成多线程的话,操作DOM有可能出现问题,比如一个线程读取DOM节点数据的同时,另一个线程把那个DOM节点删了。。所以设计者认为js一个线程就够了,代码一步一步顺序执行;但如果一个任务耗时很长的话,后面的任务必须排队等着,这样可能会造成浏览器无响应,用户体验不好,为了解决这个问题,js语言的执行模式分为两种:同步和异步。在浏览器端,耗时长的操作都应该异步执行,处理异步的几种方法:
回调函数
什么叫回调函数?回调是一个函数被作为一个参数传递到另一个函数里,在那个函数执行完后再执行。
举个栗子 有两个函数f1和f2,后者等待前者的执行结果,如果f1是一个很耗时的任务,就可以考虑改写f1,把f2写成f1的回调函数
function f1(callback){
// 把代码放到异步处理
setTimeout(function () {
// f1 的代码
callback()
}, 1000);
}
f1(f2)
如果出现多个回调嵌套时,就可能出现回调地狱问题
总结: 回调函数是异步编程的基本方式,其优点是简单、容易理解,缺点是代码高度耦合,流程比较混乱,不符合我们的思维方式。
事件监听
这种方式是采用事件驱动模式,任务的执行不取决代码的顺序,而取决于监听的事件是否发生。 还是以f1 和f2为例,采用事件监听和改写成:
// 设置监听事件
f1.on('done', f2)
function f1 () {
// f1 的代码
//执行完代码后触发事件
f1.trigger('done')
}
总结: 这种方法可以绑定多个事件,每个事件可以指定多个回调函数,而且看出代码松耦合,有利于实现模块化;缺点是整个程序变成是事件驱动型,流程可能变得不清晰
Promise
Promise是ES6新增的一个异步编程的选择
还没了解Promise用法的同学可以看这里 传送门
使用Promise改写上面的例子
var p = new Promise((resolve, reject) => {
setTimeout(() => {
// f1 的代码
console.log('start f1')
// 执行完毕后
resolve('success')
}, 1000)
})
p.then(f2)
当有多个回调函数时
f1().then(f2).then(f3)
总结:使用Promise可以将回调函数写成链式调用,程序执行流程清楚
async await
await语法是ES2017确定的异步编程方式,其可以将异步代码当作同步代码来书写
举个栗子
function getCode(){
return axios.get('json/code.json');
}
function getlist(params){
return axios.get('json/person.json',{params})
}
// promise处理
function getResult () {
getCode().then(res => {
const res = res.data
if (res.code) {
const params = res.id
getList(params).then(list => {
console.log('获取到最终数据' + list)
})
}
})
}
getResult()
// await处理
async function getResult () {
const res = await getCode()
const params = res.data.id
const list = await getList(params)
console.log('获取到最终数据' + list)
}
await意思是等一下,它会将后面的函数包装为Promise,await会在Promise被 拒绝的情况下抛出错误,否则它将返回该Promise被决议的值。由上面的栗子看出,await处理异步比Promise更为优雅
前端跨域问题处理方法
这个问题我有总结过,传送门
什么是预检请求,何时会触发
这个问题之前没怎么了解,没回答上来,有点尴尬
预请求是跨域资源共享(CORS)标准规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求求),浏览器必须首先使用OPTIONS方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求
发送预检请求的条件
- 使用了
PUT,DELETE,CONNECT,OPTIONS等HTTP方法 - 人为设置了对
CORS安全的首部字段集合之外的其他首部字段 - Content-type的值不属于下列之一:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
举个栗子 一个需要执行预检请求的HTTP请求
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
function callOtherDomain(){
if(invocation)
{
invocation.open('POST', url, true);
invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
invocation.setRequestHeader('Content-Type', 'application/xml');
invocation.onreadystatechange = handler;
invocation.send(body);
}
}
发送的预检请求会携带下面两个首部字段:
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
预检请求的响应为:
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
ES6类声明与构造器原型继承的区别
主要的区别有:
- 类表达式/类声明的函数都不会被提升,与let相识,存在临时性死区
- 代码自动运行在严格模式下,且无法退出
- 不能缺少
new调用 class函数内部无法重写类名- 类所有方法都没有
[[constuct]]指针,即不能使用new调用,不可枚举 - 能动态的决定所要继承的类,因为
extends接受任意能返回具有[[Construct]]属性的函数的表达式
以上是我总结出的两者间的区别,有什么遗漏或错误的地方欢迎提出
子类怎样调用父类同名方法(子类的原型是父类)
ES5的方法
let person = {
sayHello () {
return 'Hello'
}
}
let friend = {
sayHello () {
return Object.getPrototypeOf(this).sayHello.call(this)
}
}
Object.setPrototypeOf(friend, person)
console.log(friend.sayHello()) // Hello
ES6的方法
let person = {
sayHello () {
return 'Hello'
}
}
let friend = {
sayHello () {
return super.sayHello()
}
}
Object.setPrototypeOf(friend, person)
console.log(friend.sayHello()) // Hello
说一下你的项目,具体做了哪些模块
这个就不说了~
有什么问题想问的吗
面了大概四十多分钟,整个面试我都表现的不好,当面试官说有什么问题想问的吗,我想直接一点,就问了我有机会进入下一轮面试么。。面试官说得回去评估一下
总结
虽然这次面试失败了,总的来说也是一个不错的体验,发现了自己一些不足的地方。很快就到九月份了,还是静下心来好好准备秋招吧