该文章用来记录一些常见的前端简单面试题,这些题目部分是因为比较细碎,部分是比较偏门(对某个不常用功能的深入知识),部分是因为还没来得及,所以没有单独列出文章来讲述。

基础(基本概念)

HTML

CSS

display:none 与 visibility:hidden 的区别是什么?还有哪些隐藏元素的方法?

区别:

  1. display:none; 会让元素完全从渲染树中消失,渲染的时候不占据任何空间;visibility: hidden; 不会让元素从渲染树消失,渲染时元素继续占据空间,只是内容不可见
  2. display: none; 是非继承属性,子孙节点消失由于元素从渲染树消失造成,通过修改子孙节点属性无法显示;visibility: hidden; 是继承属性,子孙节点消失由于继承了hidden,通过设置 visibility: visible; 可以让子孙节点显式。
  3. 修改常规流中元素的 display 通常会造成文档重排。修改 visibility 属性只会造成本元素的重绘。
  4. 读屏器不会读取 display: none; 元素内容;会读取 visibility: hidden; 元素内容。

tips: 还有两种隐藏元素的方法:可以把元素移动到屏幕以外,或设置透明度 opacity:0; 为透明,也可以使用透明度滤镜 filter:opacity(0)

  1. link 是 HTML 方式, @import 是 CSS 方式
  2. link 最大限度支持并行下载,@import 过多嵌套导致串行下载,出现FOUC(Flash Of Unstyled Content)页面闪烁
  3. link 可以通过 rel="alternate stylesheet" 指定候选样式
  4. 浏览器对 link 支持早于 @import ,可以使用 @import 对老浏览器隐藏样式
  5. @import 必须在样式规则之前,可以在 css 文件中引用其他文件

css sprite是什么,有什么优缺点?

概念:将多个小图片拼接到一个图片中。通过 background-position 和元素尺寸调节需要显示的背景图案。

优点:

  1. 减少HTTP请求数,极大地提高页面加载速度
  2. 增加图片信息重复度,提高压缩比,减少图片大小
  3. 更换风格方便,只需在一张或几张图片上修改颜色或样式即可实现

缺点:

  1. 图片合并麻烦
  2. 维护麻烦,修改一个图片可能需重新布局整个图片,样式

为什么要初始化CSS样式?

因为浏览器的兼容问题,不同浏览器对有些标签的默认值是不同的,如果没对CSS初始化往往会出现浏览器之间的页面显示差异。

如何通过a标签批量下载文件?

<a> 标签的download属性指示浏览器下载而不是导航,参见 a标签的属性download ,然而该属性只适用于同源URLs,我们可以通过将地址转换为 blob:URL 的形式然后下载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let xhr = new XMLHttpRequest();
xhr.open("get", "https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1593106255,4245861836&fm=26&gp=0.jpg", true);
xhr.responseType = "blob";
xhr.onload = function() {
if (this.status == 200) {
const blob = this.response;
const ele = document.createElement('a');
ele.download = name;
ele.style.display = 'none';
ele.href = URL.createObjectURL(blob);
ele.onload= function(e) {
URL.revokeObjectURL(ele.href);
document.body.removeChild(ele);
};
document.body.appendChild(eleLink);
ele.click();
}
}
xhr.send();

JavaScript

Object.keys() 和 for … in 遍历对象属性有什么不同?

for-in 循环还会枚举原型链中的属性

for … in 遍历和 for … of 遍历有什么不同?

for...of 语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。

for...in 语句以任意顺序遍历一个对象的除 Symbol 以外的可枚举属性(包括原型链中的属性)。

for…in 和 for…of 的区别在于:

for…in 语句以任意顺序迭代对象的可枚举属性。

for…of 语句遍历可迭代对象定义要迭代的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';

for (let i in iterable) {
console.log(i); // 打印: 0, 1, 2, "foo", "arrCustom", "objCustom"
}

for (let i in iterable) {
if (iterable.hasOwnProperty(i)) {
console.log(i); // 打印: 0, 1, 2, "foo"
}
}

for (let i of iterable) {
console.log(i); // 打印: 3, 5, 7
}

为什么使用delete操作符删除对象时需要遍历属性中的属性,依次删除?

1
2
3
a = {p:{x:1}};
b=a.p;
delete a.p;

执行这段代码后b.x的值依然是1。由于已经删除的属性的引用依然存在,因此在JavaScript的某些实现中,可能因为这种不严谨的代码而造成内存泄漏。所以在销毁对象的时候,要遍历属性中的属性,依次删除。

解释 null 和 undefined 的区别

undefined 表示系统级的、出乎意料的或类似错误的值的空缺,null表示程序级的、正常的或在意料之中的值的空缺。

1
2
typeof null // 'object'
typeof undefined // 'undefined'

解释 == 与 === 的区别

“===”叫做严格相等运算符(恒等运算符),”==”叫做相等运算符。
严格相等运算符的运算规则如下,

  • 不同类型值
    如果两个值的类型不同,直接返回false。
  • 同一类的原始类型值
    同一类型的原始类型的值(数值、字符串、布尔值)比较时,值相同就返回true,值不同就返回false。
  • 同一类的复合类型值
    两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个对象。
  • undefined和null
    undefined 和 null 与自身严格相等。

创建对象的三个方法是什么?

  1. 对象直接量
1
var x = {};
  1. 通过关键字 new 创建对象
1
var x = new Object()
  1. 通过 Object.create() 创建对象
1
var x = Object.create(Object.prototype);

Array.of() 和 new Array() 创建的数组有什么不同?

Array.of()Array 构造函数之间的区别在于处理整数参数:Array.of(7) 创建一个具有单个元素 7 的数组,而 new Array(7) 创建一个长度为7的空数组(注意:这是指一个有7个空位(empty)的数组,而不是由7个undefined组成的数组)。

解释 var let const 的区别

var :

  1. 默认声明变量形式
  2. 全局作用域/函数作用域
  3. 存在变量提升
  4. 声明的全局对象同时为顶层对象的属性
  5. 可以重复声明,相当于赋值

let :

  1. 块级作用域
  2. 不存在变量提升
  3. 会在当前区域形成暂时性死区
  4. 不允许重复声明
    • 块级作用域的出现,实际上使得获得广泛应用的立即执行函数表达式(IIFE)不再必要了

const :

  1. const声明一个只读的常量。一旦声明,常量的值就不能改变。
  2. const必须在声明时初始化
  3. 块级作用域
  4. 不存在变量提升
  5. 会在当前区域形成暂时性死区
  6. 不允许重复声明
  7. const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。

解释 ()=>{} 箭头函数的作用

  1. 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
  2. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  3. 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
  4. 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

解释 ... 扩展运算符的作用

将一个数组转为用逗号分隔的参数序列。

解释 字符串模板 的作用

在需要将变量和字符串进行拼接时使用字符串模板写法更优雅

简述JS函数柯里化是什么,有什么意义?

柯里化是函数式编程的一个过程,在这个过程中我们能把一个带有多个参数的函数转换成一系列的嵌套函数。它返回一个新函数,这个新函数期望传入下一个参数。

others

解释 offsetLeft offsetTop 等参数含义

HTMLElement.offsetLeft 是一个只读属性,返回当前元素左上角相对于 HTMLElement.offsetParent 节点的左边界偏移的像素值。
HTMLElement.offsetTop 是一个只读属性,它返回当前元素相对于其 HTMLElement.offsetParent 元素的顶部内边距的距离。
HTMLElement.offsetWidth 是一个只读属性,返回一个元素的布局宽度。
HTMLElement.offsetHeight 是一个只读属性,它返回该元素的像素高度,高度包含该元素的垂直内边距和边框,且是一个整数。

常用正则表达式如何书写?

(手机号)(邮箱地址)(url地址)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 手机号
function isPhoneAvailable(str) {
var myreg = /^1[3,4,5,7,8]\d{9}$/;
return myreg.test(str);
}
// 邮箱
function isMailAvailable(str) {
var myreg = /^([\w\-\.]+)@([\w\-]+)\.([a-zA-Z]{2,4})$/;
return myreg.test(str);
}
// url
function isUrlAvailable(str) {
var myreg = /https?:\/\/[\w|\.|-]+(\.cn|\.com)[\w|\?|=|\/]*/;
return myreg.test(str);
}

为什么js在html尾部引入,CSS在头部引入比较合适?

网站加载的整个完整过程

  1. 首先浏览器从服务器接收到html代码,然后开始解析html
  2. 构建DOM树(根据html代码自顶向下进行构建),并且在同时构建渲染树
  3. 遇到js文件加载执行,将阻塞DOM树的构建;遇到CSS文件,将阻塞渲染树的构建
    (script标签中的defer属性:构建DOM树的过程和js文件的加载异步(并行)进行,但是js文件执行需要在DOM树构建完成之后 script标签中的async属性:构建DOM树、渲染树的过程和js文件的加载和执行异步(并行)进行)

综上所述,script标签最好放在标签的前面,因为放在所有body中的标签后面就不会出现网页加载时出现空白的情况,可以持续的给用户提供视觉反馈,同时在有些情况下,会降低错误的发生。
而CSS标签应该放在标签之间,因为如果放在标签的前面,那么当DOM树构建完成了,渲染树才构建,那么当渲染树构建完成,浏览器不得不再重新渲染整个页面,这样造成了资源的浪费。效率也不高。如果放在之间,浏览器边构建边渲染,效率要高的多。

谈谈你对前后端分离的理解?

https://segmentfault.com/a/1190000009329474?_ea=2038402

中级(初级编程题及深入基础知识)

编程

遍历对象属性的方法有哪些?

(1)for…in

for…in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。

(2)Object.keys(obj)

Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。

(3)Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。

(4)Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。

(5)Reflect.ownKeys(obj)

Reflect.ownKeys返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。

以上的 5 种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。

首先遍历所有数值键,按照数值升序排列。
其次遍历所有字符串键,按照加入时间升序排列。
最后遍历所有 Symbol 键,按照加入时间升序排列。

数组的遍历方法有哪些?

遍历数组 arr

第一种: 普通 for 循环

1
2
3
for(j = 0; j < arr.length; j++) {
// do something
}

第二种: 优化版 for 循环
当数组的长度非常大时,缓存长度会带来略微的性能提升
1
2
3
4
let len = arr.length;
for(j = 0; j < len; j++) {
// do something
}

第三种: 利用数组的 forEach 方法
forEach() 方法对数组的每个元素执行一次提供的函数。
1
2
3
arr.forEach((item, index, array) => {
// do something
});

第四种: 利用 for in 来遍历数组的可枚举属性
1
2
3
for(index in arr) {
// do something
}

第五种: 利用数组的 map 方法遍历
map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
1
2
3
arr.map((item, index, array) {
// do something
});

第六种: 利用 for of 遍历数组值
for…of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。
1
2
3
for(item of arr) {
// do something
});

第七种: 利用数组的 every 方法遍历
every() 方法测试数组的所有元素是否都通过了指定函数的测试。
当返回false时会由于没有通过测试而中止,所以为了遍历,必须返回true。
1
2
3
4
arr.every(item => {
// do something
return true;
});

第八种: 利用数组的 filter 方法遍历
filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
1
2
3
arr.filter((item, index, array) => {
// do something
});

第九种: 利用数组的 some 方法遍历
some() 方法测试是否至少有一个元素通过由提供的函数实现的测试。
当返回true时会由于已经找到一个元素通过测试而中止,所以为了遍历,必须返回false。
1
2
3
4
arr.some((item, index, array) => {
// do something
return false;
});

第十种: 利用数组的 reduce 方法遍历
reduce() 方法对数组中的每个元素执行一个reducer函数(升序执行),将其结果汇总为单个返回值。
如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始。
1
2
3
arr.reduce((result, item, index, array) => {
// do something
}, initialValue);

书写sass/less mixin的例子

Saas

1
2
3
4
5
6
7
8
9
10
11
12
13
$link-color: blue;
$hover-color: red;
$visited-color: green;

@mixin link-colors($normal, $hover, $visited) {
color: $normal;
&:hover { color: $hover; }
&:visited { color: $visited; }
}

a {
@include link-colors($link-color, $hover-color, $visited-color);
}

Less

1
2
3
4
5
6
7
8
9
10
11
12
13
@link-color: blue;
@hover-color: red;
@visited-color: green;

.link-colors (@normal:@link-color; @hover:@hover-color; @visited:@visited-color) {
color: @normal;
&:hover { color: @hover; }
&:visited { color: @visited; }
}

a {
.link-colors;
}

编译后

1
2
3
a { color: blue; }
a:hover { color: red; }
a:visited { color: green; }

解释Event对象 target currentTarget preventDefault() stopPropagation()含义

target: 触发事件的对象元素的引用
currentTarget: 事件属性返回其监听器绑定事件的节点,即当前处理该事件的元素、文档或窗口。
preventDefault: 该方法将通知 Web 浏览器不要执行与事件关联的默认动作(如果存在这样的动作)。
stopPropagation: 终止事件在传播过程的捕获、目标处理或起泡阶段进一步传播。调用该方法后,该节点上处理该事件的处理程序将被调用,事件不再被分派到其他节点。

实现获取指定范围的随机整数

Math.random(): 获取[0-1)范围的小数
Math.floor(num): num取整数
取[1-100]范围内一个随机整数值: Math.floor(Math.random() * 100 + 1)

解释你熟悉的js常见类库 jquery.js/zepto.js (prototype.js) backbone.js underscore.js axios.js 的作用

jQuery:
jQuery 是一个高效、精简并且功能丰富的 JavaScript 工具库。它提供的 API 易于使用且兼容众多浏览器,这让诸如 HTML 文档遍历和操作、事件处理、动画和 Ajax 操作更加简单。
Zepto:
Zepto是一个轻量级的针对现代高级浏览器的JavaScript库, 它与jquery有着类似的api。
Underscore:
一个JavaScript实用库,提供了一整套函数式编程的实用功能,但是没有扩展任何JavaScript内置对象。它弥补了部分jQuery没有实现的功能,同时又是Backbone.js必不可少的部分。
Backbone:
Backbone.js为复杂WEB应用程序提供模型(models)、集合(collections)、视图(views)的结构。
Axios:
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。

实现行内元素与块状元素的相互转换

1
2
3
{
display: inline/block;
}

解释CSS3动画/过渡效果 transition:transform animation:@keyframe

transform: 定义转换
常用值:
translateX(xdeg) 沿X轴旋转x度
translateY(ydeg) 沿Y轴旋转y度
scale(x, y) 缩放 X轴放大到x倍数,Y轴放大到y倍
rotate(ndeg) 旋转 顺时针倾斜n角度
skew(x, y) 倾斜 沿X轴旋转x度,沿Y轴旋转y度

解释模块化编程 及commonJS AMD CMD ES6import

模块化是指把一个复杂的系统分解到多个模块以方便编码。
CommonJS 是一种使用广泛的 JavaScript 模块化规范,核心思想是通过 require 方法来同步地加载依赖的其他模块,通过 module.exports 导出需要暴露的接口。 CommonJS 规范的流行得益于 Node.js 采用了这种方式,后来这种方式被引入到了网页开发中。
采用 CommonJS 导入及导出时的代码如下:

1
2
3
4
5
// 导入
const moduleA = require('./moduleA');

// 导出
module.exports = moduleA.someFunc;

CommonJS 的优点在于:

  • 代码可复用于 Node.js 环境下并运行,例如做同构应用;
  • 通过 NPM 发布的很多第三方模块都采用了 CommonJS 规范。

CommonJS 的缺点在于:

  • 这样的代码无法直接运行在浏览器环境下,必须通过工具转换成标准的 ES5。

AMD 也是一种 JavaScript 模块化规范,与 CommonJS 最大的不同在于它采用异步的方式去加载依赖的模块。 AMD 规范主要是为了解决针对浏览器环境的模块化问题,最具代表性的实现是 requirejs。
采用 AMD 导入及导出时的代码如下:

1
2
3
4
5
6
7
8
    // 定义一个模块
define('module', ['dep'], function(dep) {
return exports;
});

// 导入和使用
require(['module'], function(module) {
});

AMD 的优点在于:

  • 可在不转换代码的情况下直接在浏览器中运行;
  • 可异步加载依赖;
  • 可并行加载多个依赖;
  • 代码可运行在浏览器环境和 Node.js 环境下。

AMD 的缺点在于:

  • JavaScript 运行环境没有原生支持 AMD,需要先导入实现了 AMD 的库后才能正常使用。

ES6 模块化是欧洲计算机制造联合会 ECMA 提出的 JavaScript 模块化规范,它在语言的层面上实现了模块化。

采用 ES6 模块化导入及导出时的代码如下:

1
2
3
4
5
6
7
8
// 导入
import { readFile } from 'fs';
import React from 'react';
// 导出
export function hello() {};
export default {
// ...
};

ES6模块虽然是终极模块化方案,但它的缺点在于目前无法直接运行在大部分 JavaScript 运行环境下,必须通过工具转换成标准的 ES5 后才能正常运行。

网络

试解释强缓存和协商缓存的区别

https://www.jianshu.com/p/9c95db596df5

数据结构和算法

高级(框架,底层,扩展)

Basic

实现 define

实现 Promise 异步编程

Promise的解释:
http://bruce-xu.github.io/blogs/js/promise
两个优秀的Promise的实现:
https://github.com/bruce-xu/Promise/blob/master/Promise.js
https://blog.csdn.net/yibingxiong1/article/details/68075416

从输入url到window.onload()结束,这中间发生了什么?(计算机网络)

  1. 预处理
  2. 读取cache(强缓存)
  3. 查询DNS
  4. 建立连接
  5. 发送请求
  6. 等待响应
  7. 接收数据
  8. 读取cache(协商缓存)
  9. 处理渲染

为什么通常在发送数据埋点请求的时候使用的是 1x1 像素的透明 gif 图片?

  1. 能够完成整个 HTTP 请求+响应(不需要响应内容)
  2. 触发 GET 请求之后不需要获取和处理数据、服务器也不需要发送数据
  3. 跨域友好
  4. 执行过程无阻塞
  5. 相比 XMLHttpRequest 对象发送 GET 请求,性能上更好
  6. GIF的最低合法体积最小(最小的BMP文件需要74个字节,PNG需要67个字节,而合法的GIF,只需要43个字节)

前端性能优化从哪几方面入手?有哪些方法?

  1. 性能优化=》减少从用户输入网址到用户看到期望内容的时间
  2. 从用户输入url到 window.onload 发生了什么
    • 预处理 本地
    • 读取cache 本地
    • 查询DNS 网络
    • 建立连接 网络
    • 发送请求 网络
    • 等待响应 网络
    • 接收数据 本地
    • 处理渲染 本地
  3. 本地 =》 缩短渲染时间
    网络 =》 减少请求次数 减小请求体积 加快请求速度
  4. 基本原则
    • 建立监控机制 (监控)
    • 日志分析 (分析)
    • 针对监控暴露的弱点优化 (优化)
  5. 监控节点 window.performance.timing
    • 白屏时间 </head> 结束 domLoading - fetchStart
    • 首屏时间 首屏加载结束
    • 用户可操作时间 DOMReady&核心js加载完毕 domContentLoadedEventEnd - fetchStart
    • 总下载时间 onload/异步渲染完毕 loadEventEnd - fetchStart
  6. 常用的性能优化方法
  • 减少http请求内容
  • 使用cdn
  • 添加 expires/ cache-control header
  • 使用gzip
  • 将css放到html顶部加载
  • 将js放到底部加载
  • 外部引用js & css
  • uglify js & css
  • 避免重定向
  • 移除重复js
  • 配置Etags
  • 缓存ajax数据
  • 预先加载组件
  • 减少dom元素个数
  • 减少iframes
  • 避免404错误
  • 减少cookie大小
  • 对于不需要cookie的组件/请求,使用单独的域名
  • 减少dom操作
  • 采用事件委托
  • 减少 @import 动态导入
  • 优化图像大小
  • 使用css图像拼合
  • 不要缩放图像
  • favicon.icon 尽量小&缓存
  • 组件小于25k
  • 避免空image src
  • 采用 渐进式jpeg

解释 SSR、SEO、SPA

SSR: Server side render 服务器渲染
SEO: Search engine optimize 搜索引擎优化
SPA: Single page application 单页面应用

vue相关

react相关

Webpack相关

node相关

框架:express koa

具体请查阅 node-interview

shell相关

具体请查阅 命令行的艺术

git相关

具体请查阅Pro Git book

其他

什么是material design?

具体请查阅material-design
中文版:material-design

Database(数据库)

解释数据库事务的4个基本特征ACID

Atomic 原子性
Consistency 一致性
Isolation 隔离性
Duration 持久性

解释数据库隔离级别

Read uncommitted 未提交读 => 脏读,不可重复读,幻读
Read commited 读写提交 => 不可重复读,幻读
Repeatable read 可重复读 => 幻读
Serializable 串行化