一、HTML
1.0 如何理解 HTML 语义化?
第一段代码:
<div>标题</div>
<div>
<div>一段文字</div>
<div>
<div>列表1</div>
<div>列表2</div>
</div>
</div>
第二段代码:
<h1>标题</h1>
<div>
<p>一段文字</p>
<ul>
<li>列表1</li>
<li>列表2</li>
</ul>
</div>
以上两段代码,第一段代码给人的感觉就全是div,不方便阅读。第二段代码相比第一段代码来说,能让人知道写的具体是什么内容,让人更加容易读懂,增加了代码的可阅读性。相对于机器来说,识别第一段代码机器只能识别到div,而对于第二段代码,机器就能清楚的识别出h1表示的是标题,重点突出,p表示文字,ul li 表示一个列表。这样可以使得搜索引擎更容易读懂,更加利于SEO的优化。
1.1 默认情况下,哪些标签是块级元素、哪些是内联元素、哪些是空标签?
块级元素独占一行,
display:block/table的都是块级元素,有div、h1~h6、table、ul、ol、li、p、section等元素。
内联元素不会独占一行,display:inline/inline-block的都是内联元素,有a、img、span、b、strong、input、select、button等元素。
空标签的元素有br、hr、img、input、meta、link
二、CSS - 布局
1.0 盒子模型的宽度如何计算?
如下代码,请问 box 的 offsetWidth 是多大?
<style>
#box {
width: 100px;
border: 1px solid red;
margin: 10px;
padding: 10px;
}
</style>
<div id="box">
这是一个盒子
</div>
offsetWidth 的宽度 = 内容宽度 + 内边距 + 边框,没有外边距,因此这个 box 的 offsetWidth 宽度为 122
补充:如果让 offsetWidth 的宽度等于 100px,该如何做?
添加 box-sizing:border-box
1.1 margin 纵向重叠的问题(所谓纵向重叠就是很多个元素纵向排列,它们如果有 margin ,这个 margin 就会有重叠的问题)
如下代码,请问 AAA 和 BBB 之间的距离是多少?
<style>
p {
font-size: 16px;
line-height: 1;
margin-top: 10px;
margin-bottom: 15px;
}
</style>
<p>AAA</p>
<p></p>
<p></p>
<p></p>
<p>BBB</p>
相邻元素的 margin-top 和 margin-bottom 会发生重叠
空白的 <p></p> 也会重叠
答案为15px
1.2 margin 负值的问题
margin-left 与 margin-top 负值,元素会向上、向左移动。
margin-right 负值,右侧元素左移,自身不受影响。 (例如:A元素B元素在一行,A元素设置 margin-right:-10px,此时B元素就会向左边移动10px,而A元素不会发生变化。)
margin-bottom 负值,下方元素上移,自身不受影响。(例如:A元素在上面,B元素在下面,A元素设置margin-bottom:-10px。此时B元素就会向上边移动10px,而A元素不会发生变化。)
1.3 BFC理解和应用,什么是 BFC?如何应用?
创建 BFC
- float 不是 none
- position 是 absolute 或 fixed
- overflow 不是 visible
- display 是 flex、inline-block等。
BFC(Block formatting context) 是块级格式化上下文就是页面上一个独立的容器,在它内部的子元素不会影响到容器外的元素。
内部的元素会在垂直方向,一个接一个的放置。
BFC 清除浮动应用
<style>
.par {
width: 300px;
border: 5px solid red;
overflow: hidden;
}
.par .child {
float: left;
border: 5px solid green;
width: 100px;
height: 100px;
}
</style>
<div class="par">
<div class="child"></div>
<div class="child"></div>
</div>
添加overflow: hidden前

添加overflow: hidden后

1.4 float 布局的问题,以及claerfix
如何实现圣杯布局和双飞翼布局?
圣杯布局和双飞翼布局的技术总结:
- 使用 float 布局。
- 两边使用 margin 负值,以便和中间内容横向重叠。
- 防止中间内容被两侧覆盖,一个用 padding 一个用 margin。
圣杯布局:使用 padding-left 和 padding-right 为左右两边留出位置。
<style>
* {
margin: 0;
padding: 0;
}
header,
footer {
width: 100%;
background-color: red;
}
.container {
padding-left: 200px;
padding-right: 150px;
}
.container .left {
position: relative;
width: 200px;
background-color: green;
margin-left: -100%;
right: 200px;
}
.container .right {
width: 150px;
background-color: pink;
margin-right: -150px;
}
.container .center {
width: 100%;
background-color: blue;
}
.container .column {
float: left;
}
footer {
clear: both;
}
</style>
</head>
<body>
<header>头部</header>
<div class="container">
<div class="center column">中间</div>
<div class="left column">左边</div>
<div class="right column">右边</div>
</div>
<footer>尾部</footer>
</body>

优点:不需要添加多余的 DOM 节点
缺点:浏览器缩小至左边或右边的固定宽度后,圣杯布局会发生错乱。
双飞翼布局:使用 margin-left 和 margin-right 为左右两边留出位置。
<style>
* {
margin: 0;
padding: 0;
}
.box {
background-color: red;
width: 100%;
height: 200px;
}
.box .main {
margin-left: 200px;
margin-right: 100px;
}
.left {
width: 200px;
height: 200px;
background-color: green;
margin-left: -100%;
}
.right {
width: 100px;
height: 200px;
background-color: pink;
margin-left: -100px;
}
.column {
float: left;
}
</style>
<body>
<div class="box column">
<div class="main">
中间
</div>
</div>
<div class="left column">左边</div>
<div class="right column">右边</div>
</body>

优点:不会像圣杯布局那样发生布局错乱
缺点:多添加了一个 DOM 节点
手写 claerfix 清除浮动
<style>
.clearfix::after{
content:'';
display:table;
clear:both;
}
</style>
1.5 使用 flex 布局画一个骰子

代码如下:
<style>
.box {
width: 150px;
height: 150px;
border: 2px solid #ccc;
border-radius: 10px;
padding: 10px;
display: flex;
justify-content: space-between;
}
.box .circle {
display: block;
width: 50px;
height: 50px;
border-radius: 50%;
background-color: red;
}
.box .circle:nth-child(2) {
align-self: center;
}
.box .circle:nth-child(3) {
align-self: flex-end;
}
</style>
<div class="box">
<span class="circle"></span>
<span class="circle"></span>
<span class="circle"></span>
</div>
1.6 relative 和 absolute 分别依据什么定位?
relative 相对定位,是根据自身定位
absolute 绝对定位,是根据最近一层的元素定位
1.7 居中对齐有哪些实现方式?
水平居中:
- inline 元素:text-align:center
- block 元素:margin:auto
- absolute 元素:left:50% + margin-left:宽度一半的负值
垂直居中:
- inline 元素:line-height 行高的值等于 height 高度的值
- absolute 元素:top:50% + margin-top:高度一半的负值
- absolute 元素:transform:translate(-50%,-50%)
- absolute 元素:top,right,bottom,left都等于 0 ,然后设置 margin:auto
1.8 line-height 的继承问题
如下代码,p 标签的行高会是多少?
<style>
body {
font-size: 20px;
line-height: 200%;
}
p {
font-size: 16px;
background-color: red;
}
</style>
<body>
<p>AAA</p>
</body>
当 line-height 的值是百分比的时候,就会由当前百分比 * font-size 得到后的值继承给 p 标签,所以此时的 p 标签的行高为
40。
当 line-height 的值为 1、2 或者 20px、30px,p 标签会直接继承下来,1、2 的值为继承下来 * 当前标签的 font-size。例如:body 现在的行高为 line-height:2,p 标签此时的行高则为 font-size 的 16 * 2 得到行高32。
1.9 rem 是什么?
rem 是一个长度单位,相对于 html 根元素,常用于响应式布局,html 根元素的 font-size 为 100px ,则 1rem 等于 100px。
2.0 如何实现响应式?
如下代码,实现三种尺寸的响应式大小
<style>
@media screen and (max-width:374px) {
html {
font-size: 86px;
}
}
@media screen and (min-width:375px) and (max-width:413px) {
html {
font-size: 100px;
}
}
@media screen and (min-width:414px) {
html {
font-size: 110px;
}
}
.box {
font-size: 0.16rem;
width: 1rem;
background-color: #ccc;
}
</style>
<body>
<div class="box">
这是一个盒子
</div>
</body>
当屏幕宽度小于
374px时,盒子宽度为86px,字体为13.76px。
当屏幕宽度大于375px并且小于413px时,盒子宽度为100px,字体为16px。
当屏幕宽度大于414px时,盒子宽度为110px,字体为17.6px。
三、JavaScript
1.0 typeof 可以判断哪些类型?
可以识别所有值类型,例如:字符串(string),布尔值(boolean),undefined,数字(number),symbol
可以识别函数类型:函数(function)。
可以识别引用类型(不可细分),例如:null、对象、数组使用 typeof 判断出来的类型都是 Object。
1.1 何时使用 === 何时使用 == ?
当判断一个值是否等于 null 时,使用 == 进行判断:
const obj = { x: 100};
if(obj.x == null){}
相当于:
if (obj.x === null || obj.x === undefined) { }
除了判断 null 时,建议判断其他类型时一律使用 === 。
1.2 值类型和引用类型的区别是什么?
-
值类型:字符串
string,数值number,布尔boolean,null,undefined。- 占用空间固定,保存在
栈中。 - 保存与复制是值的本身。
- 使用
typeof检测数据类型。 - 基本类型数据是值类型。
- 占用空间固定,保存在
-
引用类型:数组
Array,对象Object,函数Function。- 占用空间不固定,保存在
堆中。 - 保存与复制是指向对象的一个指针。
- 使用
instanceof检测数据类型。 - 使用
new方法构造出来的对象是引用类型。
- 占用空间不固定,保存在
1.3 手写深拷贝
<script>
const obj = {
name: '张三',
age: 18,
sex: '男',
address: {
city: '北京'
}
}
const newObj = deepClone(obj)
newObj.address.city = '成都'
console.log(obj.address.city); // 北京
console.log(newObj.address.city); // 成都
function deepClone(obj = {}) {
// 判断 obj 不是数组和对象 或者 obj 是 null
if (typeof obj !== 'object' || obj == null) {
return obj
}
let result;
// 判断 obj 是不是数组
if (Array.isArray(obj)) {
result = []
} else {
result = {}
}
// 循环
for (let key in obj) {
// 保证 key 不是原型的属性
if (obj.hasOwnProperty(key)) {
// 递归调用
result[key] = deepClone(obj[key])
}
}
return result
}
</script>
1.4 使用 class 实现继承
代码如下:
<script>
// 父类 人
class Person {
constructor(name) {
this.name = name;
}
eat() {
console.log(`${this.name}正在吃饭!`);
}
}
// 子类 学生 继承 父类 人
class Student extends Person {
constructor(name, num) {
super(name)
// 学号
this.num = num;
}
sayHi() {
console.log(`${this.name}向你打了个招呼`)
}
}
// 子类 老师 继承 父类 人
class Teacher extends Person {
constructor(name, major) {
super(name);
// 专业
this.major = major;
}
teach() {
console.log(`${this.name}正在教学生们${this.major}`);
}
}
const student = new Student('张三', '000001');
const teacher = new Teacher('王老师', '语文课');
console.log(student.name);
console.log(student.num);
student.sayHi();
student.eat()
console.log(teacher.name);
console.log(teacher.major);
teacher.teach()
teacher.eat()
console.log(student.__proto__ === Student.prototype); // true
console.log(Person.prototype === Student.prototype.__proto__); // true
/*
张三
000001
张三向你打了个招呼
张三正在吃饭!
王老师
语文课
王老师正在教学生们语文课
王老师正在吃饭!
*/
</script>
1.5 如何理解 JS 原型
显示原型:prototype,隐式原型:__proto__ 。
原型关系:
- 每个
class都有显示原型prototype。 - 每个实例都有隐式原型
__proto__。 - 实例的
__proto__指向对应class的prototype。
基于原型的执行规则:
- 获取属性或者执行方法时,先在自身属性和方法中寻找,如果找不到则自动去
__proto__隐式原型中查找。
1.6 如何准确判断一个变量是不是数组?
-
a instanceof Array- 缺点:只能判断对象是否存在于目标对象的原型链上。
- 通过 E6 数组方法
Array.isArray(值)判断一个变量值是否是数组,成功返回true否则返回false。 - 通过原型链
Object.prototype.toString.call(值),输出"[object Array]"代表是数组。
1.7 闭包是什么?
// 函数作为返回值
function fnc() {
var a = 100;
return function () {
console.log(a);
}
}
let fn = fnc();
var a = 200;
fn() // 100
// 函数作为参数
function print(fn) {
let a = 200;
fn()
}
let a = 100;
function fnc() {
console.log(a);
}
print(fnc) // 100
闭包就是获取其他函数内部变量的函数。
闭包就是定义一个在函数内部的函数。
闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包:自由变量的查找,是从定义函数的地方,向上级作用域查找。 不是在执行函数的地方查找!!!
1.8 手写 bind 函数
<script>
Function.prototype.bindTest = function () {
// 将类数组转换成数组
const args = [...arguments];
// 获取 this 数组的第一项
const t = args.shift();
// 当前 this
const self = this;
// 返回一个函数
return function () {
return self.apply(t, args);
}
}
function fn1(a, b, c) {
console.log('this', this);
console.log(a, b, c);
return 'this is fn1'
}
const fn2 = fn1.bindTest({ x: 100 }, 100, 200, 300);
const res = fn2()
console.log(res);
/*
this { x: 100 }
100 200 300
this is fn1
*/
</script>
1.9 异步和同步的区别
- 基于 JS 是单线程语言
- 异步不会阻塞代码执行
- 同步会阻塞代码执行
前端异步的应用场景:网络请求&定时任务。
Promise 解决 callback 嵌套。
2.0 编写一个通用的事件监听函数
<script>
// 通用事件绑定
function bindEvent(el, type, selector, fn) {
// 判断 三个参数 还是四个参数
if (fn == null) { // 三个参数
fn = selector;
selector = null
}
el.addEventListener(type, e => {
const target = e.target;
// 存在说明是代理绑定
if (selector) {
// 使用 matches 来判断是否触发元素
if (target.matches(selector)) {
fn.call(target, e);
}
} else {
// 普通绑定
fn.call(target, e)
}
})
}
const ul = document.querySelector('ul');
const li = document.querySelectorAll('li');
bindEvent(li[0], 'click', function (e) {
console.log(this.innerHTML);
})
bindEvent(ul, 'click', 'li', function (e) {
console.log(this.innerHTML);
})
</script>
2.1 手写防抖
防抖是什么?防抖就是用户在触发事件后,在指定时间内只会执行一次,如果触发事件后,在指定时间内再次触发,则会重新计算函数延迟执行时间。
防抖代码如下:
<body>
<input type="text" id="input">
</body>
<script>
const input = document.getElementById('input');
// 监听事件
input.addEventListener('keyup', debounce(function () {
console.log(this.value);
}, 1000))
// 防抖
function debounce(fn, delay = 500) {
let timer;
return function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null
}, delay)
}
}
</script>
2.2 手写节流
节流是什么?节流就是用户触发事件后,在指定时间内只执行一次,如果触发事件后,在指定时间内再次触发,则不会影响上次触发,直到触发事件执行结束。多次点击在指定时间内只会执行一次。
节流代码如下:
<body>
<button id="btn">按钮</button>
</body>
<script>
const btn = document.getElementById('btn');
// 监听点击事件
btn.addEventListener('click', throttle(function () {
console.log(666);
}, 1000))
// 节流定时器
function throttle(fn, delay = 500) {
let timer;
return function () {
if (timer) return false;
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, delay)
}
}
// 节流时间戳
function throttle(fn, delay = 500) {
// 触发时间
let pre = Date.now();
return function () {
// 当前时间
let now = Date.now();
if (now - pre >= delay) {
fn.apply(this, arguments)
pre = Date.now();
}
}
}
</script>
2.3 DomContentLoaded 和 load 的区别
- load
load 用于检测一个完全加载的页面,页面的html、css、js等资源全部加载完毕后才会触发load事件。 - DomContentLoaded
当初始的html加载和解析完毕后,就会触发DomContentLoaded事件,无需等待其他资源完成加载。
四、Vue.js
1.0 v-show 和 v-if 有什么区别?
v-show会在 DOM 里生成全部元素节点,使用display:block/none控制显示隐藏,用于频繁的加载。
v-if用于不频繁加载的时候,因为它每次都是在 DOM 中创建一个新的元素出来。
1.1 v-for 中为什么要使用 key ?
因为
v-for正在更新已染的列表时,它默认‘就地复用’策略,如果列表中的顺序发生改变或者被删除某项时,vue 不是移动 DOM元素来匹配数据项的改变,而是会简单的复用现有的元素,并且确定它在特定的索引下显示已经被渲染过的每个元素。为了给 vue 一个提示,便于让它跟踪每个元素,从而更好的重排现有元素,就需要为每一项添加一个 key 作为唯一值。key 的作用主要是为了高效的更新虚拟DOM。
1.2 vue 组件的生命周期(父子组件)
1.3 vue 组件如何进行通讯?
1.4 computed 和 watch
-
computed有缓存,data不变则不会重新计算 -
watch如何深度监听? -
watch监听引用类型,拿不到oldVal