Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2018前端面试知识点整理 #11

Open
rico-c opened this issue Dec 2, 2018 · 0 comments
Open

2018前端面试知识点整理 #11

rico-c opened this issue Dec 2, 2018 · 0 comments
Labels

Comments

@rico-c
Copy link
Owner

rico-c commented Dec 2, 2018

HTML

前端语义化

  • 不仅要使机器(有助于SEO)易于理解,也要使人易于理解
  • 尽可能少的使用无语义的标签div和span
  • 多使用 强调(strong ),段落(p),标题用(h1),页眉(header),页脚(footer),导航链接 (nav),边栏(aside)

META viewport

<meta name="viewport" content="width=devidce-width",user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimun-scale=1.0">

属性 可选值 描述
width device-width/指定数字 设置viewport宽度
height device-height/指定数字 设置viewport高度
initial-scale 指定数字 设置viewport初始缩放比例
minimum-scale 指定数字 设置viewport最小缩放比例
maximum-scale 指定数字 设置viewport最大缩放比例
user-scalable yes/no/1/0 设置viewport是否允许用户缩放

HTML5新特性

  • 语意化标签(nav、aside、dialog、header、footer等)
  • canvas
  • 拖放相关api
  • Audio、Video
<video src="movie.ogg" width="320" height="240" controls="controls">
Your browser does not support the video tag.
</video>
<audio src="song.ogg" controls="controls">
Your browser does not support the audio tag.
</audio>
  • 获取地理位置

使用 getCurrentPosition()方法来获得用户的位置

  • web存储(localStorage、sessionStorage)

localStorage

属性

localStorage 方法存储的数据没有时间限制

localStorage的使用遵循同源策略

每个域名容量5M

用法
  1. localStorage.setItem("name", "张三"); 在本地客户端存储一个字符串类型的数据
  2. var data = localStorage.getItem("name"); 读取已存储在本地的数据
  3. var data2 = localStorage.removeItem("name"); 从本地存储中移除键名为name的数据
  4. localStorage.clear() 移除本地存储所有数据

sessionStorage

属性

sessionStorage 方法针对一个 session 进行数据存储。当用户关闭浏览器窗口后,数据会被删除。

用法

sessionStorage的四个用法与localstorage一致。

Web 存储的使用优缺点

优点

  • localStorage拓展了cookie的4K限制

缺点

  • 浏览器的大小不统一,并且在IE8以上的IE版本才支持localStorage这个属性
  • 所有的浏览器中都会把localStorage的值类型限定为string类型
  • localStorage不能被爬虫抓取到
  • webSocket

Web Socket

意义

HTTP 协议有一个缺陷:通信只能由客户端发起 ,websocket可以实现服务端主动推送。

属性

(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

用法

//客户端与服务器进行连接
var ws = new WebSocket("wss://echo.websocket.org");
//指定连接成功后的回调函数
ws.onopen = function(evt) { 
  console.log("Connection open ..."); 
  ws.send("Hello WebSockets!");
};
//指定收到服务器数据后的回调函数
ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};
//指定连接关闭后的回调函数
ws.onclose = function(evt) {
  console.log("Connection closed.");
};

缺点

对前端开发者,往往要具备数据驱动使用javascript的能力,且需要维持住ws连接(否则消息无法推送);对后端开发者而言,难度增大了很多,一是长连接需要后端处理业务的代码更稳定(不要随便把进程和框架都crash掉),二是推送消息相对复杂一些,三是成熟的http生态下有大量的组件可以复用,websocket则太新了一点。

CANVAS、SVG

区别

canvas为标量图,svg为矢量图,canvas为html5新增。

canvas

  • HTML

<canvas id="canvas" height="200" width="350"></canvas>

  • JS
let canvas=document.getElementById("canvas");
let ctx = canvas.getContext('2d');
//绘制表框
function drawBackground() {
      ctx.save();
      ctx.translate(r, r);
      ctx.beginPath();//起始一条路径,或重置当前路径
      ctx.lineWidth = 10 * rem; //以0,0为原点,r为半径,0为起始角,2*Math.PI为结束角,顺时针画圆
	  ctx.arc(0, 0, r - ctx.lineWidth / 2, 0, 2 * Math.PI, false); //画圆
	  ctx.stroke();//绘制已定义的路径
}
//绘制秒针
function drawSecond(second) {
      ctx.save();
      ctx.beginPath();
      ctx.fillStyle = '#c14443';
      ctx.rotate(2 * Math.PI / 60 * second);//旋转
      ctx.moveTo(-2, 20 * rem);
      ctx.lineTo(2, 20 * rem);
      ctx.lineTo(1, -r + 18 * rem);
      ctx.lineTo(-1, -r + 18 * rem);
      ctx.fill();
      ctx.restore();
      ctx.drawImage(img,10,10);//向画布上绘制图像、画布或视频
  }
  
function draw() {
      ctx.clearRect(0, 0, width, height);//清除前一帧画面
      var now = new Date();//获取此刻时间
      var seconds = now.getSeconds();
      drawBackground();
      drawSecond(seconds);
  }
setInterval(draw, 1000); 

canvas模糊问题

Canvas 在高清屏下绘制图片变模糊

不管当前的devicePixelRatio的值是多少,统一将canvasDOM节点的width属性设置为其csswidth属性的两倍,同理将height属性也设置为cssheight属性的两倍,即:

<canvas width="320" height="180" style="width:160px;height:90px;"></canvas>

这样整个 canvas 的坐标系范围就扩大为两倍,但是在浏览器的显示大小没有变,canvas画图的时候,按照扩大化的坐系来显示,不清晰的问题就得以改善了

正则表达式

  • 基础:
\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,“n”匹配字符“n”。“\n”匹配一个换行符。串行“\\”匹配“\”而“\(”则匹配“(”。
^ 匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。
$ 匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置。
* 匹配前面的子表达式零次或多次。例如,zo*能匹配“z”以及“zoo”。*等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。
? 匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“does”或“does”中的“do”。?等价于{0,1}。
{n} n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。
{n,} n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。
{n,m} m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。
? 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+?”将匹配单个“o”,而“o+”将匹配所有“o”。
. 匹配除“\n”之外的任何单个字符。要匹配包括“\n”在内的任何字符,请使用像“`(.
  • 使用:

    if(!(正则表达式.test(xxxx)))

  • 邮箱

/^([a-z0-9_\.-]+)@([0-9a-z\.-]+)\.([a-z]{2,6})$/

  • 密码 6-16位数字和字母的组合

/^[a-z0-9_-]{6,16}$/

  • 手机号

if(!(/^1[34578][0-9]{9}$/.test(phone)))

Web Worker

web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。您可以继续做任何愿意做的事情:点击、选取内容等等,而此时 web worker 在后台运行。用于更耗费 CPU 资源的任务。

使用

  1. 创建 web worker 文件,并使用*postMessage()*方法向 HTML 页面传回一段消息。
  2. 创建一个新的 web worker 对象,然后运行 "demo_workers.js" 中的代码。
  3. 向 web worker 添加一个 "onmessage" 事件监听器。
  4. 如需终止 web worker,并释放浏览器/计算机资源,请使用 terminate() 方法。
<!DOCTYPE html>
<html>
<body>

<p>Count numbers: <output id="result"></output></p>
<button onclick="startWorker()">Start Worker</button>
<button onclick="stopWorker()">Stop Worker</button>
<br /><br />

<script>
var w;

function startWorker()
{
if(typeof(Worker)!=="undefined")
{
  if(typeof(w)=="undefined")
    {
    w=new Worker("demo_workers.js");
    }
  w.onmessage = function (event) {
    document.getElementById("result").innerHTML=event.data;
  };
}
else
{
document.getElementById("result").innerHTML="Sorry, your browser
 does not support Web Workers...";
}
}

function stopWorker()
{
w.terminate();
}
</script>

</body>
</html>

CSS

link&@import

区别

1.从属关系区别
@import是 CSS 提供的语法规则,只有导入样式表的作用;link是HTML提供的标签,不仅可以加载 CSS 文件,还可以定义 RSS、rel 连接属性等。

2.加载顺序区别
加载页面时,link标签引入的 CSS 被同时加载;@import引入的 CSS 将在页面加载完毕后被加载。

3.兼容性区别
@import是 CSS2.1 才有的语法,故只可在 IE5+ 才能识别;link标签作为 HTML 元素,不存在兼容性问题。

4.DOM可控性区别
可以通过 JS 操作 DOM ,插入link标签来改变样式;由于 DOM 方法是基于文档的,无法使用@import的方式插入样式。

5.权重区别(该项有争议)
link引入的样式权重大于@import引入的样式。

盒模型

content-box : width == 内容区宽度

border-box : width == 内容区宽度 + padding宽度 + border宽度

  • 如何设置两种模型:

    box-sizing: content-box 是W3C盒子模型    

    box-sizing: border-box 是IE盒子模型

box-sizing的默认属性是content-box

  • border-box IE模型:

  • content-box W3C标准模型

伪元素&伪类

CSS3伪�素������

CSS2�CSS3伪类��

居中

水平居中

  1. 子元素margin:0 auto,子元素要有宽度
  2. 父元素text-align:center,子元素inline-block
  3. 父元素position:relative,子元素position:absolute;left:50%;transform:translateX(-50%)
  4. 父元素display:flex;justify-content:center;(需要子元素有宽度)
  5. 父元素display:tablecell;text-align:center

垂直居中

  1. display:inline-block;vertical-align:middle
  2. 父元素position:relative,子元素position:absolute;top:50%;transform:translateY(-50%)
  3. line-height:200px
  4. 父元素display:tablecell;vertical-align:center
  5. 父元素display:flex;align-items:center(需要子元素有宽度)

水平垂直居中

父{position:relative}
子{
  position:absolute;
  top:0,left:0;right:0,bottom:0;
  margin:auto;}
父{
display:flex;
content-justify:center;
align-items:center;}
父{position:relative}
子{
position:absolute;
left:50%;
top:50%
transform:translate(-50%,-50%)}

行元素、块元素

  • 行内元素

<a>、<strong>、<b>、<em>、<i>、<span>等

行内元素的特点:

(1)和相邻行内元素在一行上。

(2)高、宽无效,但水平方向的padding和margin可以设置,垂直方向的无效。

(3)默认宽度就是它本身内容的宽度。

(4)行内元素只能容纳文本或则其他行内元素。(a特殊 a里面可以放块级元素 )

  • 块级元素

<h1>~<h6>、<p>、<div>、<ul>、<ol>、<li>等

块级元素的特点:

(1)总是从新行开始

(2)高度,行高、外边距以及内边距都可以控制。

(3)宽度默认是容器的100%

(4)可以容纳内联元素和其他块元素。

  • 行内块元素(inline-block)

在行内元素中有几个特殊的标签——<img />、<input />、<td>,
可以对它们设置宽高和对齐属性,有些资料可能会称它们为行内块元素。

行内块元素的特点:
(1)和相邻行内元素(行内块)在一行上,但是之间会有空白缝隙。
(2)默认宽度就是它本身内容的宽度。
(3)高度,行高、外边距以及内边距都可以控制。

左侧固定右侧自适应

left{
  width:100xp;
  float:left
 }
 right{margin-left:100px}
 left{
  position:absolute;
  left:0;
  width:100px}
 right{
  position:absolute;
  left:100px;
  width:calc(100% - 100px)}
 parent{display:flex}
 left{flex:0 0 100px}
 right{flex:1}

两边固定中间自适应

  • 利用flex
	.father {
        display: flex;
        height: 100%;
    }
    .left,
    .right {
        flex: 0 0 100px;
        background-color: red;
    }
    .middle {
        flex: 1;
    }
  • 双飞翼布局(两边定宽,中间自适应)
<div class="box">
  <div class="middle-wrap">
    <div class="middle"></div>
  </div>
  <div class="left"></div>
  <div class="right"></div>
</div>

.box {
    position: relative;
  }
  .middle-wrap {
    position: relative;
    float: left;
    width: 100%;
  }
  .middle-wrap .middle {
    margin: 0 100px; /*留出距离*/
  }
  .left {
    float: left;
    width: 100px;
    margin-left: -100%;
  }
  .right {
    float: left;
    width: 100px;
    margin-left: -100px;
  }

FLEX布局

flex-direction属性

主轴的方向

  • row(默认值):主轴为水平方向,起点在左端。
  • row-reverse:主轴为水平方向,起点在右端。
  • column:主轴为垂直方向,起点在上沿。
  • column-reverse:主轴为垂直方向,起点在下沿。

justify-content属性

项目在主轴上的对齐方式

  • flex-start(默认值):左对齐
  • flex-end:右对齐
  • center: 居中
  • space-between:两端对齐,项目之间的间隔都相等。
  • space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。

align-items属性

交叉轴上如何对齐

  • flex-start:交叉轴的起点对齐。
  • flex-end:交叉轴的终点对齐。
  • center:交叉轴的中点对齐。
  • baseline: 项目的第一行文字的基线对齐。
  • stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。

flex属性

flex属性默认值为0 1 auto

flex:flex-grow, flex-shrink,flex-basis

  • flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。 如果所有项目的flex-grow属性都为1,则它们将等分剩余空间(如果有的话)
  • flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
  • flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。

另外:下面两个表达式相等:

.item {flex: 1;}
.item {
    flex-grow: 1;
    flex-shrink: 1;
    flex-basis: 0%;
}

Grid布局

网格容器

将属性display值设为gridinline-grid就创建了一个网格容器,所有容器直接子结点自动成为网格项目。

  • display: grid网格项目按行排列,网格项目占用整个容器的宽度。
  • display: inline-grid网格项目按行排列,网格项目宽度由自身宽度决定。

显示网格

属性grid-template-rowsgrid-template-columns用于显示定义网格,分别用于定义行轨道和列轨道。

  • 属性grid-template-rows:50px 100px用于定义行的尺寸。
  • 属性grid-template-columns:50px 100px用于定义列的尺寸。
  • 长度值可以是auto,表示轨道尺寸可以根据内容大小进行伸长或收缩。

用网格线编号定位项目

  • 通过设置网格线编号直接定位单个子元素
grid-row-start:    2;
grid-row-end:      3;
grid-column-start: 2;
grid-column-end:   3;
  • 属性grid-columngrid-column-startgrid-column-end的简写形式。
grid-column: 3 / 4; 
//二者等价
grid-column-start:3;
grid-column-end:4;
  • 通过grid-column-startgrid-column-end属性值的设置,使该网格项目跨越多列。行同理。

网格项目的对齐方式

  • 属性justify-items以行轴为参照对齐项目(水平),属性align-items以列轴为参照对齐项目(垂直)。

列举Css3新特性

  • 选择器(包括伪元素、伪类等)
  • 边框(border-image、border-radius、box-shadow)
  • 渐变(linear-gradients、radial-gradents)
  • 字体(@font-face)
  • 转换、形变(transform)
  • 过度(transition)
  • 动画(animation)
  • 弹性盒模型(flex-box)
  • 媒体查询(@media),监听屏幕尺寸的变化

选择器优先级

1、选择器越具体,优先级越高

2、同样优先级,写在后面的覆盖前面的

3、!important > id > class >tag >通配符

选择器继承

可以的有 font-size font-family color
不可以的有 border padding margin background-color width height等

transition&animation

过渡属性transition可以在一定的事件内实现元素的状态过渡为最终状态,用于模拟一种过渡动画效果,但是功能有限,只能用于制作简单的动画效果;

动画属性animation可以制作类似Flash动画,通过关键帧控制动画的每一步,控制更为精确,从而可以制作更为复杂的动画。

transition

transition: property duration timing-function delay;
描述
transition-property 规定设置过渡效果的 CSS 属性的名称。
transition-duration 规定完成过渡效果需要多少秒或毫秒。
transition-timing-function 规定速度效果的速度曲线。
transition-delay 定义过渡效果何时开始

animation

animation: name duration timing-function delay iteration-count direction;
描述
animation-name 规定需要绑定到选择器的 keyframe 名称。。
animation-duration 规定完成动画所花费的时间,以秒或毫秒计。
animation-timing-function 规定动画的速度曲线。
animation-delay 规定在动画开始之前的延迟。
animation-iteration-count 规定动画应该播放的次数。
animation-direction 规定是否应该轮流反向播放动画。

BFC块级格式化上下文

特性

  • BFC元素里面的元素不会在布局上影响外侧的元素,
  • 计算BFC的高度时,浮动元素也参与计算
  • 内部的Box会在垂直方向,一个接一个地放置
  • BFC的区域不会与float box重叠。

作用

  1. 同一个 BFC 下外边距会发生塌陷折叠
  2. BFC 可以包含浮动的元素(清除浮动)
  3. BFC 可以阻止元素被浮动元素覆盖
  • overflow:hidden 清除浮动

  • overflow:hidden 取消父子margin合并(子设置margin,父也会有margin)

    因为BFC内部的元素和外部的元素绝对不会互相影响,因此, 当BFC外部存在浮动时,它不应该影响BFC内部Box的布局,BFC会通过变窄,而不与浮动有重叠。同样的,当BFC内部有浮动时,为了不影响外部元素的布局,BFC计算高度时会包括浮动的高度。避免margin重叠也是这样的一个道理。

如何触发BFC

  1. 根元素
  2. float属性不为none
  3. position为absolute或fixed
  4. display为inline-block, table-cell, flex
  5. overflow不为visible( hidden,scroll,auto, )

移动端布局

获取宽度

  • document.body.clientWidth为body的宽度,注意body自带margin
  • window.innerWidth为窗口宽度

在JavaScript中我们可以直接通过以下window上的属性获取设备像素比

    window.devicePixelRatio

响应式

利用@media进行断点,在每个断点中编写css

@media (max-width:1000px){
    div{background:blue;}
}
@media (min-width:1000px) and (max-width:1150px){
    div{background: yellow;}
}
@media only screen and (max-width:1150px){
    div{border:solid 1px;}
}

优点:兼容性好,@media在ie9以上是支持的,PC和MOBILE是同一套代码的,不用分开。

缺点:要写得css相对另外两个多很多,而且各个断点都要做好。css样式会稍微大点,更麻烦。

REM

REM这个单位,会根据html的font-size大小进行转换

  • 通过屏幕宽度计算html的字体大小(假设设计稿为750px)
(function (doc, win) {
    var docEl = doc.documentElement,
    resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
    recalc = function () {
      var clientWidth = docEl.clientWidth;
      if (!clientWidth) return;
      docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
    };
   
    if (!doc.addEventListener) return;
       win.addEventListener(resizeEvt, recalc, false);
       doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);

优点:能维持能整体的布局效果,移动端兼容性好,不用写多个css代码,而且还可以利用@media进行优化。

缺点:开头要引入一段js代码,单位都要改成rem(font-size可以用px),计算rem比较麻烦(可以引用预处理器,但是增加了编译过程,相对麻烦了点)。pc和mobile要分开。

设置viewport中的width

  • 这种方案,就是定死viewport中的width大小。

  • 比如设计稿是750的,然后就在代码上写:

    <met name='viewport' content='width=750' />
    

优点:和REM相同,而且不用写rem,直接使用px,更加快捷。

缺点:效果可能没rem的好,图片可能会相对模糊,而且无法使用@media进行断点,不同size的手机上显示,高度间距可能会相差很大。

浮动&清除

1、浮动带来的副作用:

1)块状元素,会钻进浮动元素的下面,被浮动元素所覆盖

������述

2)行内元素,例如文字, 则会环绕在浮动元素的周围,为浮动元素留出空间

������述

3)浮动元素的父元素坍缩

������述

2、清除浮动:

������述

//解决浮动导致的高度塌陷问题
.clearfix:after{
     content:".";        
     display:block;        
     height:0;        
     clear:both;        
     visibility:hidden;        
}

为兼容IE6,IE7,因为ie6,ie7不能用after伪类。加上下面代码

.clearfix{zoom:1}

rem&em/VH&VW

em 是一种相对单位,它相对于父元素的字体大小。

rem是一种相对单位,它相对于根元素 html 的字体大小,会根据html的font-size大小进行转换

vh/vw都是相对于视口的单位,浏览器视口的区域就是通过 window.innerWidth以及 window.innerHeigth 度量得到的范围。 浏览器会将整个视口的宽度或者高度划分为100等份,因此1vw或者1wh就是相对视口宽度或者高度的1%

margin重叠

  • margin在垂直方向上相邻的值相同时会发生叠加,水平方向上相邻的值会相加

  • 只有垂直方向的 margin 才会折叠,也就是说,水平方向的 margin 不会发生折叠的现象。

    如何使元素上下margin不折叠呢?

    触发底部元素的BFC(overflow:hidden除外)

    • 底部元素设为position:absolute
    • 统一设置上或下的margin
    • 底部元素设为float:left

    如何使父子margin不折叠呢?

    触发父元素的BFC

    • 外层用padding代替
    • 内层position:absolute
    • 外层overflow:hidden
    • 内层float:left

inline-block导致的横向间隔

display设置为inline-block时,li与li之间有看不见的空白间隔

造成「inline-block」元素空隙的本质是 HTML 中存在的空白符(whitespace)

行框的排列会受到中间空白(回车空格等等)的影响,这些空白也会被应用样式,占据空间,所以会有间隔

**解决1:**设置ul的font-size为0,缺陷是必须重新在li中去设置字体大小(副作用多)

解决2:使用如下写法

<ul
><li>1</li
><li>2</li
><li>3</li
></ul>

回流、重绘

  • render tree中的一部分因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow) 。
  • 当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。

引起回流

1、添加或者删除可见的DOM元素;

2、元素位置改变;

3、元素尺寸改变——边距、填充、边框、宽度和高度

4、内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变;

5、页面渲染初始化;

6、浏览器窗口尺寸改变——resize事件发生时;

JavaScript

JS的数据类型&判断

string number boolean undefined null object symbol

typeof

// Numbers
typeof 37 === 'number';
typeof 3.14 === 'number';
typeof Math.LN2 === 'number';
typeof Infinity === 'number';
typeof NaN === 'number'; // 尽管NaN是"Not-A-Number"的缩写
typeof Number(1) === 'number'; // 但不要使用这种形式!

// Strings
typeof "" === 'string';
typeof "bla" === 'string';
typeof (typeof 1) === 'string'; // typeof总是返回一个字符串
typeof String("abc") === 'string'; // 但不要使用这种形式!

// Booleans
typeof true === 'boolean';
typeof false === 'boolean';
typeof Boolean(true) === 'boolean'; // 但不要使用这种形式!

// Symbols
typeof Symbol() === 'symbol';
typeof Symbol('foo') === 'symbol';
typeof Symbol.iterator === 'symbol';

// Undefined
typeof undefined === 'undefined';
typeof declaredButUndefinedVariable === 'undefined';
typeof undeclaredVariable === 'undefined'; 

// Objects
typeof {a:1} === 'object';

// 使用Array.isArray 或者 Object.prototype.toString.call
// 区分数组,普通对象
typeof [1, 2, 4] === 'object';

typeof new Date() === 'object';

// 下面的容易令人迷惑,不要使用!
typeof new Boolean(true) === 'object';
typeof new Number(1) === 'object';
typeof new String("abc") === 'object';

// 函数
typeof function(){} === 'function';
typeof class C{} === 'function'
typeof Math.sin === 'function';
typeof new Function() === 'function';

instanceof

instanceof运算符用来检测 constructor.prototype是否存在于参数 object 的原型链上。

function Cat(){}
Cat.prototype = {}

function Dog(){}
Dog.prototype ={}

var dog1 = new Dog();
alert(dog1 instanceof Dog);//true
alert(dog1 instanceof Object);//true

Dog.prototype = Cat.prototype;
alert(dog1 instanceof Dog);//false
alert(dog1 instanceof Cat);//false
alert(dog1 instanceof Object);//true;

var  dog2= new Dog();
alert(dog2 instanceof Dog);//true
alert(dog2 instanceof Cat);//true
alert(dog2 instanceof Object);//true

Dog.prototype = null;
var dog3 = new Dog();
alert(dog3 instanceof Cat);//false
alert(dog3 instanceof Object);//true
alert(dog3 instanceof Dog);//error

prototype

Object.prototype.toString.call(a) === '[object String]')
console.log(Object.prototype.toString.call(123))    //"[object Number]"
console.log(Object.prototype.toString.call('123'))    //"[object String]"
console.log(Object.prototype.toString.call(undefined))    //"[object Undefined]"
console.log(Object.prototype.toString.call(true))    //"[object Boolean]"
console.log(Object.prototype.toString.call(null))    //"[object Null]"
console.log(Object.prototype.toString.call({}))    //"[object Object]"
console.log(Object.prototype.toString.call([]))    //"[object Array]"
console.log(Object.prototype.toString.call(function(){}))    //"[object Function]"

constructor

注意: constructor 在类继承时会出错

c.constructor === Array

如何获得当前日期

var today=new Date()

var y=today.getFullYear()
var m=today.getMonth()
var d=today.getDate()
var h=today.getHours()
var m=today.getMinutes()
var s=today.getSeconds()

声明前置

变量提升

  • 使用var关键字声明或初始化的变量,会将声明语句“提升”到当前作用域的顶部。 只有声明才会触发提升,赋值语句(如果有的话)将保持原样。
  • 用 var 声明得到提升,用 let/const 声明不会提升。

函数提升

  • 函数声明会使函数体提升,函数可以在任意地方调用,函数声明和他的赋值都会被提前。
  • 函数表达式在使用前必须赋值,函数表达式定义的函数则只能在声明之后调用。函数声明会被提前。

function functionName(){} //函数声明

var a = function(arg0,arg1){} //函数表达式

操作数组

Array对象方法

length 数组中元素的数目

POP 删除最后一项

删除最后一项,并返回删除元素的值;如果数组为空则返回undefine

shift 删除第一项

删除原数组第一项,并返回删除元素的值;如果数组为空则返回undefine

push 增加到最后

并返回新数组长度;

unshift增加到最前

并返回新数组长度;

reverse 数组翻转

并返回翻转后的原数组,原数组翻转了

join数组转成字符串

并返回字符串,原数组木变

var a = [1,2,3,4,5]; 
var b=a.join('||');//b:"1||2||3||4||5" a:[1,2,3,4,5]

indexOf数组元素索引

并返回元素索引,不存在返回-1,索引从0开始

var a = ['a','b','c','d','e']; 
a.indexOf('a');//0

slice截取(切片)数组 得到截取的数组

返回从原数组中指定开始索引(包含)到结束索引(不包含)之间的项组成的新数组,原数组木变 ,索引从0开始

var a = ['a','b','c','d','e']; 
a.slice(1,3);//["b", "c"] a:['a','b','c','d','e']

splice剪接数组 原数组变化 可以实现shift前删除,pop后删除,unshift前增加,同push后增加一样的效果

返回剪接的元素数组,原数组变化 ,索引从0开始

var a = ['a','b','c','d','e'];
a.splice(0,0,88,99)//返回 [] 从第一个元素,截取长度0个 肯定是空 a:[88, 99, "a", "b", "c", "d", "e"] 同unshift前增加

concat数组合并

返回合并后的新数组,原数组不变

var a = ['a','b','c','d','e']; 
a.concat([88,99]);//["a", "b", "c", "d", "e", 88, 99]

filter 过滤

var arr = [
    {"name":"apple", "count": 2},
    {"name":"orange", "count": 5},
    {"name":"pear", "count": 3},
    {"name":"orange", "count": 16},
];
var newArr = arr.filter(function(item){
    return item.name === "orange";
});

console.log("Filter results:",newArr);

reduce() 可以实现一个累加器的功能,将数组的每个值(从左到右)将其降低到一个值。

//统计一个数组中有多少个不重复的单词
var arr = ["apple","orange","apple","orange","pear","orange"];
function getWordCnt(){
    return arr.reduce(function(prev,next){
        prev[next] = (prev[next] + 1) || 1;
        return prev;
    },{});
}
console.log(getWordCnt());
//reduce(callback, initialValue)会传入两个变量。回调函数(callback)和初始值(initialValue)

**find()**方法返回数组中满足提供的测试函数的第一个元素的值。否则返回undefined

var inventory = [
  {name: 'apples', quantity: 2},
  {name: 'bananas', quantity: 0},
  {name: 'cherries', quantity: 5}
];
function findCherries(fruit) { 
  return fruit.name === 'cherries';
}
console.log(inventory.find(findCherries)); // { name: 'cherries', quantity: 5 }

操作字符串

String 对象方法

length 字符串的长度

charAt() 返回在指定位置的字符。

concat() 连接字符串。

indexOf() 检索字符串。

lastIndexOf() 从后向前搜索字符串。

match() 找到一个或多个正则表达式的匹配。

replace() 替换与正则表达式匹配的子串。

search() 检索与正则表达式相匹配的值。

slice() 提取字符串的片断,并在新的字符串中返回被提取的部分。

var str="Hello happy world!"
document.write(str.slice(6,11)) //happy

split() 把字符串分割为字符串数组。

substring() 提取字符串中两个指定的索引号之间的字符。

var str="Hello world!"
document.write(str.substring(3,7))// lo w

toLowerCase() 把字符串转换为小写。

toUpperCase() 把字符串转换为大写。

toString() 返回字符串。

  • str.charAt()和str[]的区别
var s = "abc";
s[1];        // b
s.charAt(1); // b
s[5];        // undefined
s.charAt(5); // ""

遍历数组和对象

遍历对象

for in

主要用于遍历对象的可枚举属性,包括自有属性、继承自原型的属性

var obj = {"name":"Poly", "career":"it"}
Object.defineProperty(obj, "age", {value:"forever 18", enumerable:false});
Object.prototype.protoPer1 = function(){console.log("proto");};
Object.prototype.protoPer2 = 2;
console.log("For In : ");
for(var a in obj) console.log(a);

输出: name,carrer,protoPer1,protoPer2

Object.keys

返回一个数组,元素均为对象自有的可枚举属性

var obj = {"name":"Poly", "career":"it"}
Object.defineProperty(obj, "age", {value:"forever 18", enumerable:false});
Object.prototype.protoPer1 = function(){console.log("proto");};
Object.prototype.protoPer2 = 2;
console.log("Object.keys:")
console.log(Object.keys(obj));

输出:name,career

Object.getOwnProperty

用于返回对象的自有属性,包括可枚举和不可枚举的

var obj = {"name":"Poly", "career":"it"}
Object.defineProperty(obj, "age", {value:"forever 18", enumerable:false});
Object.prototype.protoPer1 = function(){console.log("proto");};
Object.prototype.protoPer2 = 2;
console.log("Object.getOwnPropertyNames: ");
console.log(Object.getOwnPropertyNames(obj));

输出:name,career,age

遍历数组

forEach

var arr=[1,2,3,4];
arr.forEach(function(val, index) {
console.log(val, index);
});

for in

var arr=["张三","李四","王五","赵六"];
for (var i in arr){
console.log(i,":",arr[i]);

Promise

概念

Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

特点

  • 无法取Promise一旦新建它就会立即执行,无法中途取消。
  • 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  • 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

用法

基本用法:

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

then:

Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。

catch:

Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。

all:

Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.all([p1, p2, p3]);
  1. 只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。
  2. 只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

race:

Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.race([p1, p2, p3]);

上面代码中,只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

扩展运算符...

扩展运算符( spread )是三个点(...)。将一个数组转为用逗号分隔的参数序列。

  1. console.log(...[1, 2, 3])  
    // 1 2 3 
    

async/await?

  1. async 函数算是一个语法糖,使异步函数、回调函数在语法上看上去更像同步函数
  2. async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
  3. 当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
//指定 50 毫秒以后,输出hello world。
function timeout(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
}

asyncPrint('hello world', 50);

AJAX

let xhr = new XMLHttpRequest()
xhr.open('GET','/xxxx')
xhr.onreadystatechange = function(){
  if(xhr.readyState === 4 && xhr.status === 200){
      console.log(xhr.responseText)
  }
}
xhr.send('a=1&b=2')
POST:
//创建异步对象  
var xhr = new XMLHttpRequest();
//设置请求的类型及url
//post请求一定要添加请求头才行不然会报错
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
 xhr.open('post', '02.post.php' );
//发送请求
xhr.send('name=fox&age=18');
xhr.onreadystatechange = function () {
    // 这步为判断服务器是否正确响应
  if (xhr.readyState == 4 && xhr.status == 200) {
    console.log(xhr.responseText);
  } 
};
  • 优点
    • 页面无刷新
    • 异步与服务器通信
  • 缺点
    • 无back功能
    • 安全性,收到跨站脚本攻击等
    • SEO支持弱

事件循环&异步

JavaScript的单线程

JavaScript从诞生起就是单线程。原因大概是不想让浏览器变得太复杂,因为多线程需要共享资源、且有可能修改彼此的运行结果,对于一种网页脚本语言来说,这就太复杂了。后来就约定俗成,JavaScript为一种单线程语言。(Web Worker API可以实现多线程,但是JavaScript本身始终是单线程的。)

event loop事件循环

概念

在程序中设置两个线程:一个负责程序本身的运行,称为"主线程";另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为"Event Loop线程"

异步执行步骤

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

(4)主线程不断重复上面的第三步。

JS的异步编程方法

  • callback回调函数
function fn1 () {
  console.log('Function 1')
}

function fn2 () {
  setTimeout(() => {
    console.log('Function 2')
  }, 500)
}

function fn3 () {
  console.log('Function 3')
}
//执行顺序 :F1 > F3 > F2
function fn2 (f) {
  setTimeout(() => {
    console.log('Function 2')
    f()
  }, 500)
}

fn2(fn3)
//可以通过callback使fn3在fn2后执行,达到F1 > F2 > F3的目的
  • 事件发布/订阅
  • Promise
function fn1 () {
  console.log('Function 1')
}

function fn2 () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('Function 2')
      resolve()
    }, 500)
  })
}

function fn3 () {
  console.log('Function 3')
}
fn1()
fn2().then(() => { fn3() })

// output : Function 1 > Function 2 > Function 3
  • async&await
function fn1 () {
  console.log('Function 1')
}

function fn2 () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('Function 2')
      resolve()
    }, 500)
  })
}

function fn3 () {
  console.log('Function 3')
}

async function asyncFunArr () {
  fn1()
  await fn2()
  fn3()
}

asyncFunArr()

// output : Function 1 > Function 2 > Function 3

堆和栈?

  • 栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。里面存放的是基本类型的值和引用类型的地址 .
  • 堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。 里面存放引用类型的值。

img

闭包

作用域

原则

ES5中没有块级作用域,只有函数作用域和全局作用域

每次定义一个函数,都会产生一个作用域链(scope chain)。当JavaScript寻找变量varible时(这个过程称为变量解析),总会优先在当前作用域链的第一个对象中查找属性varible ,如果找到,则直接使用这个属性;否则,继续查找下一个对象的是否存在这个属性。

闭包

概念

一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。 

意义

局部变量无法共享和长久的保存,而全局变量可能造成变量污染,所以我们希望有一种机制既可以长久的保存变量又不会造成全局污染。

特点

占用更多内存,不容易被释放

使用
function createAdder(){
  var n=0
  return function(){
  n += 1
  console.log(n)
  }
}
let adder = createAdder()
adder() //1
adder() //2
console.log(n) // n is not undefined

new操作符的执行步骤

  1. 创建一个新对象;
  2. 将函数的作用域赋给新对象(this指向新对象);
  3. 执行函数中的代码;
  4. 返回新对象

this

  • 没有对象只有方法则指向全局对象

    fn()的this是window

  • 有对象调用方法就指向调用对象

    a.fn()的this是a

  • new F()的this是新实例

  • 箭头函数里的this是外部作用域的this

  • 当我们使用add.call(),第一个参数是this需要绑定的对象,剩下的是add函数本来的参数

立即执行函数

(function(){});
(function(){})()

立即执行函数是为了生成一个函数作用域,防止污染全局变量

浅拷贝VS深拷贝

  • 浅复制只会将对象的各个属性进行依次复制,并不会进行递归复制,而 JavaScript 存储对象都是存地址的,所以浅复制会导致 obj.arr 和 shallowObj.arr 指向同一块内存地址,并没有开辟新的栈 .
  • 浅复制,只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用
  • 深复制则不同,它不仅将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上

浅拷贝

function copy(obj1) {
    var obj2 = {};
    for (var i in obj1) {
      obj2[i] = obj1[i];
    }
    return obj2;
}

深拷贝

  • 用JSON深拷贝
  var a = {...}
  var b = JSON.parse(JSON.stringify(a))
  • 递归拷贝
var china = {
	  	nation : '中国',
	  	birthplaces:['北京','上海','广州'],
	  	skincolr :'yellow',
	  	friends:['sk','ls']
	  }
	  //深复制,要想达到深复制就需要用递归
	  function deepCopy(o,c){
	    var c = c || {}
	    for(var i in o){
	    	if(typeof o[i] === 'object'){
	  	   	   	  //要考虑深复制问题了
                      if(o[i].constructor === Array){
                    	//这是数组
                    	c[i] =[]
                    }else{
                    	//这是对象
                    	c[i] = {}
                    }
                     //递归进入对象或数组中进行下一轮深层拷贝
                    deepCopy(o[i],c[i])
	  	   	   }else{
	  	   	   	 c[i] = o[i]
	  	   	   }
	  	   }
	  	   return c
	  }
	  var result = {name:'result'}
	  result = deepCopy(china,result)

原型

array.push(num)中push是沿着array.__proto__找到的,也就是Array.prototype.push

原型图

�������

img

判断变量Array类型&判断变量是Number

  • 数组:
  1. arr instanceof Array); // true instanceof 运算符用来测试一个对象是否是后者的实例。
  2. arr.constructor === Array ; // true
  3. Array.isArray([1, 2, 3]); // true
  4. Object.prototype.toString.call(arr);   //'[object Array]' 此种方法最准确
  • Number:

    isNaN 如果为true则不是Number类型

立即执行函数

  1. var foo = function() {}

    编译后变量声明foo会“被提前”了,但是他的赋值(也就是FUNCTION_BODY)并不会被提前。

    也就是,匿名函数只有在被调用时才被初始化。

  2. function foo() {}

    编译后函数声明和他的赋值都会被提前。

    也就是说函数声明过程在整个程序执行之前的预处理就完成了,所以只要处于同一个作用域,就可以访问到,即使在定义之前调用它也可以。

匿名函数

1、匿名函数的事件不能解绑

2、匿名函数和普通函数最大的区别是在于,匿名函数可以作为一个具体的“值”赋予给变量或者对象属性,其次,由于匿名函数可以被定义在不同地方,使得他可以有效利用他所在的局域内的变量(或者说上下文中的变量)。

JS获得用户来源

navigator.userAgent.indexOf('')

ES6的class

生成实例对象的传统方法是通过构造函数 ,

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
};

var p = new Point(1, 2);

上面的代码用 ES6 的class改写

//定义类
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

继承

function A(name){  this.name=name; }
A.prototype.sayName=function(){ console.log(this.name); }
function B(age){ this.age=age; }

原型链继承

B.prototype=new A("mbj");  //被B的实例共享
var foo=new B(18);
foo.age;    //18,age是本身携带的属性
foo.name;   //mbj,等价于foo.__proto__.name
foo.sayName(); //mbj,等价于foo.__proto__.proto__.sayName()
foo.toString();  //"[object Object]",等价于foo.__proto__.__proto__.__proto__.toString();
  • 所有子类共享父类实例,如果某一个子类修改了父类,其他的子类在继承的时候,会造成意想不到的后果。
  • 在构造子类实例的时候,不能给父类传递参数。

构造函数继承

function B(age,name){  
    this.age=age;
    A.call(this,name); 
}
var foo=new B(18,"wmy");
foo.name;     //wmy
foo.age;      //18
foo.sayName();   //undefined
  • 父类的prototype中的函数不能复用

原型链+构造函数继承

function B(age,name){  this.age=age;A.call(this,name); }
B.prototype=new A("mbj");
var foo=new B(18,"wmy");
foo.name;     //wmy
foo.age;      //18
foo.sayName();   //wmy

extends继承

class Point {
}
class ColorPoint extends Point {
}

reduce/filter/find

  • filter
var arr = [
    {"name":"apple", "count": 2},
    {"name":"orange", "count": 5},
    {"name":"pear", "count": 3},
    {"name":"orange", "count": 16},
];
var newArr = arr.filter(function(item){
    return item.name === "orange";
});
 
console.log("Filter results:",newArr);
  • **reduce()**可以实现一个累加器的功能,将数组的每个值(从左到右)将其降低到一个值。
  //统计一个数组中有多少个不重复的单词
  var arr = ["apple","orange","apple","orange","pear","orange"];
  function getWordCnt(){
      return arr.reduce(function(prev,next){
          prev[next] = (prev[next] + 1) || 1;
          return prev;
      },{});
  }
  console.log(getWordCnt());
  //reduce(callback, initialValue)会传入两个变量。回调函数(callback)和初始值(initialValue)
  • find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined
var inventory = [
    {name: 'apples', quantity: 2},
    {name: 'bananas', quantity: 0},
    {name: 'cherries', quantity: 5}
];

function findCherries(fruit) { 
    return fruit.name === 'cherries';
}

console.log(inventory.find(findCherries)); // { name: 'cherries', quantity: 5 }

事件代理

window.onload = function(){
  var oUl = document.getElementById("ul1");
  oUl.onclick = function(ev){
    var ev = ev || window.event;
    var target = ev.target || ev.srcElement;
    if(target.nodeName.toLowerCase() == 'li'){
        	alert(123);
         alert(target.innerHTML);
    }
  }
}
window.onload = function(){
            var oBox = document.getElementById("box");
            oBox.onclick = function (event) {
                var event = event || window.event;
                var target = event.target || event.srcElement;
                if(target.nodeName.toLowerCase() == 'input'){
                    switch(target.id){
                        case 'add' :
                            alert('添加');
                            break;
                        case 'remove' :
                            alert('删除');
                            break;
                        case 'move' :
                            alert('移动');
                            break;
                        case 'select' :
                            alert('选择');
                            break;
                    }
                }
            }       
        }

请说明.forEach循环和.map()循环的主要区别,它们分别在什么情况下使用?

  • 相同点:

1)都是循环遍历数组中的每一项

2)forEach和map方法里每次执行匿名函数都支持3个参数,参数分别是item(当前每一项)、index(索引值)、arr(原数组)

3)匿名函数中的this都是指向window

4)只能遍历数组

  • 区别:

区别在于map有返回值,而forEach没有返回值。

map方法返回一个新的数组

forEach()方法用于调用数组的每个元素,将元素传给回调函数。

性能上map稍落后于forEach

img

DOM

DOM事件流

  • 捕获
  • 冒泡

通过addEventListener((type, listener, useCapture)的useCapture来设定,useCapture=false代表着事件冒泡,useCapture=true代表着采用事件捕获。

事件模型?

事件绑定模型

  • DOM 0级事件
<button onclick="func()">内联模型绑定</button>

同一个节点只能添加一次同类型事件,如果添加多次,最后一个生效。

通过DOM0绑定的事件,一旦绑定将无法取消。

  • DOM 2级事件
.addEventListener("click",函数,true/false);

同一个节点,可以使用DOM2绑定多个同类型事件。

使用DOM2绑定的事件,可以有专门的函数进行取消。

事件流模型

  • 事件冒泡

触发一个节点的事件,会从当前节点开始,依次触发其祖先节点的同类型事件,直到DOM根节点。

所有事件绑定默认为事件冒泡

阻止冒泡:

 e.stopPropagation();
  • 事件捕获

触发一个节点的事件,会从DOM根节点开始,依次触发其祖先节点的同类型事件,直到当前节点自身。

使用addEventListener绑定事件,第三个参数传为true时表示事件捕获

DOM加载事件

  • document.ready监控dom是否加载完毕,dom加载完毕时及资源加载之前触发
  • window.onload当页面全部加载完成(包括所有资源)
  • DOMContentLoaded: 在形成完整的DOM树之后就会触发,而不理会图像、javascript文件、CSS文件或其他资源是否下载完毕

移动端的触摸事件

  • touchstart touchmove touchend touchcancel
  • 模拟swipe事件:记录两次touchmove的位置差

事件委托,如何识别点击的子元素?

监听父元素,看事件触发是哪个子元素

通过event.target识别具体是哪个子元素

<ul id="ul">
     <li id="li-1">1</li>
     <li id="li-2">2</li>
     <li id="li-3">3</li>
     <li id="li-4">4</li>
     <li id="li-5">5</li>
</ul>

document.getElementById("ul").addEventListener("click",function(e) {
     if(e.target && e.target.nodeName == "LI") {
         console.log("List item ",e.target.id.replace("post-")," was clicked!");
     }
})

增删改查

创建新节点

​ createElement() //创建一个具体的元素

添加、移除、替换、插入

​ appendChild()

​ removeChild()

​ replaceChild()

​ insertBefore()

查找

​ getElementsByTagName()

​ getElementsByName()

​ getElementById()

​ getElementByClassName()

HTTP

状态码

1XX信息性状态码(Informational)服务器正在处理请求

2XX成功状态码(Success)请求已正常处理完毕

​ 200 OK 表示请求被服务器正常处理

​ 204 No Content 表示请求已成功处理,但是没有内容返回(就应该没有内容返回的状况)

3XX重定向状态码(Redirection)需要进行额外操作以完成请求

​ 301 Moved Permanently 永久重定向,表示请求的资源已经永久的搬到了其他位置

​ 302 Found 临时重定向,表示请求的资源临时搬到了其他位置

​ 303 See Other 表示请求资源存在另一个URI,应使用GET定向获取请求资源

​ 304 Not Modified 使用缓存

4XX客户端错误状态码(Client Error)客户端原因导致服务器无法处理请求

​ 400 Bad Request 表示请求报文存在语法错误或参数错误,服务器不理解

​ 401 Unauthorized 表示发送的请求需要有HTTP认证信息或者是认证失败了

​ 403 Forbidden 表示对请求资源的访问被服务器拒绝了

​ 404 Not Found 表示服务器找不到你请求的资源

5XX服务器错误状态码(Server Error)服务器原因导致处理请求出错

​ 500 Internal Server Error 表示服务器执行请求的时候出错了

​ 503 Service Unavailable 表示服务器超负载或正停机维护,无法处理请求

301和302的区别

  • 301永久重定向:浏览器会缓存
  • 302临时重定向:浏览器不缓存

HTTP和HTTPS的区别?

  • Hyper Text Transfer Protocol Secure,相比http,多了一个secure
  • HTTP所封装的信息是明文的,通过抓包工具可以分析其信息内容
  • HTTP 缺省工作在TCP协议80端口,HTTPS缺省工作在TCP协议443端口
  • https是HTTP运行在SSL/TLS之上,SSL/TLS运行在TCP之上 ,使用对称加密算法

提高页面性能的方法

  • 缓存静态和动态内容
  • 减少请求数
  • 减少操作DOM
  • CSS文件放在页面顶部,而JS文件放在底部
  •  压缩CSS和JavaScript
  • 启用GZIP压缩

Websocket

意义

HTTP 协议有一个缺陷:通信只能由客户端发起 ,websocket可以实现服务端主动推送。

属性

(1)建立在 TCP 协议之上,服务器端的实现比较容易。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

用法

//客户端与服务器进行连接
var ws = new WebSocket("wss://echo.websocket.org");
//指定连接成功后的回调函数
ws.onopen = function(evt) { 
  console.log("Connection open ..."); 
  ws.send("Hello WebSockets!");
};
//指定收到服务器数据后的回调函数
ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};
//指定连接关闭后的回调函数
ws.onclose = function(evt) {
  console.log("Connection closed.");
};    

缺点

对前端开发者,往往要具备数据驱动使用javascript的能力,且需要维持住ws连接(否则消息无法推送);对后端开发者而言,难度增大了很多,一是长连接需要后端处理业务的代码更稳定(不要随便把进程和框架都crash掉),二是推送消息相对复杂一些,三是成熟的http生态下有大量的组件可以复用,websocket则太新了一点。

缓存

http����

根据是否需要重新向服务器发起请求来分类,可以将其分为两大类(强制缓存,对比缓存),强制缓存如果生效,不需要再和服务器发生交互,而对比缓存不管是否生效,都需要与服务端发生交互。 两类缓存规则可以同时存在,强制缓存优先级高于对比缓存,也就是说,当执行强制缓存的规则时,如果缓存生效,直接使用缓存,不再执行对比缓存规则。
强制缓存

header中会有两个字段来标明失效规则Cache-Control,指的是当前资源的有效期

对比缓存

浏览器第一次请求数据时,服务器会将缓存标识与数据一起返回给客户端,客户端将二者备份至缓存数据库中。 再次请求数据时,客户端将备份的缓存标识发送给服务器,服务器根据缓存标识进行判断,判断成功后,返回304状态码,通知客户端比较成功,可以使用缓存数据。
在对比缓存生效时,状态码为304,并且报文大小和请求时间大大减少。

请求头?响应头?

常见的请求头:
Accept: text/html,image/*                                      #浏览器可以接收的类型
Accept-Charset: ISO-8859-1                                     #浏览器可以接收的编码类型
Accept-Encoding: gzip,compress                                 #浏览器可以接收压缩编码类型
Accept-Language: en-us,zh-cn                                   #浏览器可以接收的语言和国家类型
Host: www.lks.cn:80                                            #浏览器请求的主机和端口
If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT               #某个页面缓存时间
Referer: http://www.lks.cn/index.html                          #请求来自于哪个页面
User-Agent: Mozilla/4.0 compatible; MSIE 5.5; Windows NT 5.0   #浏览器相关信息
Cookie:                                                        #浏览器暂存服务器发送的信息
Connection: close1.0/Keep-Alive1.1                             #HTTP请求的版本的特点
Date: Tue, 11 Jul 2000 18:23:51GMT                             #请求网站的时间
Allow:GET                                                      #请求的方法 GET 常见的还有POST
Keep-Alive:5                                                   #连接的时间;5
Connection:keep-alive                                          #是否是长连接
Cache-Control:max-age=300                                      #缓存的最长时间 300s

Cookie是什么? Session是什么?

特性 Cookie Localstorage sessionstorage
生命周期 一般由服务器生成,可设置失效时间。如果在浏览器端生成Cookie,默认是关闭浏览器后失效。 除非被清除,否则永久保存 仅在当前会话下有效,关闭页面或浏览器后被清除
数据大小 4k 5MB
与服务器端通信 每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题 仅在客户端(即浏览器)中保存,不参与和服务器的通信
用途 用于标识用户身份 用于浏览器端缓存数据
易用性 cookie需要自己封装setCookie,getCookie 可以用源生接口,也可再次封装来对Object和Array有更好的支持

cookie

  • HTTP响应通过Set-cookie设置Cookie
  • 浏览器访问指定域名时必须带上cookie作为request header
  • cookie一般用来记录用户信息

session

  • session是服务器端的内存
  • session一般通过在cookie里记录sessionID实现
  • sessionID一般是随机数

localstorage和cookie区别

  • cookie会随请求发送到服务器上,localstorage不会
  • cookie一般大小4k,localstorage一般是5M

cookie和session如何实现前后端相连?如何实现?

浏览器端如何生成cookie?

document.cookie = "name=Jonh; ";

文件上传的解决方案?

文件+文字同时上传

$("#upload-newtab-button").click(function(){
	    	if(($("#uploadname").val())&&($("#uploadsinger").val())&&($("#uploadfile").val())){
			    var data = new FormData();
			    var fileobj = document.getElementById('uploadfile').files[0];

			    data.append("name",$("#uploadname").val());
			    data.append("singer",$("#uploadsinger").val());
			    data.append("file",fileobj);

		        $.ajax({
		            url: 'php/upload.php',
		            type: 'POST',
		            data: data,
		            dataType: 'text',
		            cache: false,
		            processData: false,
		            contentType: false,
		            success: function(feedbackdata){
		            	$("#uploadresulutinfo").html(feedbackdata);
		            }
		        })
		    }else{
		    	$("#uploadresulutinfo").html('请填写完整信息并上传指定格式的乐谱')
		    }
	    });

GET和POST的区别

  1. GET用来读数据,POST用来写数据

  2. GET的参数有长度限制,一般是1024个字符,POST的参数没有长度限制,一般是4-10Mb

  3. 包:GET请求只需发一个包,POST请求需要发两个以上包(因为POST有消息体)

  4. GET的参数放在URL的查询参数里,POST的参数放在请求消息体(数据)里

    • GET 请求可被缓存
    • GET 请求保留在浏览器历史记录中
    • GET 请求可被收藏为书签
    • GET 请求不应在处理敏感数据时使用
    • GET 请求有长度限制
    • GET 请求只应当用于取回数据
    • POST 请求不会被缓存
    • POST 请求不会保留在浏览器历史记录中
    • POST 不能被收藏为书签
    • POST 请求对数据长度没有要求

浏览器同源策略

意义

同源策略主要用来防止CSRF攻击

  1. 用户登录了自己的银行页面 http://mybank.comhttp://mybank.com向用户的cookie中添加用户标识。
  2. 用户浏览了恶意页面 http://evil.com。执行了页面中的恶意AJAX请求代码。
  3. http://evil.comhttp://mybank.com发起AJAX HTTP请求,请求会默认把http://mybank.com对应cookie也同时发送过去。
  4. 银行页面从发送的cookie中提取用户标识,验证用户无误,response中返回请求数据。此时数据就泄露了。而且由于Ajax在后台执行,用户无法感知这一过程。

属性

如果非同源,共有三种行为受到限制。

(1) Cookie、LocalStorage 和 IndexDB 无法读取。

(2) DOM 无法获得。

(3) AJAX 请求不能发送。

跨域

  • js里发送ajax请求,如果请求的URL和当前的URL非同域,浏览器拒接提供接受的收据并报错。
  • 解决办法:JSONP、CORS、代理服务器、POSTMESSAGE(主用于:页面和其打开的新窗口的数据传递)
  • 相比JSONP只能发GET请求,CORS允许任何类型的请求。

代理服务器

例如www.123.com/index.html需要调用www.456.com/server.php,可以写一个接口www.123.com/server.php,由这个接口在后端去调用www.456.com/server.php并拿到返回值,然后再返回给index.html,这就是一个代理的模式。相当于绕过了浏览器端,自然就不存在跨域问题。

JSONP

实现

它的基本思想是,网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。

function addScriptTag(src) {
  var script = document.createElement('script');
  script.setAttribute("type","text/javascript");
  script.src = src;
  document.body.appendChild(script);
}

window.onload = function () {
  addScriptTag('http://example.com/ip?callback=foo');
}

function foo(data) {
  console.log('Your public IP address is: ' + data.ip);
};
//服务器返回
foo({
  "ip": "8.8.8.8"
});
缺点

JSONP只能发GET请求

CORS

意义

允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制

性质
  • 浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
  • 简单请求请求方法是以下三种方法之一:HEAD/GET/POST
  • 整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息。
使用

在头信息之中,增加一个Origin字段 ,用来说明本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求 .

如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequestonerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

//该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求
Access-Control-Allow-Origin: http://api.bob.com
//该字段可选。表示是否允许发送Cookie。
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

如果指定要发cookie,另一方面,开发者必须在AJAX请求中打开withCredentials属性。 否则,即使服务器同意发送Cookie,浏览器也不会发送。

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
缺点
  • 需要控制好允许访问的域名
  • 浏览器兼容性稍弱

http请求过程

从输入URL到页面展示过程

  1. DNS查询

    a. 浏览器缓存,浏览器会缓存DNS记录一段时间。

    b.在hosts静态文件、DNS解析器缓存中查找某主机的ip地址

    c.将前面的查询请求发给路由器,它一般会有自己的DNS缓存。

    d. 每一个ISP(网络服务提供商)会有一个自己的本地域名服务器,他会在url第一次访问时缓存该域名的指向。下次再访问时,他会从缓存里把这个url曾经指向的IP调出来。

    e.ISP的DNS服务器会从根域名开始进行递归查询

  2. 建立TCP连接,三次握手

  3. 发送HTTP请求

  4. 后台处理请求

  5. 发送HTTP相应

  6. 关闭TCP连接,四次挥手

  7. 解析HTML

  8. 下载CSS、并解析

  9. 下载JS、并解析

  10. 下载并解析图片

  11. 渲染DOM

  12. 渲染样式树

  13. 执行JS

VUE

VUE基础

vue的生命周期

Vue共有8个生命周期钩子函数,分别为:

  • beforeCreate el 和 data 并未初始化均为undefined
  • created 完成了 data 数据的初始化,el没有初始化
  • beforeMount 完成了el初始化,但data并没有挂载完成
  • mounted 完成了挂载
  • beforeUpdate 数据更新之前
  • updated 数据更新之前
  • beforeDestroy 实例销毁之前,在这一步,实例仍然完全可用
  • destroyed Vue 实例销毁后,Vue 实例指示的所有东西都会解绑定

如何组件通信?

父组件向子组件通信

使用props,父组件可以使用props向子组件传递数据。

//父组件
<template>
    <child :msg="message"></child>
</template>

<script>

import child from './child.vue';

export default {
    components: {
        child
    },
    data () {
        return {
            message: 'father message';
        }
    }
}
</script>
//子组件
<template>
    <div>{{msg}}</div>
</template>

<script>
export default {
    props: {
        msg: {
            type: String,
            required: true
        }
    }
}
</script>

子组件向父组件通信

使用vue事件,父组件向子组件传递事件方法,子组件通过$emit触发事件,回调给父组件。

//父组件
<template>
    <child @msgFunc="func"></child>
</template>

<script>

import child from './child.vue';

export default {
    components: {
        child
    },
    methods: {
        func (msg) {
            console.log(msg);
        }
    }
}
</script>
//子组件
<template>
    <button @click="handleClick">点我</button>
</template>

<script>
export default {
    props: {
        msg: {
            type: String,
            required: true
        }
    },
    methods () {
        handleClick () {
            //........
            this.$emit('msgFunc');
        }
    }
}
</script>

非父子组件、兄弟组件之间的数据传递

非父子组件通信,Vue官方推荐使用一个Vue实例作为中央事件总线。 $on方法用来监听一个事件。

$emit用来触发一个事件。

/*新建一个Vue实例作为中央事件总嫌*/
let event = new Vue();

/*监听事件*/
event.$on('eventName', (val) => {
    //......do something
});

/*触发事件*/
event.$emit('eventName', 'this is a message.');

VUE项目配置文件了解么?

├── build --------------------------------- webpack相关配置文件
│   ├── build.js --------------------------webpack打包配置文件
│   ├── check-versions.js ------------------------------ 检查npm,nodejs版本
│   ├── dev-client.js ---------------------------------- 设置环境
│   ├── dev-server.js ---------------------------------- 创建express服务器,配置中间件,启动可热重载的服务器,用于开发项目
│   ├── utils.js --------------------------------------- 配置资源路径,配置css加载器
│   ├── vue-loader.conf.js ----------------------------- 配置css加载器等
│   ├── webpack.base.conf.js --------------------------- webpack基本配置
│   ├── webpack.dev.conf.js ---------------------------- 用于开发的webpack设置
│   ├── webpack.prod.conf.js --------------------------- 用于打包的webpack设置
├── config ---------------------------------- 配置文件
├── node_modules ---------------------------- 存放依赖的目录
├── src ------------------------------------- 源码
│   ├── assets ------------------------------ 静态文件
│   ├── components -------------------------- 组件 
│   ├── main.js ----------------------------- 主js
│   ├── App.vue ----------------------------- 项目入口组件
│   ├── router ------------------------------ 路由
├── package.json ---------------------------- node配置文件
├── .babelrc--------------------------------- babel配置文件
├── .editorconfig---------------------------- 编辑器配置
├── .gitignore------------------------------- 配置git可忽略的文件

Vue中引入组件的步骤?

1)采用ES6的import ... from ...语法或CommonJS的require()方法引入组件
2)对组件进行注册,代码如下

// 注册
Vue.component('my-component', {
  template: '<div>A custom component!</div>'
})

3)使用组件<my-component></my-component>

computed/method/watch的区别

cpmputed/method

<p>Reversed message: "{{ reversedMessage() }}"</p>

// 在组件中
methods: {
  reversedMessage: function () {
    return this.message.split('').reverse().join('')
  }
}
  • 计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。
  • 使用:原始数据经过处理得到结果。计算属性默认只有 getter 。

watch

watch是监控一个对象,当变化时执行操作

<div id="watch-example">
  <p>
    Ask a yes/no question:
    <input v-model="question">
  </p>
  <p>{{ answer }}</p>
</div>
var watchExampleVM = new Vue({
  el: '#watch-example',
  data: {
    question: '',
    answer: 'answer'
  },
  watch: {
    // 如果 `question` 发生改变,这个函数就会运行
    question: function (newQuestion, oldQuestion) {
      this.answer = 'Waiting for you to stop typing...'
      this.debouncedGetAnswer()
    }
  }
}

V-if和V-show的区别?

  • v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display
  • v-if 是“真正”的条件渲染

v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

vue的事件修饰符

Vue.js 为 v-on 提供了事件修饰符

<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

keep-alive?

组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个 <keep-alive> 元素将其动态组件包裹起来。

<!-- 失活的组件将会被缓存!-->
<keep-alive>
  <component></component>
</keep-alive>

<keep-alive>
  <router-view></router-view>
</keep-alive>

VUE路由

动态路由&动态参数

一个“路径参数”使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用

//HTML
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

<div id="app">
  <p>
    <router-link to="/user/foo">/user/foo</router-link>
    <router-link to="/user/bar">/user/bar</router-link>
  </p>
  <router-view></router-view>
</div>

//JS
const User = {
  template: `<div>User {{ $route.params.id }}</div>`
}

const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User }
  ]
})

const app = new Vue({ router }).$mount('#app')

嵌套路由

//HTML
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
  <p>
    <router-link to="/user/foo">/user/foo</router-link>
    <router-link to="/user/foo/profile">/user/foo/profile</router-link>
    <router-link to="/user/foo/posts">/user/foo/posts</router-link>
  </p>
  <router-view></router-view>
</div>

//JS
const User = {
  template: `
    <div class="user">
      <h2>User {{ $route.params.id }}</h2>
      <router-view></router-view>
    </div>
  `
}
const UserHome = { template: '<div>Home</div>' }
const UserProfile = { template: '<div>Profile</div>' }
const UserPosts = { template: '<div>Posts</div>' }
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User,
      children: [
        { path: '', component: UserHome },
        { path: 'profile', component: UserProfile },

        { path: 'posts', component: UserPosts }
      ]
    }
  ]
})
const app = new Vue({ router }).$mount('#app')

路由传参

点击不同的li进行不同跳转并传参

<li v-for="article in articles" @click="getDescribe(article.id)">

方法一 通过动态路由的:值传参

getDescribe(id) {
//   直接调用$router.push 实现携带参数的跳转
     this.$router.push({
    path: `/describe/${id}`,
	})
}
//路由配置
{
     path: '/describe/:id',
     name: 'Describe',
     component: Describe
}
//取参数
$route.params.id

方法二 通过name确定路由,通过params来传递参数

this.$router.push({
          name: 'Describe',
          params: {
            id: id
          }
})
//路由配置
{
     path: '/describe',
     name: 'Describe',
     component: Describe
}
//取参数
$route.params.id

方法三 使用query

this.$router.push({
          path: '/describe',
          query: {
            id: id
          }
})
//路由配置
{
     path: '/describe',
     name: 'Describe',
     component: Describe
}
//取参数
$route.query.id

编程式的导航路由

// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: 123 }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})

导航守卫

全局路由钩子:

router.beforeEach((to, from, next) => {
    //会在任意路由跳转前执行,next一定要记着执行,不然路由不能跳转了
  console.log('beforeEach')
  console.log(to,from)
  //
  next()
})
//
router.afterEach((to, from) => {
    //会在任意路由跳转后执行
  console.log('afterEach')
})

单个路由钩子:
只有beforeEnter,在进入前执行,to参数就是当前路由

 routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]

路由组件钩子:

  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }

VUEX

State

从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态

//每当 store.state.count 变化的时候, 都会重新求取计算属性,并且触发更新相关联的 DOM。
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return store.state.count
    }
  }
}

Getter

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

  • Getter 接受 state 作为其第一个参数:
const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})

Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation

Mutation 必须是同步函数

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})
//通过commit来调用mutation中的方法
store.commit('increment')

Action

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})
//Action 通过 store.dispatch 方法触发:
store.dispatch('increment')

Module

Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter

双向绑定原理

原理简述

vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,触发相应的监听回调。

实现流程

要实现mvvm的双向绑定,就必须要实现以下几点:

  • 实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者

那么将需要observe的数据对象进行递归遍历

给这个对象的某个值赋值,就会触发setter ,利用Obeject.defineProperty()来监听属性变动

建立订阅者集合的数组,在setter中写入方法遍历并执行通知方法给所有的订阅者

  • 实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数

���述

  • 实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图

1、在自身实例化时往属性订阅器(dep)里面添加自己

2、自身必须有一个update()方法

3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。

  • mvvm入口函数,整合以上三者

���述

算法

排序算法

冒泡排序

var examplearr=[8,94,15,88,55,76,21,39];
function sortarr(arr){
    for(i=0;i<arr.length-1;i++){
        for(j=0;j<arr.length-1-i;j++){
            if(arr[j]>arr[j+1]){
                var temp=arr[j];
                arr[j]=arr[j+1];
                arr[j+1]=temp;
            }
        }
    }
    return arr;
}
sortarr(examplearr);

快速排序

 function quickSort(arr){
            //如果数组<=1,则直接返回
            if(arr.length<=1){return arr;}
            var pivotIndex=Math.floor(arr.length/2);
            //找基准,并把基准从原数组删除
            var pivot=arr.splice(pivotIndex,1)[0];
            //定义左右数组
            var left=[];
            var right=[];
            //比基准小的放在left,比基准大的放在right
            for(var i=0;i<arr.length;i++){
                if(arr[i]<=pivot){
                    left.push(arr[i]);
                }
                else{
                    right.push(arr[i]);
                }
            }
            //递归
            return 				  quickSort(left).concat([pivot],quickSort(right));
        }   

二分查找法

返回-1或存在的数组下标。

//二分查找,递归实现。
function binarySearch(target,arr) {
    var start= 0;
    var end=arr.length-1;

    var mid = parseInt(start+(end-start)/2);
    if(target==arr[mid]){
        return mid;
    }else if(target>arr[mid]){
        return binarySearch(target,arr,mid+1,end);
    }else{
        return binarySearch(target,arr,start,mid-1);
    }
    return -1;
}
//不使用递归实现
function binarySearch(target,arr) {
    var start   = 0;
    var end     = arr.length-1;

    while (start<=end){
        var mid = parseInt(start+(end-start)/2);
        if(target==arr[mid]){
            return mid;
        }else if(target>arr[mid]){
            start= mid+1;
        }else{
            end= mid-1;
        }
    }
    return -1;
}

数组去重

var arr = ['abc','abcd','sss','2','d','t','2','ss','f','22','d'];
var s = [];
for(var i = 0;i<arr.length;i++){
    if(s.indexOf(arr[i]) == -1){  //判断在s数组中是否存在,不存在则push到s数组中
        s.push(arr[i]);
    }
}
console.log(s);
  • Set去重
var arr = [1,2,2,3,4] // 需要去重的数组

var set = new Set(arr) // {1,2,3,4}
var newArr = Array.from(set) // 再把set转变成array

console.log(newArr) // [1,2,3,4]

千位分隔符

function format(num){  
 num=num+'';//数字转字符串  
  var str="";//字符串累加  
  for(var i=num.length- 1,j=1;i>=0;i--,j++){  
      if(j%3==0 && i!=0){//每隔三位加逗号,过滤正好在第一个数字的情况  
          str+=num[i]+",";//加千分位逗号  
          continue;  
      }  
      str+=num[i];//倒着累加数字  
  }  
  return str.split('').reverse().join("");//字符串=>数组=>反转=>字符串  
}  

各字符出现次数

const arr="abcdaabc";
let counter = {};
for (let i = 0, len = arr.length; i < len; i++ ) {
    counter[arr[i]] ? counter[arr[i]]++ : counter[arr[i]] = 1;
}
console.log(counter);

找出正整数组的最大差值

function getMaxPro(arr){
    var minPrice=arr[0];
    var maxProfit=0;
    for (var i=0;i<arr.length;i++){
       var currentPrice=arr[i];
       minPrice=Math.min(minPrice,currentPrice);
      var potentialProfit =currenrPrice-minPrice;
       maxProfit=Math.max(maxProfit,potentialProfit);
     }
     return maxProfit;  
   }

随机生成指定长度的字符串

function random(n){
   let str='abcdefghijkmnopqrstuvwxyz9876543210';
   let tmp='',
     i=0,
     l=str.length;

  for(i=0;i<n;i++){
    tmp +=str.charAt(Math.floor(Math.random()*l))
    }
    return tmp;
  }

翻转字符串

function reverseString(str){
  var tmp = '';
  for(var i=str.length-1; i>=0; i--)
    tmp += str[i];
  return tmp
}
function reverseString(str){
  var arr = str.split("");
  var i = 0,j = arr.length-1;
  while(i<j){
    tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
    i++;
    j--;
  }
  return arr.join("");
}

阶乘

//非递归
function factorialize(num) {
  var result = 1;
    if(num < 0) return -1;
    if(num == 0 || num == 1) return 1;
    while(num>1) {
      result *= num--;
    }
    return result;
}
//递归
function factorialize(num) {
  var result = 1;
  if(num < 0) return -1;
  if(num == 0 || num == 1) return 1;
  if(num > 1) return num*factorialize(num-1);
}

统计字符串次数最多的字符

function findMaxDuplicateChar(str) {
  if(str.length == 1) {
    return str;
  }
  var charObj = {};
  for(var i = 0; i < str.length; i++) {
    if(!charObj[str.charAt(i)]) {
      charObj[str.charAt(i)] = 1;
    } else {
      charObj[str.charAt(i)] += 1;
    }
  }
  var maxChar = '',
      maxValue = 1;
  for(var k in charObj) {
    if(charObj[k] >= maxValue) {
      maxChar = k;
      maxValue = charObj[k];
    }
  }
  return maxChar + ':' + maxValue;
}

找到句子中最长的单词,并计算它的长度

function findLongestWord(str) {
//转化成数组
 var astr=str.split( " " );
//对数组中每个元素的字符串长度进行比较,按照字符串长度由大至小排列数组顺序。
 var bstr=astr.sort(function(a,b){
   return b.length-a.length;
 });
//取出数组中第一个元素(也就是最大长度的字符串)
 var lenMax= bstr[0].length;
//返回长度值
 return lenMax;
}
findLongestWord("The quick brown foxjumped over the lazy dog");
//结果:6

字符串的每个单词首字母都大写,其余部分小写

function titleCase(str) {
 var astr=str.toLowerCase().split(" ");
 for(var i=0 ; i<astr.length; i++){
   astr[i]=astr[i][0].toUpperCase()+astr[i].substring(1,astr[i].length);
 }
 var string=astr.join(" ");
 return string;
}
titleCase("I'm a little teapot");
//结果:I'm A LittleTea Pot

不利用第三方变量的情况下交换两个变量的值

var a = 10;  
        var b = 12;  
        function swap (a,b) {  
            b = b - a;  
            a = a + b;  
            b = a - b;  
            return [a,b]  
        }  
        console.log(swap(a,b));  

安全

XSS跨域脚本攻击

  • 发生了不在预期内执行的JS代码 ,达到获取本地的部分cookie信息等目的 。例如:网站form表单收集数据的时候,有的用户非法/恶意的把”html/css/js”代码内容给植入到form表单域中

    • 攻击者对含有漏洞的服务器发起XSS攻击(注入JS代码)。
    • 诱使受害者打开受到攻击的服务器URL。
    • 受害者在Web浏览器中打开URL,恶意脚本执行。
  • XSS的分类

存储型XSS、反射型XSS、DOM-XSS

1、存储型XSS

数据库中存有的存在XSS攻击的数据,返回给客户端。若数据未经过任何转义。被浏览器渲染。就可能导致XSS攻击;

2、反射型XSS

将用户输入的存在XSS攻击的数据,发送给后台,后台并未对数据进行存储,也未经过任何过滤,直接返回给客户端。被浏览器渲染。就可能导致XSS攻击;

3、DOM-XSS

纯粹发生在客户端的XSS攻击

预防:从输入到输出都需要过滤、转义。

输入

  1. 在产品形态上,针对不同输入类型,对输入做变量类型限制。 如,http://xss.qq.com?default=12,Default值强制限制为整形。
  2. 字符串类型的数据,需要针对<、>、/、’、”、&五个字符进行实体化转义。
function(a){
       return a.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
   }

输出

即使在客户端对用户的输入做了过滤、转义,攻击者一样可能,通过截包,转发等手段,修改你的请求包体。最终还是要在数据输出的时候做数据转义。

  1. 如果是字符串操作,保证字符串被引号包裹。
  2. 输出到页面上的数据必须使用相应方法转义,前端可以考虑寻找js插件处理。目前jquery-encoder,可用于前端json转义。使用方式与ESAPI类似,在需要渲染的时候进行转义。
  • 预防主要通过对输入数据的对每个用户的输入都做严格检查
  • 在输出的时候,对某些特殊字符进行转义,替换等 再过滤掉危险标签、属性和事件等。
  • 表单输入的字符进行实体转码,把<替换成&lt,>替换成&gt ,&替换成&amp
  • 为Cookie加上HttpOnly标记,以避免cookie劫持的危险。
  • 不要用innerHTML,用innerText

CSRF跨站请求伪造

img

过程

  1. 用户打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;

  2. 在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;

  3. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;

  4. 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;

  5. 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户的Cookie信息以的权限处理该请求,导致来自网站B的恶意代码被执行。
    避免:

  6. 验证 HTTP Referer 字段 : 在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。在通常情况下,访问一个安全受限页面的请求来自于同一个网站。

  7. CSRF Tokens

    最终的解决办法是使用CSRF tokens。在请求地址中添加 token 并验证 ,CSRF tokens是如何工作的呢?

    在后端生成表单的时候生成一串随机 token ,内置到表单里成为一个字段,同时,将此串 token 置入 session 中。每次表单提交到后端时都会检查这两个值是否一致,以此来判断此次表单提交是否是可信的。

    攻击者需要通过某种手段获取你站点的CSRF token, 他们只能使用JavaScript来做。 所以,如果你的站点不支持CORS, 那么他们就没有办法来获取CSRF token, 降低了威胁。

    确保CSRF token不能通过AJAX访问到! 不要创建一个/CSRF路由来获取一个token, 尤其不要在这个路由上支持CORS!

Webpack

概念问题三:什么是Loader?什么是Plugin?

答案:
1)Loaders是用来告诉webpack如何转化处理某一类型的文件,并且引入到打包出的文件中
2)Plugin是用来自定义webpack打包过程的方式,一个插件是含有apply方法的一个对象,通过这个方法可以参与到整个webpack打包的各个流程(生命周期)。

插件

html-webpack-plugin

extract-text-webpack-plugin

@rico-c rico-c added the 面试 label Dec 2, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant