【面试】阿里钉钉事业部前端开发暑期实习面试

235 阅读3分钟

一面(电话面试)

整体感觉问了很多宏观的问题

  1. 学校的学习经历,怎样接触前端的
  2. 前端的学习经验有没有形成自己的方法论
  3. 怎么使用GitHub,有没有什么开源贡献,怎么看待开源,有没有在GitHub上提问
  4. 在工作中怎样高效的去解决问题,自己查还是去提问
  5. 除了框架有没有用原生js去写过项目,使用的是ES6吗
  6. 有没有接触过其他语言
  7. 实习中最大的收获是什么

技术类

  1. 进程与线程的关系,浏览器中的进程和线程
  2. 怎样理解js与线程的关系
  3. 怎样理解Event loop与异步
  4. 常用的跨域请求方法有哪些,在项目中经常使用的有哪些
  5. ajax底层是怎么实现异步的
  6. jsonp有什么安全性问题,其原理等方面与CORS有何关系
  7. 对token有了解吗?项目中是怎么使用的,token解决了哪些安全性问题
  8. ES6的数组方法有用过哪些,理解这些方法的底层原理吗?与其他语言有何异同?
  9. ES6的数组遍历有用过哪些,forEach与for of与for in有何区别
  10. 怎么理解ES6的Interator,哪些数据结构有Interator,为什么要提出Interator解决了什么问题
  11. 有哪些常见的搜索算法,在具体的项目场景中有用过吗?
  12. git pull,git fetch的区别,除了功能的区别还有什么区别
  13. 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;
    }
}