# 前端面经

题外话

每日解决一题,进步一点点,希望多一点

# HTML相关

# HTML介绍

HTML 是用来描述网页的一种语言。

  • HTML 指的是超文本标记语言 (Hyper Text Markup Language)
  • HTML 不是一种编程语言,而是一种标记语言 (markup language)
  • 标记语言是一套标记标签 (markup tag)

# 常见的块级元素和行级元素和行级块元素

块级元素:divpulliolformaddress(特点:独占一行,可以通过css改变高度)

行级元素:spanstrongemadel(特点:内容决定元素所在位置,不可以通过css改变宽高)

行级块元素:img(特点:内容决定大小,,可以通过css改变宽高)

HTML知识点相信很多朋友都不陌生,在这一章节的知识点也不是很多,考的也不是很多,陆续增加...

# CSS相关

# 浏览器内核冷知识

chrome	=>	webkit / blink
IE	=>	trident
Safari	=>	webkit
firefox	=>	Gecko
Opera	=>	presto
1
2
3
4
5

# 什么是BFC

总结

BFC(Border formatting context): 块级格式上下文,块级元素内部有一套自己的规则来影响子元素的布局,不与外界相互干预

# I、如何触发BFC盒子的BFC?

  1. position:absolute或者fixed
  2. display:inline-block、table-cell、table-caption、flex、inline-flex
  3. float:left / right
  4. overflow:hidden

更多详细的BFC知识点请看我的这一篇文章,在这里就不详细说了

# margin塌陷问题

特点

父级和子级相对于顶部的margin,取父级和子级最大值

弥补方法:(只能弥补不能解决)

  • 给父级加上border-top (不专业)
  • BFC(block format context)

# margin合并问题

特点

两个兄弟元素垂直方向的margin会合并

弥补方法:

  • BFC
  • 数学方法

# CSS权重

扩展

每个级别之间的进制是256进制,由一个优秀的工程师测出的

选择器 权重
!important Infinity
行间样式 1000
id 100
class、属性、伪类 10
标签、伪元素 1
通配符 0

# 盒模型

组成部分:盒子壁、内边距、盒子内容、外边距(这方面的扩展就不多说了)

# 浮动(float)

特点

浮动元素产生了浮动流,所有产生浮动流的元素,块级元素看不到他们,产生了BFC的元素和文本属性元素(inline)以及文本都能看到浮动元素

# I、如何变成浮动元素?

float: left / right;
1

# II、清除浮动的方法?

  • 利用clear样式
  • 父元素结束标签之前插入清除浮动的块级元素
  • 利用伪元素(clearfix)
  • 利用overflow清除浮动

# III、如何让父级元素包裹住浮动元素?

可以让父级元素设置为BFC,或者让子级最后一个元素清除浮动

<div class="plat">
    <div class="box">1</div>
    <div class="box">2</div>
    <div class="box box3">3</div>
    <p></p>
</div>

// style
*{
	margin: 0;
	padding: 0;
	text-decoration: none;
}
.plat {
	border: 5px solid green;
	display: inline-block;
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	user-select: none;
}

.box {
	width: 100px;
	height: 100px;
	background-color: black;
	color: red;
	float: left;
	line-height: 100px;
	text-align: center;
	cursor: pointer;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 如何居中一个浮动元素?

<div>
  <div class="left">浮动元素</div>
</div>

.left{
  position: absolute;
  left: 50%;
  right: 50%;
  transform: translate(-50%, -50%);
}
1
2
3
4
5
6
7
8
9
10

# 更多CSS面试题

# JavaScript相关

# 原始类型

涉及到的面试题:原始类型有哪几种?null 是对象嘛?

  • boolean
  • null
  • undefined
  • number
  • string
  • symbol 首先原始类型存储的都是值,是没有函数可以调用的

另外对于 null 来说,很多人会认为他是个对象类型,其实这是错误的。虽然 typeof null 会输出 object,但是这只是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object 。虽然现在的内部类型判断代码已经改变了,但是对于这个 Bug 却是一直流传下来

# 对象类型

涉及面试题:对象类型和原始类型的不同之处?函数参数是对象会发生什么问题?

在 JS 中,除了原始类型那么其他的都是对象类型了。对象类型和原始类型不同的是,原始类型存储的是值,对象类型存储的是地址(指针)。当你创建了一个对象类型的时候,计算机会在内存中帮我们开辟一个空间来存放值,但是我们需要找到这个空间,这个空间会拥有一个地址(指针)。

function test(person) {
  person.age = 26
  person = {
    name: 'yyy',
    age: 30
  }

  return person
}
const p1 = {
  name: 'yck',
  age: 25
}
const p2 = test(p1)
console.log(p1) // -> ?
console.log(p2) // -> ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

对于以上代码,你是否能正确的写出结果呢?接下来让我为你解析一番:

  • 首先,函数传参是传递对象指针的副本
  • 到函数内部修改参数的属性这步,我相信大家都知道,当前 p1 的值也被修改了
  • 但是当我们重新为 person 分配了一个对象时就出现了分歧,所以最后 person 拥有了一个新的地址(指针),也就和 p1 没有任何关系了,导致了最终两个变量的值是不相同的。

# 类型转换

涉及面试题:该知识点常在笔试题中见到,熟悉了转换规则就不惧怕此类题目了 首先我们要知道,在 JS 中类型转换只有三种情况,分别是:

  • 转换为布尔值
  • 转换为数字
  • 转换为字符串

我们先来看一个类型转换表格,然后再进入正题

原始值 转换目标 结果
number 布尔值 除了0、-0、NaN都为true
string 布尔值 除了空串都为true
undefined、null 布尔值 False
引用类型 布尔值 True
number 字符串 5 => '5'
Boolean、函数、Symbol 字符串 'true'
数组 字符串 [1,2] => '1,2'
对象 字符串 '[object Object]'
string 数字 '1' => 1,'a' => NaN
数组 数字 空数组为0,存在一个元素且为数字转数字,其他情况NaN
null 数字 0
除了数组的引用类型 数字 NaN
Symbol 数字 抛错

# I、转Boolean

在条件判断时,除了 undefinednullfalseNaN''0-0,其他所有值都转为 true,包括所有对象。

# II、对象转原始类型

对象在转换类型的时候,会调用内置的 [[ToPrimitive]] 函数,对于该函数来说,算法逻辑一般来说如下:

  • 如果已经是原始类型了,那就不需要转换了
  • 调用 x.valueOf(),如果转换为基础类型,就返回转换的值
  • 调用 x.toString(),如果转换为基础类型,就返回转换的值
  • 如果都没有返回原始类型,就会报错

当然你也可以重写 Symbol.toPrimitive ,该方法在转原始类型时调用优先级最高。

let a = {
  valueOf() {
    return 0
  },
  toString() {
    return '1'
  },
  [Symbol.toPrimitive]() {
    return 2
  }
}
1 + a // => 3
1
2
3
4
5
6
7
8
9
10
11
12

# III、四则运算符

加法运算符不同于其他几个运算符,它有以下几个特点:

  • 运算中其中一方为字符串,那么就会把另一方也转换为字符串
  • 如果一方不是字符串或者数字,那么会将它转换为数字或者字符串
1 + '1' // '11'
true + true // 2
4 + [1,2,3] // "41,2,3"
1
2
3

如果你对于答案有疑问的话,请看解析:

  • 对于第一行代码来说,触发特点一,所以将数字 1 转换为字符串,得到结果 '11'
  • 对于第二行代码来说,触发特点二,所以将 true 转为数字 1
  • 对于第三行代码来说,触发特点二,所以将数组通过 toString 转为字符串 1,2,3,得到结果 41,2,3

另外对于加法还需要注意这个表达式 'a' + + 'b'

'a' + + 'b' // -> "aNaN"
1

因为 + 'b' 等于 NaN,所以结果为 "aNaN",你可能也会在一些代码中看到过 + '1' 的形式来快速获取 number 类型。

那么对于除了加法的运算符来说,只要其中一方是数字,那么另一方就会被转为数字

4 * '3' // 12
4 * [] // 0
4 * [1, 2] // NaN
1
2
3

# IV、比较运算符

  1. 如果是对象,就通过 toPrimitive 转换对象
  2. 如果是字符串,就通过 unicode 字符索引来比较
let a = {
  valueOf() {
    return 0
  },
  toString() {
    return '1'
  }
}
a > -1 // true
1
2
3
4
5
6
7
8
9

# this

涉及面试题:如何正确判断 this?箭头函数的 this 是什么?

this 是很多人会混淆的概念,但是其实它一点都不难,只是网上很多文章把简单的东西说复杂了。在这一小节中,你一定会彻底明白 this 这个概念的。

我们先来看几个函数调用的场景

function foo() {
  console.log(this.a)
}
var a = 1
foo()

const obj = {
  a: 2,
  foo: foo
}
obj.foo()

const c = new foo()
1
2
3
4
5
6
7
8
9
10
11
12
13

接下来我们一个个分析上面几个场景

  • 对于直接调用 foo 来说,不管 foo 函数被放在了什么地方,this 一定是 window
  • 对于 obj.foo() 来说,我们只需要记住,谁调用了函数,谁就是 this,所以在这个场景下 foo 函数中的 this 就是 obj 对象
  • 对于 new 的方式来说,this 被永远绑定在了 c 上面,不会被任何方式改变 this

说完了以上几种情况,其实很多代码中的 this 应该就没什么问题了,下面让我们看看箭头函数中的 this

function a() {
  return () => {
    return () => {
      console.log(this)
    }
  }
}
console.log(a()()())
1
2
3
4
5
6
7
8

首先箭头函数其实是没有 this 的,箭头函数中的 this 只取决包裹箭头函数的第一个普通函数的 this。在这个例子中,因为包裹箭头函数的第一个普通函数是 a,所以此时的 thiswindow。另外对箭头函数使用 bind 这类函数是无效的。

最后种情况也就是 bind 这些改变上下文的 API 了,对于这些函数来说,this 取决于第一个参数,如果第一个参数为空,那么就是 window

那么说到 bind,不知道大家是否考虑过,如果对一个函数进行多次 bind,那么上下文会是什么呢?

let a = {}
let fn = function () { console.log(this) }
fn.bind().bind(a)() // => ?
1
2
3

如果你认为输出结果是 a,那么你就错了,其实我们可以把上述代码转换成另一种形式

// fn.bind().bind(a) 等于
let fn2 = function fn1() {
  return function() {
    return fn.apply()
  }.apply(a)
}
fn2()
1
2
3
4
5
6
7

可以从上述代码中发现,不管我们给函数 bind 几次,fn 中的 this 永远由第一次 bind 决定,所以结果永远是 window

let a = { name: 'yck' }
function foo() {
  console.log(this.name)
}
foo.bind(a)() // => 'yck'
1
2
3
4
5

以上就是 this 的规则了,但是可能会发生多个规则同时出现的情况,这时候不同的规则之间会根据优先级最高的来决定 this 最终指向哪里。

首先,new 的方式优先级最高,接下来是 bind 这些函数,然后是 obj.foo() 这种调用方式,最后是 foo 这种调用方式,同时,箭头函数的 this 一旦被绑定,就不会再被任何方式所改变。

更多this方面的知识点解析,推荐这篇文章

# 作用域篇

# 闭包篇

  1. 下面输出x的值是什么
var x = 1;
if(function test(){}){
  x += typeof test
}
console.log(x)  // 1undefined
1
2
3
4
5

# 包装类篇

var str = 'abc';
str += 1
let rs = typeof str
if(rs.length == 6){
  rs.name = 'hello'
}
console.log(rs.name)  // undefined
1
2
3
4
5
6
7

# 实现call、apply、bind

请看博主此文

# 原型篇

# 继承篇

# 深浅拷贝篇

# 数组篇

# dom事件篇

# 正则篇

# 性能优化篇

# webpack篇

# Vue篇

# Node篇

# HTTP篇

# 浏览器篇

达摩师兄
春风化雨,行健不息
MIT Licensed | Copyright © 2019-present 达摩师兄
>