前言:正所谓金三银四,我也在三月份面了一次荔枝FM,初试是做一套比较简单的笔试题,发出来跟大家简单探讨一下,面试时的一些问题主要是通过这些笔试题进行拓展,例如flex布局、改变正则条件,promise事件机制等等,会在下一篇文章中提到,这篇文章仅为初面的笔试题
1、变量提升和函数提升
function outter () {
return inner;
function inner () {}
var inner;
inner = 9;
}
//问题是下面代码执行输出值是什么:
typeof outter();
复制代码
答案:这道题不单只是考变量提升,因为这里还有函数提升,那就是还要考函数提升和变量提升究竟谁优先级更高了。
根据 《你不知道的JavaScript(上卷)》 里的说法,js引擎在解释代码之前会先进行编译,编译阶段会将所有的变量声明和函数声明与相关作用域关联起来,并且将这些声明提升到作用域顶部,然后到代码执行阶段才会进行变量赋值。
虽然第一行代码就是return inner
, 但是根据上面的结论,var inner
的变量声明和function inner() {}
的函数声明会第一时间被提升到顶部。
那么剩下的问题就是究竟函数优先还是变量优先,答案自然也在书中找得到,那就是函数优先,函数优先提升后,由于inner
已经被声明为函数变量,后面var inner
时编译器进行LHS,也就是左查询,发现同一作用域中已经有该值会忽略该变量声明,因此这题的答案是字符串'function'
;
2、用html和css实现一个宽度为屏幕45%的正方形,可使用最新标准
<html>
<div class="box">div>
html>
<style>
.box {
width: 45vw;
height: 45vw;
}
style>
复制代码
我这是使用最新标准也最简单的实现方式了,大家可以在评论写一写自己的实现方式。
3、写出6个div元素的堆叠顺序,最上面的在第一个位置,例如: .one .two .three .four .five .six(z-index)
html:
<div class="one">
<div class="two">div>
<div class="three">div>
div>
<div class="four">
<div class="five">div>
<div class="six">div>
div>
复制代码
css:
.one {
position: relative;
z-index: 2;
.two {
z-index: 6;
}
.three {
position: absolute;
z-index: 5;
}
}
.four {
position: absolute;
z-index: 1;
.five {}
.six {
position: absolute;
top: 0;
left: 0;
z-index: -1;
}
}
复制代码
PS:其实这道题代码写的不大好,因为没有div宽度和高度,甚至.one和.four也不重叠,就算自己把代码跑起来也是无法看出是否重叠的,因此需要自己去加点东西,不过主要知道考点是什么就好了。
解析如下:从w3c的文档可以知道,z-index属性设置一个定位元素沿 z 轴的位置,z 轴定义为垂直延伸到显示区的轴。如果为正数,则离用户更近,为负数则表示离用户更远。
- 没有定位的元素z-index是不生效的
- 定位元素拥有更高堆叠顺序的元素总是会处于堆叠顺序较低的定位元素的前面
- 子元素必然在父元素的前面,不管是否是定位元素
- 同级定位元素的z-index相同时遵循“后来居上”,后面的定位元素堆叠顺序更前
- z-index小于0的定位元素处于所有元素的后面,仅在层叠上下文的背景颜色和边框前面
通过上面几点可以得出答案是 .three .two .one .five .six .four,但是这只是元素层叠顺序的冰山一角,更深入的了解可以读一下张旭鑫大神的文章。
还有疑问的可以直接通过代码看效果:元素z-index的堆叠顺序
拓展阅读:深入理解CSS中的层叠上下文和层叠顺序
4、写出能匹配以下两个要求的正则表达式
(1)字符串长度为8-12
(2)字符串只包含英文和数字
这道题算是送分题,只要对正则有所了解就能答出,而且答案不止一个,大家可以写写看自己的答案
/(\d|[A-z]{8,12})/
复制代码
5、四个请求接口分别是/a、/b、/c、/d,算出最快请求回来的那个接口的耗时时长(可用setTimeout模拟异步请求)
function a () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('/a');
}, 1000);
});
}
function b () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('/b');
}, 2000);
});
}
function c () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('/c');
}, 500);
});
}
function d () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('/d');
}, 3000);
});
}
// Promise.race竞赛方法在第一个请求resolve后就立马进入then,可以算出最快接口的时间
const startTime = new Date / 1;
Promise.race([a(), b(), c(), d()]).then(o => {
const endTime = new Date / 1;
console.log(`接口${o}最快完成请求,请求时长为${endTime - startTime}`);
});
复制代码
6、实现一个模态框dialog
要求:构造函数不能直接执行,必须使用new,每个实例不互相影响参数列表如下
参数名 | 类型 | 默认值 |
---|---|---|
title | String | XXXX(不记得了) |
body | String | '' |
cancelText | String | 取消 |
okText | String | 确认 |
具有show和close方法,可以通过on绑定相关事件,off解绑相关事件。
需要达成效果如下:
let dialog = new Dialog();
dialog.on('show', () => {
console.log('Dialog Show');
});
dialog.on('close', () => {
console.log('Dialog Close');
});
dialog.show(); // 输出Dialog Show;
setTimeout(() => {
dialog.close(); // 输出Dialog Close;
}, 3000);
复制代码
答案:这道题看起来也是少了很多细节, 不过这是一种常用的弹窗组件,因此我们可以自己给它补上应有的功能。
class Dialog {
// show方法的回调函数数组
showCallbacks = [];
// close方法的回调函数数组
closeCallbacks = [];
// 构造函数初始化参数
constructor(options) {
this.options = Object.assign(
{
title: "title",
body: "是否确认是一个dialog",
cancelText: "取消",
okText: "确认"
},
options
);
document.body.appendChild(this.renderTemplate());
this.initEvent();
}
// 渲染弹窗模板方法
renderTemplate() {
const { title, body, cancelText, okText } = this.options;
this.container = document.createElement("div");
this.container.classList = "dialog-container";
this.container.innerHTML = `${title}
${body}
`;
return this.container;
}
// 初始化事件
initEvent() {
this.container
.querySelector(".dialog-confirm")
.addEventListener("click", () => {
this.close();
});
this.container
.querySelector(".dialog-cancel")
.addEventListener("click", () => {
this.close();
});
}
// 事件绑定
on(type, callback) {
if (!callback || typeof callback !== "function")
throw new Error("callback必须是函数");
this[`${type}Callbacks`].push(callback);
}
// 事件解绑
off(type, callback) {
if (callback && typeof callback === "function") {
// 将函数toString后在回调数组中查找对比。
callback = callback.toString();
this[`${type}Callbacks`].forEach((v, i) => {
// 同样的回调函数将一次性全部解绑
if (v.toString === callback) {
// 删除事件回调数组中的值
this[`${type}Callbacks`].splice(i, ++i);
}
});
return;
}
// 如果不传callback,清空整个回调函数数组
this[`${type}Callbacks`] = [];
}
// show方法,执行show回调函数数组
show() {
this.container.style.display = "block";
this.showCallbacks.forEach(callback => {
callback();
});
}
// close方法,执行close回调函数数组
close() {
this.container.style.display = "none";
this.closeCallbacks.forEach(callback => {
callback();
});
}
}
复制代码
示例代码:实现一个Dialog弹窗
最后这道题其实有两道题,选择其中之一作答,不过时间有限,我没做另一道题所以没记住问题是什么,顺便吐槽一下那个答题页面打代码时真的很不顺手,严重影响答题效率。
上面的答案可能并不一定对或者并不是最佳答案,毕竟个人答案或有缺漏,希望有更好答案的朋友可以在评论写一下,谢谢大家的阅读。