一面(电话面试)
整体感觉问了很多宏观的问题
- 学校的学习经历,怎样接触前端的
- 前端的学习经验有没有形成自己的方法论
- 怎么使用GitHub,有没有什么开源贡献,怎么看待开源,有没有在GitHub上提问
- 在工作中怎样高效的去解决问题,自己查还是去提问
- 除了框架有没有用原生js去写过项目,使用的是ES6吗
- 有没有接触过其他语言
- 实习中最大的收获是什么
技术类
- 进程与线程的关系,浏览器中的进程和线程
- 怎样理解js与线程的关系
- 怎样理解Event loop与异步
- 常用的跨域请求方法有哪些,在项目中经常使用的有哪些
- ajax底层是怎么实现异步的
- jsonp有什么安全性问题,其原理等方面与CORS有何关系
- 对token有了解吗?项目中是怎么使用的,token解决了哪些安全性问题
- ES6的数组方法有用过哪些,理解这些方法的底层原理吗?与其他语言有何异同?
- ES6的数组遍历有用过哪些,forEach与for of与for in有何区别
- 怎么理解ES6的Interator,哪些数据结构有Interator,为什么要提出Interator解决了什么问题
- 有哪些常见的搜索算法,在具体的项目场景中有用过吗?
- git pull,git fetch的区别,除了功能的区别还有什么区别
- git merge 和 git rebase的区别
事业部笔试
/*
笔试注意事项:
1. 答题总时长:60min
2. 答题过程不允许线上查阅资料,允许使用浏览器自带控制台调试代码。
3. 答题过程中需全程开启摄像头。
*/
// 题目1:
// 使用递归 / 非递归方式遍历出 data 里面所有的name, 得到一个个names 数组
const data = [
{
name: '中国',
children: [
{
name: '教第三节课',
},
{
name: '教呼呼',
children: [
{
name: '大一',
children: [
{
name: '课程1',
children: [
{
name: '1231',
},
{
name: '121',
},
],
},
{
name: '课程3',
children: [
{
name: '1233',
},
],
},
],
},
],
},
{
name: '活动',
children: null,
},
],
},
];
// 使用递归实现
function getNameListRecursively(data) {
// your implementation here
const res = [];
const dfs = function(data) {
if(data && data.length) {
data.forEach((item) => {
res.push(item.name);
dfs(item.children);
});
}
}
dfs(data);
return res;
}
// 使用非递归实现
function getNameList(data) {
// your implementation here
const res = [];
const stack = [data];
while(stack.length) {
const temp = stack.shift();
temp.forEach((item) => {
res.push(item.name);
if(item.children && item.children.length) {
stack.push(item.children);
}
});
}
return res;
}
// 题目2:
/*
实现一个简易的模板引擎
let template = '嗨,{{name}}您好,今天是星期 {{day}}';
let data = {
name: '张三',
day: '三'
}
render(template, data); // 嗨,张三您好,今天是星期三
*/
function compile(tpl) {
let string = '';
tpl = tpl.trim();
while(tpl) {
const start = tpl.indexOf('{{');
const end = tpl.indexOf('}}');
if(start > -1 && end > -1) {
if(start > 0) {
string += JSON.stringify(tpl.slice(0, start));
}
string += '+ data.' + tpl.slice(start + 2, end).trim() + '+';
tpl = tpl.slice(end + 2);
if(!tpl.length) {
string = string.slice(0, string.length - 1);
}
} else {
string += JSON.stringify(tpl);
tpl = '';
}
}
return new Function('data', 'return' + string);
}
const render = function(template, data) {
return compile(template)(data);
}
// 题目3:
/**
*
* 写一个字符串转换的函数 string convert(string s, int numRows);
* 说明:把字符串“NONGCUNTAOBAO”,按Z字行重新排列如下
* N C A O
* O G U T O A
* N N B
* 然后按行输出如下“NCAOOGUTOANNB”
* 示例:
* 1:输入: s = "NONGCUNTAOBAO", numRows = 3; // 返回'NCAOOGUTOANNB'
* 2:输入: s = "NONGCUNTAOBAOJISHUBU", numRows = 4; // 返回'NNOBOUTAJUUNCABIHGOS'
*/
const convert = function(s, numRows) {
if(numRows === 1) {
return s;
}
const len = Math.min(s.length, numRows);
const rows = [];
for(let i = 0; i < len; i++) {
rows[i] = '';
}
let index = 0;
let flag = false;
for(let c of s) {
rows[index] += c;
if(index === 0 || index === numRows - 1) {
flag = !flag;
}
index += flag ? 1 : -1;
}
return rows.join('');
}
教育线笔试
// 实现一个方法,用于比较两个版本号 compareVersion(version1、version2)
// - 如果version1 > version2,返回1
// - 如果version1 < version2,返回-1
// - 其他情况返回 0
// 版本号规则 `x.y.z`,xyz均为大于等于 0 的整数,至少有 x 位
// 输入:compareVersion('0.1', '1.1.1')
// 输出:-1
const compareVersion = function(version1, version2) {
let v1 = version1.split('.');
let v2 = version2.split('.');
v1 = v1.map(item => parseInt(item));
v2 = v2.map(item => parseInt(item));
const len = Math.max(v1.length, v2.length);
while(v1.length < len) {
v1.push(0);
}
while(v2.length < len) {
v2.push(0);
}
for(let i = 0; i < len; i++) {
if(v1[i] > v2[i]) {
return 1;
} else if (v1[i] < v2[i]) {
return -1;
}
}
return 0;
}
compareVersion('0.1', '1.1.1')
// 实现 EventEmitter 类,提供 on,off,once 和 emit 的链式调用 API,支持单页应用不同路由间通信
// import eventEmitter from "./EventEmitter"
// eventEmitter.on('changeA',(evt)=>{
// console.log(evt);
// }).once('changeB',(evt)=>{
// console.log(evt);
// });
// eventEmitter.emit('changeA',['OK1']);
// eventEmitter.emit('changeA',['OK2']);
// eventEmitter.emit('changeB',['OK3']);
// eventEmitter.emit('changeB',['OK4']);
// 输入如下结果:
// ['OK1']
// ['OK2']
// ['OK3']
class EventEmitter {
constructor() {
this.events = {};
}
on(event, callback) {
let callbacks = this.events[event] || [];
callbacks.push(callback);
this.events[event] = callbacks;
return this;
}
off(event, callback) {
if(this.events[event] && callback) {
let callbacks = this.events[event];
this.events[event] = callbacks.filter(fn => fn !== callback);
}
return this;
}
once(event, callback) {
const tempFn = (...params) => {
callback.apply(this, params);
this.off(event, tempFn);
}
this.on(event, tempFn);
return this;
}
emit(event, params) {
const callbacks = this.events[event];
if(callbacks && callbacks.length) {
callbacks.forEach(fn => fn.apply(this, params));
}
return this;
}
}