前端知识点回顾

515 阅读19分钟

HTML

HTML5的新特性

  • 增加了语义化标签:<article> <section> <header> <footer> <nav> <aside>
  • 绘画:<canvas>(使用脚本来绘制图形的HTML元素)
  • 媒体标签:<video> <audio>
  • 其他标签:<command> <details> <figure> <mark> <progress> <time> <dialog>
  • <form>新属性:autocomplete,<input>新属性:autofocus、autocomplete
  • 表单元素增加了类型:date、color、search、range、url、email
  • 新增了技术:WebSocket(在单个TCP连接上进行全双工通信的协议)、WebWorker(为JS单线程开辟一个子线程,Worker线程向主线程通信是通过postMessage API,主线程监听message事件)
  • 用manifest设置缓存资源:<html lang="en" manifest="demo.appacache">
  • 本地存储技术:localStorage、sessionStorage

<article> 与 <section>的区别

  • section:从字面理解就是区块、部分的意思,通常用来对页面内容进行分块(比如导航菜单、文章正文、文章评论部分都可以成为内容区块)
  • article:代表文档、页面等可以被外部引用的内容,可以是一篇博客或报刊中的文章、一段用户评论或其他独立的内容。一个article通常要有标题,包含在\<header>里。
    • article元素可以嵌套。内层的<article>与外层的要有关联,比如在一篇文章中,评论就可以使用嵌套\<article>元素的形式

对整个HTML结构进行语义化的规范操作的好处

  • 有利于SEO、爬虫(根据标签来确定上下文和关键字的权重)
  • 方便其他设备解析(盲人阅读器、屏幕阅读器、移动设备)以有意义的方式来渲染网页
  • 便于团队开发和维护:语义化根据可读性,减少差异化

<DOCTYPE>的作用

不是html标签,用来说明当前HTML或XHTML文档是什么版本,方便浏览器用适合的解析器来解析文档

HTML5通常定义为\<DOCTYPE html>

canvas和svg的区别

  • canvas:标签是图形容器,使用js来绘制图形,依赖分辨率,不支持事件处理器,能够以.png 或 .jpg 格式保存结果图像。
  • svg:是一种基于xml描述2D图形的语言,绘制的图形是不依赖分辨率的矢量图形,绘制的每个元素都是一个节点,可以为元素添加事件处理器

src和href的区别

  • src:当浏览器解析到这个元素,会停止对其他资源的处理,知道src指向的资源下载并嵌入到当前文档
  • href:指向的是网络资源的位置,建立与当前文档的联系,不会阻塞其他资源的加载

em与rem的区别

  • em:是相对单位,相对当前对象的文本的字体大小
  • rem:是相对单位,相对根元素的字体大小

CSS

CSS3新增的选择器

  • nth-child(n)
  • first-child、last-child、only-child
  • nth-of-type
  • first-of-type、last-of-type、only-of-type
  • :empty
  • :not
  • 属性选择器,属性名称的匹配 [type^=val] [type$=val] [type*=val]
  • :target
  • :enable、:disable、:checked
  • :root
  • ::before、::after

animation

animation:name duration timing-function delay iteration-count direction fill-mode

  • name:为 @keyframes 动画指定一个名称
  • duration:动画在多少s或ms内完成
  • timing-function:动画的速度
    • linear匀速
    • ease(默认)先加速,在结束前变慢
    • ease-in:以低速开始
    • ease-out:以低速结束
    • ease-in-out:以低速开始和结束
  • iteration-count:动画执行的次数
  • direction:是否来回播放、反向动画
    • normal:默认值
    • reverse:反向动画
    • alternative:来回播放,第一次往终点方向运动,第二次从终点向起点运动
    • alternative-reverse:与alternative相反
  • fill-mode:动画完成时或当动画由一个延迟未开始播放时,物体的属性/状态
    • forwards:保持最后一帧的样式(位置)
    • backwords:保持启动动画的第一帧的样式,当方向为reverse或alternative-reverse时,保持to关键帧中的样式。
animation:mymove 3s;
animation-iteration-count:2;
animation-fill-mode:forwards;

@keyframes mymove
{
	from {top:0px;}
	to {top:200px;}
}

animations

transition

transition:property duration timing-function delay

  • property:指定要过渡的属性
div
{
	width:100px;
	height:100px;
	background:red;
	transition:width 2s;
	-webkit-transition:width 2s; /* Safari */
}

div:hover
{
	width:300px;
}

animations与transition的区别

  • animations不需要手动触发,能返回起始状态
  • animations可以指定多个关键帧,transition只能指定起始和结束状态

transform

属性:

  • translate(x,y):定义2D转换
  • translate3d(x,y,z):定义3D转换
  • translateX(X)、translateY(Y)、translateZ(Z)
  • rotate(angle):2D旋转
  • rotate3d(x,y,z,angle):3D旋转
  • rotateX(X)、rotateY(Y)、rotateZ(Z)
  • scale(x,y):2D缩放
  • scale3d(x,y,z):3D缩放
  • scaleX(X)、scaleY(Y)、scaleZ(Z)
  • skew(x-angle,y-angle):沿着x轴和y轴的倾斜角度
  • skewX(angle)、skewY(angle)
  • perspective(n):为3D转换元素定义透视视图

与transform:rotate()搭配的可以有:

transform-origin:x-axis y-axis z-axis; -- 设置旋转元素的基点位置

  • 参数可以是百分比,默认是50% 50% 0,以中心点为旋转中心
  • x-axis:left、center、right
  • y-axis:top、center、bottom

flex布局

采用flex布局的元素称为flex容器,flex容器默认有两根轴,分别是水平方向的主轴和垂直方向的交叉轴。子元素默认沿着主轴方向排列。

设置在容器上的属性有:

  • flex-direction:设置主轴的方向
  • flex-wrap:设置子元素如果在一条轴线上排布不下是否换行
  • justify-content:设置子元素在主轴上的对齐方式
  • align-items:设置子元素在交叉轴上的对齐方式

设置在子元素上的属性有:

  • flex-grow:设置子元素的放大比例,在剩余空间能分配到的比例
  • flex-shink:设置子元素的缩晓比例
  • flex-basic:表示在分配剩余空间前,子元素在主轴上占据的空间大小,默认是auto
  • flex:是flex-grow flex-shink flex-basic的缩写
  • order:子元素的排列顺序

JavaScript

基本数据类型

null undefined string boolean number Symbol

typeof 返回的数据类型

string number boolean function undefined object Symbol

event.target和event.currentTarget

  • event.target返回触发事件的元素

  • event.currentTarget返回绑定事件的元素

事件委托

事件委托:将事件绑定在父元素上,利用事件的冒泡,从而父元素能够处理子元素的事件响应

好处:

  • 减少事件的注册,提高性能
  • 通过事件委托添加的事件,对后来添加的子元素仍有效

闭包是什么,使用的场景

  • 闭包是一个对象,里面保存着内部函数引用外部函数的变量和变量的值
  • 闭包如何形成:函数的嵌套,内部函数引用外部函数的变量,返回内部函数
  • 闭包的好处:能访问函数内部的值,能将这些变量一直保存在内存中
  • 闭包的坏处:闭包将引用的变量保存在内存中,如果滥用闭包,会导致内存消耗,影响网页性能,在ie中可能会导致内存泄漏
  • 闭包使用的场景:防抖函数、节流函数

作用域、执行上下文、作用域链、闭包

  • 作用域:是变量适用的范围,控制变量的可见性
  • 作用域与执行上下文的区别:函数作用域是函数声明时确定的,函数执行上下文是函数调用时创建的
  • 作用域链:查找变量时,会从当前作用域开始查找,找不到则向上一级作用域中找,直到找到第一个匹配的标识符位置或全局作用域,这就是js的作用域链
  • js的执行上下文环境是栈类型
  • 闭包能访问外部函数的内部变量的原因是:外部函数的执行上下文环境没有被销毁,仍存在执行上下文栈中

节流函数、防抖函数

防抖函数:

let li=document.getElementById('li')
    function f(fn,delay,arg) {
        console.log(arg);
        let timer;
        return function () {
            if(timer){
                clearTimeout(timer);
                timer=null;
            }
            timer=setTimeout(function () {
                fn.call(arg)    //绑定this是触发事件的dom对象
            },delay)
        }
    }
    li.addEventListener("click",f(function (c) {
        console.log(this.attributes); 
    },2000,li))

节流函数:

function jieliu(fn,delay,arg) {
        let timer=null;
        return function () {
            if(timer){
               console.log('时间还没到')
               return;
            }
            timer=setTimeout(function () {
                clearTimeout(timer);
                timer=null;
                fn.call(arg);
            },delay)
        }
    }
    li.addEventListener("click",jieliu(function () {
        console.log(this.attributes);
    },2000,li))
节流函数:以固定的频率触发,防止用户多次触发,但又想给用户有所响应
防抖函数:多次触发只有最后一次有效

对原型和原型链的了解

  • 每个对象都有隐形属性__proto__指向其构造函数的原型对象,除了通过Object.create()构造的对象(这个对象的构造函数是Object)
  • 每个函数都有prototype属性,指向一个对象
  • 每个对象都是Object的实例,因为原型链的最顶层就是Object的原型对象
  • 原型对象上的属性被实例共享
  • 当在一个对象上找不到属性时,会沿着原型链查找

继承的几种方式

ES5的继承:

  • 原型链继承:使子类的原型指向父类的实例
    • 缺点:无法向父类构造函数动态传参
  • 构造函数继承:在子类构造函数里通过.call调用父类构造函数
    • 优点:能向父类构造函数动态传参
    • 缺点:只能继承父类的实例属性,而且子类实例依旧不属于父类这一类型,违背了继承
  • 组合式继承(前两种结合):
    • 优点:子类实例继承了父类的实例属性+原型属性,而且子类在原型上添加属性和方法也不会影响父类的原型
    • 缺点:调用了两次父类构造函数
  • 寄生组合式继承:构造一个新的空函数充当父类构造函数,要让这个空函数的原型指向父类的原型,然后让子类的原型指向这个空函数的实例对象

ES6的继承:

  • 使用extends和super关键字实现继承

不同点:

  • ES5是先创建子类实例,再往子类实例添加父类的属性
  • ES6是先在子类构造函数里通过super函数创建指向父类实例的this,在往this添加属性

实现一个对象的深拷贝

需要考虑的因素很多,比如

  • 引用类型还包括function,函数还分为箭头函数
  • 使用typeof判断是否是对象类型,还需考虑null这个情况
  • 类型可能包括由内置对象创建的对象,Number、Boolean、RegExp...
  • 除了object、array可以继续遍历,还有map和set类型
  • 对象的类型可能指向这个对象,循环引用,如果未处理这个清空,会导致内存溢出

以下没有考虑map、set、内置对象的情况,详细参考

## 判断是否是引用类型
function isObj(target){
    let type=typeof target;
    return type!==null && (type==='object'||type==='function')
}
## 函数类型的拷贝,箭头函数没有prototype
function cloneFunc(func){
    const funcString=func.toString();
    //箭头函数
    if(!func.prototype){
        return eval(funcString); //通过eval和函数的字符串形式构建箭头函数
    }
    const regBody=/(?<=\{)(.|\n)+(?=\})/m
    const regParm=/(?<=\().+(?=\)\s*\{)/;
    const parm=regParm.exec(funcString); //exec是正则对象的方法,match是字符串的方法
    const body=regBody.exec(funcString);
    if(parm){
        parm=parm[0].split(",");
        return new Function(...parm,body[0]) //第一个参数传递要构造的函数的参数,第二个是函数体
    }else{
        return new Function(body[0])
    }
}
function deepClone(target,map=new WeakMap()){
    if(!isObj(target)){
        return target;
    }
    if(map.get(target)){    //是否访问过该对象
        return map.get(target);
    }
    let res;
    let type=typeof target;
    if(type==='function'){
        res=cloneFunc(target);
        map.set(target,res)
    }
    else if(Array.isArray(target)){
        res=[];
        map.set(target,res);
        let arr=target.slice(0);
        while(arr.length){
            res.push(deepClone(arr.shift(),map))
        }
    }
    else{
        res={};
        map.set(target,res);
        let keys=Object.keys(target);
        let len=keys.length;
        let index=0;
        while(index<len){
            res[keys[index]]=deepClone(target[keys[index]],map)
            index++;
        }
    }
    return res;
}
使用WeakMap而不使用Map的原因:
WeakMap也是存放键值对,它的键要求是对象类型,而且是弱引用,则被认为是不可访问(弱访问),随时可能被回收
而Map如果把一个对象作为键,然后手动将对象=null进行释放,可是Map对它是强引用,因此这部分内存依旧不可被释放。

let obj = { name : 'ConardLi'}
const target = new Map();
target.set(obj,'code秘密花园');
obj = null;

ES6 -- let、const

  • ES5没有块级作用域,只有函数作用域和全局作用域
  • ES6引入了块级作用域,即函数内部和{}围起来的区域

var和let、const的区别

  • 有块级作用域,let和const属于块级声明的一种
  • 会出现暂时性死区,变量声明之前不可访问
  • 变量不会被提升
  • 不能重复声明
  • let和const声明的全局变量不会添加到全局对象window的属性

let和const的区别

  • let可以先声明不赋值,const声明时必须赋值
  • const声明的变量不可更改,如果是引用类型则内部数据可以更改,指向的内存地址不可更改

循环

for(var i=0;i<3;i++){
        var i='ac';  //改变了循环的i,只会进行一次循环
        console.log(i);
    }
输出一次:ac    
for(let i=0;i<3;i++){
        let i='ac';
        console.log(i);
    }
输出三次:ac    

重复声明变量i不会出错的原因:

用let在for循环声明的变量,会在()内建立一个隐形的作用域,所以{}内的同名的变量i不会出错,因为它属于另一个作用域

对ES6的了解

箭头函数

  • 箭头函数的this是在函数定义时绑定的,不是在调用时绑定的,是从上一级作用域继承this
  • 箭头函数不能通过call、apply绑定this,只能用来传递参数,所以传递的第一个参数会被忽略
  • 箭头函数没有prototype属性
  • 箭头函数没有自己的arguments,如果访问了,则访问的是上级作用域的
  • 箭头函数不能使用yield关键字,因此箭头函数不能用作生成器generator
  • 箭头函数不能用作构造器,不能使用new
<script type="text/javascript">
    // es5
    function sum(){
        console.log(this)
    }
    sum()       
    // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

    // es6箭头函数
    var sum1 = ()=>{
        console.log(this)
    }
    sum1()      
    //Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
</script>
<script type="text/javascript">
    var app = document.getElementById('app');
    
    // es5
    // app.onclick = function(){
    //     console.log(this)       //指向触发事件的当前元素对象
    // }

    // es6箭头函数
    app.onclick = ()=>{
        console.log(this)  
        // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    }

    // 总结: 在全局定义事件处理函数,es6箭头函数指向window。无论什么时候,es5的this始终指向触发当前事件的对象元素,
   
</script>

promise

是异步编程的解决方案,比传统的异步解决方案【事件】【回调函数】更合理和强大

关于promise中reject和catch的问题

  • 当调用reject,则一定会进入then的第二个回调,如果没有第二个回调,才会进入catch
  • 当then中的第二个回调出错,则会进入catch
  • 当调用resolve,且传递的参数不是Promise类型或传递的是fulfilled状态的Promise,则一定会进入then中的第一个回调,即使没有第一个回调也不会进入catch
  • 当出现网络异常就会直接进入catch而不会进入then的第二个回调

有哪些性能优化的方法

  • 使用精灵图,减少http请求
  • 将css代码放在页面头部
  • 使用link代替@import
  • 将script标签放在body底部
  • js和css尽量使用引用外部文件的形式,因为js和css文件能够在浏览器中产生缓存
  • 合并多个css文件和js文件
  • 最小化对dom的访问

网络

get和post的区别

get和post是什么?是http协议中两种发送请求的方法

  • get将传递的参数加在url后面,post放在请求体中
  • get请求可以缓存
  • get的请求参数会保存到浏览器的历史记录里
  • 回退页面时,对get请求没有影响,对于post会重新提交请求
  • get请求只能进行url编码,而post支持多种编码方式
  • get请求对传递的数据的大小有限制,post没有限制
  • get会产生一个TCP数据包:浏览器会把http header和data一并发送,服务器响应200。post会产生两个TCP数据包,先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok
其实本质上get和post没有区别,本质上就是TCP连接。
只是HTTP协议的规定和服务器的限制导致在应用上有区别。
HTTP规定了传递的参数加在的位置,浏览器和服务器限制了传递的参数的大小。

cookie和session的区别

共同点:都是用来保存用户相关信息,让服务端能识别用户

不同点:

  • cookie由服务端产生,保存在客户端
  • session由服务端产生,保存在服务端
  • session的实现需要使用cookie:session技术是将数据保存在服务端的技术。服务端先检测HTTP请求是否包含了session-id,没有则产生session-id返回给客户端,客户端保存在cookie里,下次发送请求时再将cookie携带上去。有则根据session-id检索出属于客户端的session

如何保证cookie的安全性

  • 对保存到cookie的私密数据进行加密
  • 设置http-only=true
  • 给cookie设置有效期
  • 给cookie加个时间戳和ip戳,实际就是让cookie在同个ip下过多少时间后失效

Ajax的原生写法

let xhr=new XMLHttpRequest();
xhr.open("get","index.php",true);
xhr.onreadystatechange=function(){
    if(xhr.readyState===4){
        if(xhr.status===200){
            console.log(xhr.responseText)
        }
    }
}
xhr.send();

为什么有同源策略

  • 两个页面的协议、域名、端口号一致,则表示同源
  • 设置同源限制是为了安全,否则浏览器的cookie等其他数据可以任意读取,不同域下的dom可以随意操作,Ajax可以任意发送请求,如果浏览了恶意网站则会泄露隐私数据。

HTTP

HTTP是应用层上的一种客户端服务端模型的通信协议,由请求和响应组成,是无状态的

HTTP请求

请求行(请求方法+url+协议版本号)+请求头+请求体

HTTP响应

状态行(协议版本+状态码+状态码描述)+响应头+响应体

HTTP状态码

200:请求被正常处理

204:请求处理成功但没有数据实体返回。比如请求方法是head

301:永久重定向

302:临时重定向

400:请求报文出现语法错误

401:请求要求用户的身份认证

403:没有获得服务器的访问权限,ip被禁止

404:请求的资源不存在或服务器拒绝请求

500:服务器内部错误,无法完成请求

跨域的方法

script、link、image这三个标签天生就有跨域属性

  • jsonp:只能发送get请求
  • document.domain:只能解决主域相同的跨域问题
  • window.name+一个代理的iframe
  • Websocket:全双工通信
  • CORS
  • 使用postMessage API
  • 代理服务器:同源策略对服务器不加限制
  • nginx反向代理:同上

从发送url到页面显示,中间发生了什么?

  • DNS域名解析,获取IP地址
  • 通过三次握手建立TCP连接
    • 客户端向服务端发送SYN包(SYN表示建立一条新连接):SYN=1,随机产生一个Seq(序列号)=x
    • 服务端接收到数据包后,由SYN=1知道client请求建立连接,因此将标志位SYN和ACK(确认序号有效)置为1,之后返回SYN+ack包:SYN=1,ack=x+1,随机产生Seq=y
    • 客户端接收到之后,确认ack是否=x+1,标志位ACK是否=1,是则返回ack包,ack=y+1,然后server检测ack是否=y+1,ACK是否=1,是则连接建立成功
  • 发送HTTP请求,server返回数据
  • 浏览器解析响应报文
    • 将html文档解析,构建DOM树
    • 将css解析,构建CSSOM树
    • 如果中途遇到script标签会停止其他内容的解析
    • 遍历DOM树的可见节点,找到CSSOM树上对应的CSS样式,合并成Render树
    • 计算render树上节点的位置和大小,最后绘制到页面上
  • 四次挥手断开TCP连接
    • 客户端发送FIN包:FIN=M,意味着这一方向没有数据流动
    • 服务端收到之后,返回ACK=M+1
    • 服务端发送完毕,发送FIN=N包
    • 客户端返回ACK=N+1

需要四次挥手断开的原因:TCP是全双工通信,所以每个方向需要单独的关闭。当收到对方的FIN报文时,仅仅是说明对方不再发送数据但可以接收数据。

MVVM和MVC的区别

  • MVC:是后端分层开发的概念。V层接收用户的输入操作,C层响应view的事件,当涉及到数据的增删改查曾调用model层的接口对数据进行操作,model层数据变化后通知v层更新视图。
  • MVVM:与MVC最大的区别就是实现了m层与v层的自动同步,vm层是m层和v层的桥梁,与v层进行双向数据绑定,与m层通过接口的形式进行数据交换。开发者不用手动操作dom就能实现v层的更新。(在vm层直接修改数据即可实现v层的更新)

MVVM与jQuery的区别

jQuery需要操作dom来更新视图

MVVM以数据驱动视图,只关心数据,无需手动操作都没,dom的操作被封装

vue和react的区别

共同点:

  • 都有虚拟dom
  • 组件化开发
  • 数据驱动视图
  • 都有状态管理,react有redux,vue有vuex

不同点:

  • vue是MVVM模式,react是MVC中的v层
  • vue实现数据的双向绑定,react数据流动是单向的
  • vue会跟踪每个组件的依赖关系,不需要重新渲染整个组件树。对于react,只要应用的状态发生改变,全部组件就会重新渲染,所以react需要shouldComponentUpdate这个生命周期函数来进行控制

模块化和组件化

  • 组件化从功能出发,强调的是功能性和可复用性
  • 模块化从业务逻辑出发,强调的是完整性和业务性
  • 好处是:提高代码的复用性,解耦

Vue

Vue的render函数

render函数是用来解析模板字符串的,通过js代码来构建虚拟的dom树,然后再调用patch函数将虚拟的dom树替换成真实的dom

Vue如何实现双向数据绑定

  • v-m:通过事件监听
  • m-v:通过Object.defineProperty实现数据劫持+订阅和发布模式

Vue如何实现响应式

  • 响应式是什么?当数据发生改变,vue能监听到
  • 实现:用Object.defineProperty,将data的属性代理到vm上