平时遇到的前端面试题
我最近一个多月都在忙着找工作,这些是在面试过程中遇到的一些面试题。
10 + ‘20’ = ? 再加 ‘20’ 呢?
10 + '20' = '1020'
'1020' + '20' = '102020'
请写出 Ajax 请求中用到的函数
var xhr = new XMLHttpRequest();
xhr.open('GET/POST', 'http://localhost:8080?username=meishadevs', true);
xhr.onreadystatechange = function() {};
xhr.send();
使用正则表达式提取出url值为 https://map.baidu.com/x/y/z 中的 map.baidu.com
// 会匹配两次,第一次使用整个正则表达式去匹配,第二次使用子正则表达式去匹配,匹配结果以一个数组的形式返回
/https:\/\/([^\/]+)/.exec("https://map.baidu.com/x/y/z")[1];
// 会匹配两次,第一次使用整个正则表达式去匹配,第二次使用子正则表达式去匹配,匹配结果以一个数组的形式返回
"https://map.baidu.com/x/y/z".match(/https:\/\/([^\/]+)/)[1];
// 会采用子正则表达式去匹配,并返回与第一个子表达式匹配的结果
"https://map.baidu.com/x/y/z".replace(/^https:\/\/([^\/]+)(\/[a-z])+$/, "$1");
执行结果

使用 ES6 的方法实现数组去重
let array = [1, 1, 1, 1, 2, 3, 4, 4, 5, 3];
let set = new Set(array);
let arr = Array.from(set);
设计一个函数实现 add(3)(4)
function add(a) {
return function(b) {
return a + b;
}
}
add(3)(4)
ES6的写法
add = a => b => a + b
add(3)(4)
对ES6写法做一个变形
add = a => (b => a + b)
再变
add = function (a) {
return (b => a + b)
}
再变
add = function (a) {
return function (b) {
return a + b;
}
}
使用Ajax实现一个表单提交功能,并跳转到提交的地址,(可以使用 jQuery 或 Zepto)
$.ajax({
url: "http://page/login",
type: "post",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({
username: "meishadevs",
password: "000000"
}),
success:function (res) {
// 跳转到首页
window.location = "http://page/home"
},
error:function (error) {
console.log("error:", error);
}
});
在完成上一题后使用 Promise 再实现一遍
function sendJSON(url) {
// 创建一个 promise 对象
let promise = new Promise(function(resolve, reject) {
let xhr = new XMLHttpRequest();
// 新建一个 http 请求
xhr.open("POST", url, true);
// 设置状态的监听函数
xhr.onreadystatechange = function() {
if (this.readyState !== 4) {
return;
}
// 当请求成功或失败时,改变 promise 的状态
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
// 设置错误监听函数
xhr.onerror = function() {
reject(new Error(this.statusText));
};
// 设置响应的数据类型
xhr.responseType = "json";
// 设置请求头信息
xhr.setRequestHeader("Accept", "application/json");
// 发送 http 请求
xhr.send(null);
});
return promise;
}
JavaScript中的基本数据类型
Number、String、Boolean、Null、Undefined
一道响应式布局的题目
call、apply的作用与区别
call 和 apply 都是为了改变函数体内部 this 的指向,它们的第一个参数都是调用函数的对象,call方法接收由若干个参数组成的参数列表,apply方法接收的是一个包含多个参数的数组
例如使用call获取一组数字中的最大值
Math.max.call(null, 1, 3, 5, 8, 2)
例如使用apply获取一组数字中的最大值
Math.max.apply(null, [1, 3, 5, 8, 2])
标准盒模型和怪异盒模型的区别
标准盒模型的 box-sizing 属性的值为 content-box
怪异盒模型的 box-sizing 属性的值为 border-box
标准盒模型中 width = 内容的宽度
怪异盒模型中 width = 内容的宽度 + padding + border
em 与 rem 的区别
rem 表示根节点(html标签)的字体大小的倍数
当 em 作为 font-size 的单位时,表示相对于父元素的 font-size 值的倍数
当 em作为其他属性单位时,代表自身字体大小的倍数
localStorage 与 sessionStorage 的区别
使用 localStorage 保存的数据,除非手动清除,否则会永久保存
使用 sessionStorage 保存的数据仅在当前会话下有效,关闭页面或浏览器后会被清除
元素层叠
参考张鑫旭大神的文章:深入理解CSS中的层叠上下文和层叠顺序
使用原型现继承
使用 __proto__ 实现继承(不推荐使用这种方式)
//创建animal对象
var animal = {
name: "animal",
eat: function () {
console.log(this.name + " is eating");
}
};
//创建dog对象
var dog = {
name: "dog",
//指向animal对象(dog继承自animal)
__proto__: animal
};
//创建cat对象
var cat = {
name: "cat",
//指向animal对象(cat继承自animal)
__proto__: animal
};
dog.eat();
cat.eat();
使用 prototype 实现继承
//创建animal对象
var animal = {
name: "animal",
eat: function () {
console.log(this.name + " is eating");
}
};
//创建构造函数Dog
function Dog() {
this.name = "dog";
}
//创建构造函数Cat
function Cat() {
this.name = "cat";
}
//设置Dog的原型为animal(Dog继承自animal)
Dog.prototype = animal;
//设置Cat的原型为animal(Cat继承自animal)
Cat.prototype = animal;
//创建dog对象
var dog = new Dog();
//创建cat对象
var cat = new Cat();
dog.eat();
cat.eat();
JavaScript 中事件绑定的方式
button.onclick = function() {}
button.addEventListener("click", function () {});
设计一个函数实现判断一个数据的数据类型是不是数组
function isArray(num) {
return num instanceof Array;
}
什么是浏览器的同源策略?
协议相同、域名相同、端口号相同
什么是浏览器的标准模式和怪异模式?
参考MDN上的一篇文章:怪异模式和标准模式
预处理CSS有什么优点和缺点?
优点:可以实现重用、自动加前缀等
缺点:浏览器不能直接识别,需要转换成 CSS
Web性能优化的手段有哪些?
1、减少HTTP请求数
2、使用CDN
3、给HTTP头部添加过期时间
4、使用Gzip压缩
5、样式表置于页面最前面
6、把JavaScript脚本放在最后面
7、使用外部的JavaScript和CSS
8、压缩JavaScript和CSS
9、如果使用小图标,可以将小图标制成一张雪碧图
常见的 HTTP 状态码有哪些?
参考我网上找到的一篇文章:HTTP常见状态码 200 301 302 404 500 - starof - 博客园
下面代码的执行结果
var a = 1;
(function () {
console.log(a);
var a = 2;
console.log(a);
})()
输出的结果是 undefined, 2
因为这里存在变量提升,所以上面的代码可以写成下面的形式
var a = 1;
(function () {
var a;
console.log(a);
a = 2;
console.log(a);
})()
下面代码的输出结果
var User = {
count: 1,
getCount: function () {
return this.count;
}
};
var func = User.getCount;
console.log(User.getCount());
console.log(func());
输出的结果是 1, undefined,因为第一个console输出中this指向的是User对象,count是User对象的属性,第二个console输出中this指向的是window对象,window对象中没有count属性,所以输出 undefined
如何理解this
1、构造函数中的this指向新创建的实例对象
function Person(name, age) {
this.name = name;
this.age = age;
this.printThis = function () {
console.log(this);
}
}
var person = new Person("meishadevs", 24);
person.printThis();
执行结果
2、普通函数中的this指向函数的调用者
function printThis() {
console.log(this);
}
printThis();
执行结果
3、自执行函数中的this指向window对象
(function () {
console.log(this);
})();
执行结果
4、箭头函数中的this指向函数定义时所在的对象
// 定义箭头函数,打印箭头函数中的 this
let printThis = () => {
console.log(this);
}
let o = {
print: function() {
printThis()
}
};
// 在全局调用箭头函数
printThis();
// 在对象的方法中调用箭头函数
o.print()
执行结果
通过执行结果可知我们定义的箭头函数,不管是在哪里使用 this 指向都没有发生变化都是指向函数定义时所在的对象

写出以字母开头,可以包含字母或数字的4—10位的用户密码的正则表达式
/^[a-zA-Z][a-zA-Z0-9]{3,9}$/.test(password);
实现 child 元素在 parent 元素中水平垂直居中
方法一:position + transform
.parent {
position: relative;
}
.child {
transform: translate(-50%, -50%);
position: absolute;
left: 50%;
top: 50%;
}
方法二:使用flex布局
.parent {
display: flex;
justify-content: center;
align-items: center;
}
DOM事件流包括那三个阶段
第一阶段:从window对象传导到目标节点,称为“捕获阶段”(capture phase)
第二阶段:在目标节点上触发,称为“目标阶段”(target phase)
第三阶段:从目标节点传导回window对象,称为“冒泡阶段”(bubbling phase)
写出js代码合并下列两个数组,并去重
let arr1 = [2, 6, 1, 3, 8];
let arr2 = [5, 2, 7, 6, 4];
实现代码
let arr1 = [2, 6, 1, 3, 8];
let arr2 = [5, 2, 7, 6, 4];
let arr3 = arr1.concat(arr2);
let set = new Set(arr3);
let arr4 = Array.from(set);
console.log(arr4);
下面这段代码的输出结果是,如何使它输出1、2、3、4
for (var i = 1; i < 5; i++) {
setTimeout(function () {
console.log(i);
});
}
输出4个5,使用两种方法可以输出1、2、3、4
方法1:使用自执行函数
for (var i = 1; i < 5; i++) {
(function (num) {
setTimeout(function () {
console.log(num);
});
})(i);
}
方法2:将var改成let,因为使用var声明的变量没有局部作用域,为了解决这个问题才在ES6中引入了let
for (let i = 1; i < 5; i++) {
setTimeout(function () {
console.log(i);
});
}
4,4,10,10,加减乘除,怎么出24点?四个数字分别只能用一次
(10 * 10 - 4) / 4
如果你有无穷多的水,一个容积为3L的和5L的提桶,你如何准确称出4L的水
第一步:将装5L水的水桶装满
装5L水的水桶中装了5L水
装3L水的水桶没有装水
第二步:将装5L水的水桶中的水倒入只能装3L水的水桶中,并倒满只能转3L水的水桶
装5L水的水桶中装了2L水
装3L水的水桶中装了3L水
第三步:将装3L水的水桶中的水倒掉
装5L水的水桶中装了2L水
装3L水的水桶为空
第四步:将装5L水的水桶中的2L水倒入装3L水的水桶中
装5L水的水桶为空
装3L水的水桶中装了2L水
第五步:将装5L水的水桶装满
装5L水的水桶中装了5L水
装3L水的水桶中装了2L水
第六步:将装满5L水的水桶中的水倒入装3L水的水桶中,并倒满只能装3L水的水桶
装5L水的水桶中装了4L水
装3L水的水桶中装了3L水
如何实现快速排序
var quickSort = function (arr) {
//如果数组中只有一个元素
if (arr.length <= 1) {
//直接返回改数组
return arr;
}
//获得数组中中间值的索引(基准的索引)
var pivotIndex = Math.floor(arr.length / 2);
//获得数组中的中间值(数组中的基准)
//并将该值从原数组中删除
var pivot = arr.splice(pivotIndex, 1)[0];
//创建数组,用于保存原数组中小于基准的值
var left = [];
//创建数组,用于保存原数组中大于基准的值
var right = [];
//遍历原数组
for (var i = 0; i < arr.length; i++) {
//如果原数组的值小于基准
if (arr[i] < pivot) {
//将数组的值保存到left数组中
left.push(arr[i]);
//如果原数组的值大于或等于基准
} else {
//将数组的值保存到right数组中
right.push(arr[i]);
}
}
//使用递归不断重复这个过程
return quickSort(left).concat([pivot], quickSort(right));
};
var array = [85, 24, 63, 45, 17, 31, 96, 50];
console.log('排序前:', array);
var arr = quickSort(array);
console.log('排序后:', arr);
http 请求中的三次握手

参考链接
- 深入理解CSS中的层叠上下文和层叠顺序
- Javascript:一个屌丝的逆袭
- javascript王国的一次旅行,一个没有类的世界怎么玩转面向对象?
- javascript王国之函数教主
- 怪异模式和标准模式
- HTTP常见状态码 200 301 302 404 500 - starof - 博客园
- 快速排序(Quicksort)的Javascript实现
- 由浅入深,66条JavaScript面试知识点
meishadevs欢迎任何形式的转载,但请务必注明出处,尊重他人劳动成果。
转载请注明: 【文章转载自meishadevs:平时遇到的前端面试题】