初级前端需要知道的基础知识

本文罗列十道基础题目,总结一下近期的知识。

1.第1题: 请写出一个符合 W3C 规范的 HTML 文件,要求

页面标题为「我的页面」
页面中引入了一个外部 CSS 文件,文件路径为 /style.css
页面中引入了另一个外部 CSS 文件,路径为 /print.css,该文件仅在打印时生效
页面中引入了另一个外部 CSS 文件,路径为 /mobile.css,该文件仅在设备宽度小于 500 像素时生效
页面中引入了一个外部 JS 文件,路径为 /main.js
页面中引入了一个外部 JS 文件,路径为 /gbk.js,文件编码为 GBK
页面中有一个 SVG 标签,SVG 里面有一个直径为 100 像素的圆圈,颜色随意
注意题目中的路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>我的页面</title>
<link rel="stylesheet" href="/style.css">
<link rel="stylesheet" href="/print.css" media="print">
<link rel="stylesheet" href="/mobile.css" media="(max-width: 500px)">
</head>

<body>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
<circle cx="100" cy="100" r="50" fill="red" />
</svg>
</body>
<script src="/main.js"></script>
<script src="/gbk.js" charset="GBK"></script>
</html>

第2题: 移动端是怎么做适配的?

回答要点:
1.meta viewport
2.媒体查询
3.动态 rem 方案
1.在html中的head加入下面这个代码

1
2
3
4
5
6
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0,user-scalable=no">
//width=device-width: 让当前viewport宽度等于设备宽度
//user-scalable=no: 禁止用户缩放
//initial-scale=1.0: 设置页面的初始缩放值为不缩放
//maximum-scale: 允许用户的最大缩放值为1.0
//minimun-scale: 允许用户的最小缩放值为1.0

2.不同的设备采取不同的样式,实现该方法应用媒体查询

使用 media 查询来响应不同分辨率
如在引入外部样式时,只有满足条件最大宽度为425px时,example.css才生效:

1
<link rel="stylesheet" media="(max-width: 425px)" href="example.css" />

在CSS内部,也可以用media来设置特定的情况下让某些样式生效,如下,即当最大宽度为425px时,该样式生效:

1
2
3
4
5
<style>
@media (max-width: 425px) {
……
}
</style>

  • 使用动态 REM 方案保证手机端的显示效果
  • rem指的是根元素的字体大小,根元素一般指的是html
    3.动态rem即在js中设置
    1
    2
    var pageWidth = window.innerWidth
    document.write('<style>html{font-size:'+pageWidth/10+'px;}</style>')

这样可以保证整体页面的比例,不会导致页面变形

(2) 使用scss直接将px转换成rem,减少自己计算时间

例子:

1
2
3
4
5
6
@function px2rem($px){
@return $px/$designWidth*5 + rem;
}

$designWidth: 750;
1rem = px2rem(150) =150px;

第3题: CSS3 实现圆角矩形和阴影怎么做?

CSS3 MDN文档
css3 中的新特性,比较常用的:
新增加的伪类如nth-child()等、flex布局、使用transform对元素进行变换、transition过渡,通过定义keyframes做一些动画效果、添加阴影box-shadow、移动端适配中会用到媒体查询等

  • 阴影包括box-shadow和text-shadow:
    1
    2
    box-shadow: inset 1px 2px 3px blue
    text-shadow: inset 1px 2px 3px blue

以上代码一个在边框内,inset(阴影向内) ,offset-x(x轴上的偏移量) 1px, offset-y(y轴上的偏移量) 2px, blur-radius(模糊半径大小) 3px的蓝色box阴影和text阴影
box-shadow 文档
box-shadow 可以使用一个或多个投影,如果使用多个投影时必须需要用逗号“,”分开。用法如下:

  • 对象选择器 {box-shadow:inset x-offset y-offset blur-radius spread-radius color}
  • 对象选择器 {box-shadow:投影方式 X轴偏移量 Y轴偏移量 阴影模糊半径 阴影扩展半径 阴影颜色}

用borde-radius实现圆形矩角

1
border-radius: 2px;

以上代码设置了一个四个圆角角度为2px的圆角矩形
border-radious 文档

  • 后面接一个值时:为元素的四个角都设置相同的圆角;
  • 后面接两个值:第一个值设置的是左上角和右下角的值,第二个值指的是右上角和左下角的值;
  • 后面接四个值时:四个值分别设置的圆角顺序为,从左上角开始顺时针的四个角,即分别为左上角,右上角,右下角,左下角。
  • 如果我们要做一个圆形,可以这么写:border-radious:50%;

使用圆角还可以做椭圆:border-radius还可以用斜杠设置第二组值。这时,第一组值表示水平半径,第二组值表示垂直半径。第二组值也可以同时设置1到4个值,应用规则与第一组值相同。如:border-radius: 1px 2px 3px 4px / 5px 4px 3px 3px;

第4题: 什么是闭包? 闭包的用途?

  • “函数”和”函数内部能访问到的变量(也叫环境)”的总和,就是一个闭包。
  • JavaScript有两种作用域,全局作用域和函数作用域。函数内部可以直接读取全局变量。但是,在函数外部无法读取函数内部声明的变量。换言之。如果一个函数,使用了它范围外的变量,那么”这个函数+这个变量”就叫做闭包。
  1. 闭包的用途:我们之所以使用闭包,是因为闭包可以用来「间接访问一个变量」。换句话说,「隐藏一个变量」。例如函数中的局部变量,在执行完毕之后就会死掉,在其他地方是访问不到的,而闭包就是用来暴露一个访问器(函数),用于间接访问到这个变量。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function f1(){
    var n = 1;
    function f2(){
    console.log(n)
    }
    return f2
    } //这段代码中函数 f2 和变量 n 的总和就叫闭包

    var local = '变量'
    function foo(){
    console.log(local)
    } //这段代码中函数 foo 和变量 local的总和就叫闭包
  2. 让这些变量始终保持在内存中(缓存数据)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function f1(n){
    return function(){
    return n++;
    }
    }
    var a = f1(1)
    a() //1
    a() //2
    a() //3
    //这段代码中,闭包使得内部变量记住上一次调用时的运算结果。
  3. 封装对象的私有属性和私有方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function f1(n){
    return function(){
    return n++;
    }
    }
    var a = f1(1)
    a() //1
    a() //2
    a() //3

    var b = f1(5)
    b() //5
    b() //6
    b() //7
    //这段代码中,a 和 b 是相互独立的,各自返回自己的私有变量。

    }
总结:闭包的用处:
可以间接调用函数内部的局部变量。
可以让这些变量的值始终保持在内存中。
可以暂存数据,给变量开辟私密空间,避免外部污染。

第5题: call、apply、bind 的用法分别是什么?

在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向

  • call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。
  • apply() 方法调用一个函数, 其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数。
  • bind() 方法创建一个新的函数,被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。
对于apply、call二者而言,作用完全一样,只是接受参数的方式不太一样,call需要把参数按顺序传递进去,而 apply则是把参数放在数组里。
  • apply 、call、bind三者都是用来改变函数的this对象的指向的;
  • apply、 call、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
  • apply、 call 、bind三者都可以利用后续参数传参;
  • bind 是返回对应函数,便于稍后调用;apply 、call则是立即调用 。
举例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var obj = {
x: 21,
};

var foo = {
getX: function() {
return this.x;
}
}

console.log(foo.getX.bind(obj)()); //21
console.log(foo.getX.call(obj)); //21
console.log(foo.getX.apply(obj)); //21

// 三个输出的都是21,但是注意看使用 bind() 方法的,他后面多了对括号。
//也就是说,区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。

参考博客

第6题:请说出至少 8 个 HTTP 状态码,并描述各状态码的意义

一篇介绍状态码的博客
HTTP状态码 维基百科

  • 1xx消息(这一类型状态码,代表请求已被接受,需要继续处理)
  • 2xx成功(这一类型状态码,代表请求已被成功接收,理解,并接受)
  • 3xx重定向(这一类型状态码,代表需要客户端采取进一步的操作才能完成请求)
  • 4xx客户端错误(这一类型状态码,代表客户端看起来发生了错误,妨碍了服务器的处理)
  • 5xx服务器错误(这一类型状态码。代表服务器无法完成明显有效的请求)

1.状态码200表示:请求成功。
2.状态码202表示:服务器已接受请求,但尚未处理。
3.状态码204表示:服务器已经成功处理了部分GET请求。
4.状态码301表示:请求的资源已被永久的分配了新的URL。
5.状态码302表示:请求的资源临时的分配了新的URL。
6.状态码400表示:请求错误,请求报文中存在语法错误。
7.状态码401表示:发送的请求需要有通过HTTP认证的认证信息。
8.状态码403表示:禁止请求,请求被服务器拒绝了。
9.状态码404表示:服务器上无法找到请求的资源。
10.状态码500表示:服务器错误,服务器端在执行请求时发生了错误。
11.状态码503表示:服务器暂时处于超负荷或正在进行停机维护,现在无法处理请求。

第7题:请写出一个 HTTP post 请求的内容,包括四部分。

其中
第四部分的内容是 username=ff&password=123
第二部分必须含有 Content-Type 字段
请求的路径为 /path

1
2
3
4
5
6
7
8
1 POST /path HTTP/1.1
2 Host: www.baidu.com
2 User-Agent: curl/7.63.0
2 Axxept: */*
2 Content-Length: 24
2 Content-Type: application/x-www-form-urlencoded
3
4 username=ff&password=123

参考博客

第8题: 请说出至少三种排序的思路,这三种排序的时间复杂度分别为

  • O(n*n)
  • O(n log2 n)
  • O(n + max)
冒泡排序O(n*n)

每次比较两个相邻的数字作比较,小的放前面,大的放后面,这样一轮下来小的数字就放在了最前面,直到所有的数字排完。

快速排序O(nlogn)

到其中一个数作为标杆,小于标杆的数字放在左边,大于标杆的数字放在右边,这一次排完之后再重复前面的操作,直到有一个数字为止。

基数排序O(n+max)

所有正整数总从最低的位为开始,把相同的排列在一起,然后再排十位,不够的补零。然后再依次排下一位,这样排列下来。再挨个去取即可得到有序数列。

第9题: 一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么

1.URL输入

URL的格式定位了需要请求的地址,格式如下

  • 协议类型:[//服务器地址[:端口号]][/资源层级UNIX文件路径]文件名[?查询][#片段ID]
2.DNS解析
  • DNS解析就是根据域名查找IP地址并找到服务器。DNS解析过程就是寻找哪台服务器上有你需要资源的过程,例如www.zhihu.com,其实不是知乎网站真正意义上的地址。互联网上每一台计算机的唯一标识是它的IP地址,但是IP地址并不方便记忆。用户更喜欢用方便记忆的网址去寻找互联网上的其它计算机,也就是上面提到的百度的网址。所以互联网设计者需要在用户的方便性与可用性方面做一个权衡,这个权衡就是一个网址到IP地址的转换,这个过程就是DNS解析。它实际上充当了一个翻译的角色,实现了网址到IP地址的转换。
3.TCP连接
知道了服务器的IP地址,下面就开始与服务器建立连接了。
  • 客户端向服务器发送了一个建议连接的请求(您好,我想认识你);
  • 服务器接到请求后发送同意连接的信号(好的,很高兴认识您);
  • 客户端接到同意连接的信号后,再次向服务器发送了确认信号(我也很高兴认识您),然后,客户端与服务器两者建立了联系;
4.发送HTTP请求
  • 发送HTTP请求的过程就是构建HTTP请求报文并通过TCP协议中发送到服务器指定端口(HTTP协议80/8080, HTTPS协议443)。

  • 数据经过应用层、传输层、网络层、数据链路层、物理层逐层封装,传输到下一个目的地。其中每一层的作用如下:

#####(5)应用层:
为应用进程提供服务,加应用层首部封装为协议数据单元。

#####(4)传输层:
实现端到端通信,加TCP首部封装为数据包,TCP控制了数据包的发送序列的产生,不断的调整发送序列,实现流控和数据完整。

#####(3)网络层:
转发分组并选择路由;加IP首部封装为IP分组。

#####(2)数据链路层:
相邻的节点间的数据传输;加首部[mac地址]和尾部封装为帧。

#####(1)物理层:
具体物理媒介中的数据传送,数据转换为比特流。
下一个目的地接受到数据后,从物理层得到数据然后经过逐层的解包 到 链路层 到 网络层,然后开始上述的处理,在经网络层 链路层 物理层将数据封装好继续传往下一个地址。
到达最终目的地,再经过5层结构,逐层剥离,最终将数据送到目的主机的目的端口。

  • 一个正常的 http request header 一般包括请求的方法,例如GET或者POST等,不常用的还有PUT 和 PATCH, DELETE, HEAD, OPTIONS,以及TRACE
  • 一般的浏览器只能发送GET和POST请求

    客户端向服务器发起http请求的时候,会有一些请求信息,请求信息包括四个部分(最少三部分,第四部分可省略)
    1.请求方法 URI协议/版本
    2.请求头(Request Header)
    3.请求正文:
    4.要上传的内容

5.服务器处理请求并返回HTTP报文
HTTP响应报文也是由三部分组成:状态码,响应报头,响应报文。
6.浏览器解析并渲染页面
浏览器是一个边解析边渲染的过程。首先浏览器解析HTML文件构建DOM树,然后解析CSS文件渲染树,等到渲染树完成后,浏览器开始布局渲染树并将其绘制到屏幕上。
7.连接结束
1.客户端向服务器发送一个断开连接的请求(不早了,我该走了);
2.服务器接受到请求后发送确认收到请求的信号(知道了);
3.服务器向客户端发送断开通知(我也该走了);
4.客户端接到断开通知后断开连接并反馈一个确认信号(好的),服务器收到确认信号后断开连接;

第10题:如何实现数组去重?

假设有数组 array = [1,5,2,3,4,2,3,1,3,4]
你要写一个函数 unique,使得
unique(array) 的值为 [1,5,2,3,4]
也就是把重复的值都去掉,只保留不重复的值。
要求:
不要做多重循环,只能遍历一次
请给出两种方案,一种能在 ES 5 环境中运行,一种能在 ES 6 环境中运行(提示 ES 6 环境多了一个 Set 对象

ES5写法1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function unique(array){
var tempArr = []
var hash = {}
for(var i=0; i<array.length; i++){
if(hash[array[i]] === undefined){
tempArr.push(array[i])
hash[array[i]] = 1
}
}
return tempArr
}

var array = [1,5,2,3,4,2,3,1,3,4]
unique(array)
  • 当数组中出现数字3 和字符串 ‘3’时,第一种写法会将其当成同一个值返回
ES5写法2:
1
2
3
4
5
6
7
8
9
10
11
12
function uniqueCom(array) {
var hash = {}
var tempArray = []
array.forEach(function(value) {
var key = (typeof value) + value;
if (!hash[key]) {
hash[key] = true
tempArray.push(value)
}
})
return tempArray
}
  • 第二种写法可以判断数字3 和字符串 ‘3’的不同。
    ES6写法: 使用Set对象,MDN文档:
    1
    2
    3
    function unique(array){
    return Array.from(new set(array))
    }