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>元素的形式
- 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;}
}
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上