坐标杭州, 18 年毕业, 算上实习一年半开发经验. 是外派的面试, 后面两面都是阿里的面试官.
本来是给我发的在线测评, 但是那边服务器出现问题, 我一直打不开网页, 最后只好以电话问答的形式. 下面我写的大部分都是测评里的题目, 部分是电话里新增的题目...
尽量使用尽可能多的方式实现子元素的垂直水平居中
- <div class="father">
- <div class="child">
- </div>
- </div>
- <style>
- .father {
- width: 300px;
- height: 300px;
- }
- .child {
- }
- </style>
child 分很多种情况, 大致说一下
行内元素: text-align + line-height
- .father{
- text-align: center;
- line-height: 300px;
- }
定宽定高: absolute + margin
- .father{
- position: relative;
- }
- .child{
- position: absolute;
- top: 50%;
- left: 50%;
- margin: -150px 0 0 -150px;
- }
- /* 或者 */
- .child{
- position: absolute;
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
- margin: auto;
- }
不定高: absolute + translate
- .father{
- position: relative;
- }
- .child{
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%,-50%);
- }
不定高: flex
- .father{
- display: flex;
- }
- .child{
- margin:auto;
- }
- /* 或者 */
- .child{
- justify-content: center;
- align-items: center;
- }
table 方式:
- .father{
- display: table;
- }
- .child{
- display: table-cell;
- text-align: center;
- vertical-align: middle;
- }
尽可能多的方式实现如下三栏布局, 要求 .main 在中间显示
- <div class="container">
- <div class="main"></div>
- <div class="sub"></div>
- <div class="extra"></div>
- </div>
浮动
flex 布局: 利用 order 属性(排序, 这个没答上来)
gird 布局: grid-template-areas
执行下面的代码, 输出结果是什么?
- console.log(+false) // 0, 这里是一元运算符进行的弱类型转换, 相对应的 +true 则返回 1
- console.log('1' + 2 + '3' + 4) // '1234' 遇到字符串就会拼接
- console.log(4 + 3 + 2 + '1') // '91' 先加在一起再拼接
运行下面的代码, 输出什么?
- var x = 3;
- var foo = {
- x: 2,
- baz: {
- x: 1,
- bar: function() {
- return this.x;
- }
- }
- }
- var go = foo.baz.bar;
- console.log(go()); // 3
- console.log(foo.baz.bar()); // 1
这题考的是 this 的指向:
this 由调用者提供, 由调用函数的方式来决定. 如果是一个对象调用的函数, 则 this 指向该对象, 比如 foo.baz.bar(). 如果函数独立调用比如 go(), 那么该函数内部的 this, 则指向 undefined. 但是在非严格模式中, 它会被自动指向全局对象 Windows.
实现以下函数, 使得输入的字符串逆序输出
- function reverse(str) {
- let res = str.split('');
- return res.reverse().join('');
- }
- reverse('hello world!'); // output: '!dlrow olleh'
进阶问题
'hello world!'.reverse(); // output: '!dlrow olleh'
这个问题需要给 String 的原型上添加方法, 但是要考虑有一些改造:
- String.prototype.reverse = function reverse() {
- let res = this.split('');
- return res.reverse().join('');
- }
实现 sleep 函数
要求用法:
- /**
- - 使当前运行的异步操作 (promise 或者 async) 停止等待若干秒
- -
- - @param ms */
- (async () => {
- console.log('hello')
- await sleep(2000) // 等待两秒
- console.log('world')
- })()
- const sleep = (ms) => {
- new Promise(resolve, reject) {
- setTimeOut(() => {
- resolve(); // 大致这样?
- }, ms)
- }
- }
实现 throttle 节流函数
用法:
- const throFun = () => console.log('hello');
- const thro = throttle(throFun, 300);
- document.body.onscroll = () => {
- thro(); // 调用至少间隔 300 毫秒才会触发一次
- }
- /**
- - 频率控制, 返回函数连续调用时, action 执行频率限定为 1 次 / delay
- - @param delay {number} 延迟时间, 单位毫秒
- - @param action {function} 请求关联函数, 实际应用需要调用的函数
- - @return {function} 返回客户调用函数 */
- function throttle(action, delay) {
- var previous = 0;
- // 使用闭包返回一个函数并且用到闭包函数外面的变量 previous
- return function() {
- var _this = this;
- var args = arguments;
- var now = new Date();
- if(now - previous> delay) {
- action.apply(_this, args);
- previous = now;
- }
- }
- }
实现 debounce 防抖函数
用法:
- const throFun = () => console.log('hello');
- const thro = debounce(throFun, 300);
- document.body.onscroll = () => {
- thro(); // 若一直调用则不会执行, 空闲时间大于 300 毫秒才会执行
- }
- /**
- - 空闲控制 返回函数连续调用时, 空闲时间必须大于或等于 delay,action 才会执行
- - @param delay {number} 空闲时间, 单位毫秒
- - @param action {function} 请求关联函数, 实际应用需要调用的函数
- - @return {function} 返回客户调用函数 */
- function debounce(action, delay) {
- var timer; // 维护一个 timer
- return function () {
- var _this = this; // 取 debounce 执行作用域的 this
- var args = arguments;
- if (timer) {
- clearTimeout(timer);
- }
- timer = setTimeout(function () {
- action.apply(_this, args); // 用 apply 指向调用 debounce 的对象, 相当于_this.action(args);
- }, delay);
- };
- }
数组去重有哪些方法?
- const arr = [1,2,3,4,4,3,2,1];
- // 方法一: new Set ES6
- return [...new Set(arr)]; // 这里又问到我... 的用法
- // 方法二: 双层 for 循环 (然后说这样性能不好, 让我只用一层 for 循环的方法)
- function unique(arr){
- var res=[];
- for (var i = 0; i <arr.length; i++) {
- for (var j = i+1; j < arr.length; j++) {
- if (arr[i] === arr[j]) {
- ++ i;
- j = i;
- }
- }
- res.push(arr[i]);
- }
- return res;
- }
- // 方法三: 单层 for 循环 + indexOf
- function unique(array){
- var res = [];
- for(var i = 0; i < array.length; i++) {
- // 如果当前数组的第 i 项在当前数组中第一次出现的位置是 i, 才存入数组; 否则代表是重复的
- if (array.indexOf(array[i]) === i) {
- res.push(array[i])
- }
- }
- return res;
- }
- // 方法三点三: 或者这样
- function unique(array){
- let res = [];
- for(var i = 0; i < array.length; i++) {
- if (res.indexOf(array[i]) === -1) {
- res.push(array[i]);
- }
- }
- return res;
- }
- // 方法四: 面试官说如果可以容忍改变原有数组的情况下, 怎么改进性能更好
- function unique(array){
- // 注意这里一定要倒叙 for 循环, 否则会出现问题
- for(var i = array.length - 1; i> 0; i--) {
- if (array.indexOf(array[i]) !== i) {
- array.splice(i, 1);
- }
- }
- // 因为少声明一个变量, 节省了内存空间(虽然可以忽略不计, 但是面试嘛~)
- return array;
- }
场景题, 考察事件代理, 事件监听, DOM 自定义属性等
题目描述:
网页中有一个元素 A, 它有个 data-href 属性, 里面存放一个链接地址
实现一个函数, 当任意点击时, 如果点击的元素就是 A 或其上层节点中找到 A, 则进行链接跳转
实现思路:
首先给 Windows 添加点击事件的监听, 然后先声明一个中间变量 targetNode 记录节点
然后循环判断当前节点 targetNode 是否存在 data-href 属性
如果存在则进行跳转, break. 不存在则将该节点的父节点赋值给 targetNode
一直循环到判断 targetNode 是最外层节点为止
面试过后整理的代码:
- Windows.addEventListener('click', (e) => {
- let targetNode = e.target
- while (targetNode !== document) { // 只要当前节点不是最外层 document
- console.log(targetNode)
- if (targetNode.getAttribute('data-href')) { // 其实用 hasAttribute 更合适
- Windows.open(targetNode.dataset.href)
- break
- } else { // 没找到就继续往上找
- targetNode = targetNode.parentNode
- }
- }
- })
总结:
感觉这道题出的很好, 回答过程中面试官也一直引导我的思路, 比如最开始我想到判断节点是否为上下级关系的方法用 DOM 的 contains()方法, 然后面试官考虑性能, 最好用原生 API. 然后我就说用 parentNode. 后来我查了一下 MDN, 发现 contains 这个方法就是原生的 API,IE5 以上就支持了:
但是这也不是重点, 重点是用 contains 这个方法不能实现这个场景, 因为你需要父节点 (上层节点) 是已知的. 但这个场景是通过已知子节点 (点击事件返回的 target) 来向上寻找节点.
我说到用 dataset 获取自定义属性的时候, 面试官说考虑到兼容性, 有没有别的方法. 我回答 getAttribute().
在说怎么一层一层的往上找节点的时候, 最开始我想到了用递归调用函数, 面试官当即打断我说没有必要, 而且浪费性能. 然后提示我说有什么循环的方法. 最后想到了 while
其他问题
ES6 数组有哪些方法
React 使用中用过哪些状态管理工具, 紧接着问了 redux 和 mobx 的区别, 面试遇到三四次了, 故总结一下:
1.Redux 鼓励一个应用只用一个 Store,Mobx 鼓励使用多个 Store; 2.Redux 是函数式编程, 而 Mobx 的模式是面向对象的; 3.Redux 使用 "拉" 的方式使用数据, 这一点和 React 是一致的, 但 Mobx 使用 "推" 的方式使用数据; 4.Redux 鼓励数据范式化, 减少冗余, Mobx 容许数据冗余, 但同样能保持数据一致. 5.Redux 更严格, 必须调用 reducer 触发 action 来改变 state, Mobx 最初的一个卖点就是直接修改数据, 但是实践中大家还是发现这样无组织无纪律不好, 所以后来 Mobx 还是提供了 action 的概念. 和 Redux 的 action 有点不同, Mobx 中的 action 其实就是一个函数, 不需要做 dispatch. 如果想强制要求使用 action, 禁止直接修改 observable 数据, 使用 Mobx 的 configure, 如下:
- import {
- configure
- } from 'mobx';
- configure({
- enforceActions: true
- });
让讲了一下 react 的虚拟 DOM, 简单讲一下 diff 算法原理
React 中 key 的作用
来源: https://juejin.im/post/5c83f7d15188257e566edcf1