2019前端面试总结

HTML面试题

  1. 你是如何理解HTML语义化的?
  • 比较简单的回答:我理解的语义化就是 标签用在合适的位置,好比段落要用p标签,标题要用h1-h6标签.
  • 更细点的回答:我理解的HTML语义化是正确的标签作正确的事情,可以便于开发者阅读和写出更优雅的代码的同时,让网络爬虫更好的解析。
  1. 为何要作到语义化?
  • 有利于SEO,有利于搜索引擎爬虫更好的解析咱们的页面,从而获取更多的有效信息,提高网页的权重。
  • 在没有CSS的时候,可以清晰看出网页的结构,加强可读性。
  • 便于团队合做开发和维护,提升开发效率
  1. <!DOCTYPE> 文档声明,它不是HTML标签,是一个指示web浏览关于页面使用哪一个HTML版本编写的指令。<!DOCTYPE> 声明必须位于文档的第一行,位于<html>标签以前。
    <!DOCTYPE html>html

  2. <html lang='en'>lang属性设定文档语言。node

    做用:SEO搜索引擎优化;有助于阅读障碍人士,经过读屏器阅读页面jquery

    还能够是 <html lang="zh-CN">web

5.meta标签的几种用法。面试

  • meta指定文档编码
//这行代码的意思是,文档用UTF-8编码的,浏览器解析的时候用UTF-8编码解析。
<meta charset="UTF-8">
  • 适配移动页面
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="viewport"  content="width=device-width,initial-scale=1.0">
  • 添加页面描述
<meta name="description" content="腾讯网(www.qq.com)是中国最浏览量最大的门户网站">

6.你用过哪些 HTML5标签。ajax

内容性的标签:编程

<header>          网页的头部
<nav>                网页的导航
<section>          标签订义文档中的节(好比章节、页眉、页脚或文档中的其余部分。)
<article>            标签的内容独立于文档的其他部分。好比外部的一篇文章,一个博客,论文等。
<aside>              网页侧边栏
<footer>            网页的页脚

功能性的标签json

<canvas>  经过脚本绘制图像
<Audio>    播放音频
<Video>    播放视频
  1. 什么是H5?
    H5是中国人制造的一个专有名词
    其实是指移动端页面,从某种意义上来讲它是 HTML5,微信网页,移动PPT的母级。

CSS面试题

  1. 两种盒模型。
    盒模型一共有两种模式:
  • 标准盒模型
    标准盒模型下 盒子的总宽度/高度=width/height+padding+border+margin,标准盒模型下 width是指content的宽度
  • 怪异盒模型
    怪异盒模型下 盒子的总宽度/高度=width/height+margin,怪异盒模型下 width 指的是内容content+padding+border的宽度------IE6,7,8 不设置 <!DOCTYPE html> 状况下,是怪异盒模型,若是加了就是标准盒模型
  • 具体是使用哪种盒模型,用box-sizing来设置
  • 兼容性: box-sizing现代浏览器都支持,但IE家族只有IE8版本以上才支持
box-sizing:content-box;
box-sizing:border-box;

目前使用此属性,须要加前缀,Mozilla须要加上-moz-,Webkit内核须要加上-webkit-,Presto内核-o-,IE8-ms-
-webkit-box-sizing:content-box;
-moz-box-sizing:birder-box;

标准盒模型的缺点是,盒子的宽度和高度计算复杂(两子元素并排例子)
怪异盒模型的优势,当肯定了width/height以后,能够随意修改padding和border的厚度值,而不用担忧父容器被撑爆。
canvas

  1. 如何实现水平和垂直居中?
  • 元素水平居中
//行内元素
text-align:center

//块级元素
margin-left: auto;
margin-right:auto;
  • 元素垂直居中
//方案1     position-----元素固定宽高的状况下
<div class="out">
  out
  <div class="in">in</div>
</div>

<style>
.out{
  width:300px;
  height:300px;
  background:#ccc;
  color:red;
  position:relative;
}
.in{
  width:100px;
  height:100px;
  background:pink;
  color:blue;
  position:absolute;
  left:50%;
  top:50%;
  margin-left:-50px;
  margin-top:-50px;
}
</style>

image.png跨域

//方案2     元素宽高不固定的状况下,把上边的 margin-left:(宽度/2);
  margin-top:(高度/2) 换成 transform:translate(-50%,-50%);
<div class="out">
  out
  <div class="in">测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测</div>
</div>

<style>
.out{
  width:300px;
  height:300px;
  background:#ccc;
  color:red;
  position:relative;
}
.in{
  background:pink;
  color:blue;
  position:absolute;
  left:50%;
  top:50%;
  transform:translate(-50%,-50%);
}
</style>

image.png

//方案3  flex
<div class="out">
  <div class="in"></div>
</div>

<style>
.out{
  width:300px;
  height:300px;
  background:#ccc;
  display:flex;  //flex布局
  justify-content:center;  //规定项目在主轴上居中对齐(水平居中)
  align-items:center;   //规定项目在交叉轴上居中对齐(垂直居中)
}
.in{
  width:100px;
  height:100px;
  background:pink;
}
</style>

image.png

//方案4 table-cell
给父元素设置 display:table; 父元素变成块级表格元素(至关于table);
给子元素设置 display:table-cell;使子元素变成表格单元格(至关于td),而后设置 vertical-align:center; text-align:center;
<div class="out">
  <div class="in">哈哈哈哈哈哈哈哈哈哈</div>
</div>

<style>
.out{
  width:300px;
  height:300px;
  background:#ccc;
  display:table;   
}

.in{
  background:pink;
  display:table-cell;     
  vertical-align:middle;  
  text-align:center;      
}
</style>

image.png

  1. flex怎么用?经常使用的属性有哪些?
    flex弹性布局,为盒模型提供最大的灵活性,任何一个容器,均可以指定为flex布局;
.box{
  display:flex;
}

//行内元素也能够设置为flex
.box{
    display:inline-flex;
}

注意设为flex布局后,子元素的float,clear,vertical-align属性将失效。

采用flex布局的元素,成为flex容器。它的全部子元素自动成为容器成员,称为flex项目。
经常使用的,设置到容器上的属性有:

1.flex-direction:属性决定主轴的方向(即项目的排列方向)。
.box{
  flex-direction:row | row-reverse | column | column-reverse
}

--------------------------------------------------------------------------------

2. flex-wrap:默认状况下,项目都排在一条线上。这个属性定义,若是一条轴线拍不下,如何换行。
.box{
  flex-wrap:nowrap | wrap | wrap-reverse
}

--------------------------------------------------------------------------------

3. justify-content:属性定义了项目在主轴上的对齐方式。
.box{
   justify-content:flex-start | flex-end | center | space-between | space-around
}

--------------------------------------------------------------------------------

4.align-items:属性定义项目在交叉轴上如何对齐
.box{
   align-items:flex-start | flex-end | center | baseline | stretch
}
baseline 项目的第一行文字的基线对齐
stretch(默认值)  若是项目未设置高度或设为auto,将占满整个容器的高度。

--------------------------------------------------------------------------------

5. align-content:属性定义了多根轴线的对齐方式。若是项目只有一根轴线,该属性不起做用。
.box {
  align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}

--------------------------------------------------------------------------------
6. flex-flow  是flex-direction和 flex-wrap的简写,默认是 row nowrap

设置到项目上的属性:

1.order 属性定义项目的排列顺序。数值越小,排列越靠前。默认为0
.item {
  order: 1;
}

2.flex-grow属性定义项目的放大比例,默认为0.即若是有剩余空间,也不放大。
.item {
  flex-grow: <number>; /* default 0 */
}

3.flex-shrink 属性定义了项目的缩小比例。默认为1,若是空间不足,项目将缩小。
.item {
  flex-shrink: <number>; /* default 1 */
}
若是全部项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。若是一个项目的flex-shrink属性为0,其余项目都为1,则空间不足时,前者不缩小。

负值对该属性无效。

4. flex-basis 属性定义了在分配多余空间以前,项目占据的主轴空间。默认为auto.即项目原本的大小。它能够设为跟width或height属性同样的值(好比350px),则项目将占据固定空间。
.item {
  flex-basis: <length> | auto; /* default auto */
}

5. flex 属性是flex-grow,flex-shrink,flex-basis,默认值为0 1 auto
该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。
建议优先使用这个属性,而不是单独写三个分离的属性,由于浏览器会推算相关值。

6.align-self 属性 容许单个项目有与其余项目不同的对齐方式,可覆盖align-items属性,默认值为auto,表示继承父元素的align-items属性,若是没有父元素,则等同于stretch。
.item {
  align-self: auto | flex-start | flex-end | center | baseline | stretch;
}
  1. BFC是什么?
    (Block Formatting Context)块级格式化上下文。BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之也如此.而且在一个BFC中,块盒与行盒(行盒由一行中全部的内联元素所组成)都会垂直的沿着其父元素的边框排列。
    这个能够具体回答,好比给一个div设置 overflow:hidden 它里边的浮动元素就会被包裹起来。
    如何触发BFC?
1. 根元素 即HTML元素
2. float的值,不为none
3. overflow的值,不为visible
4. display的值,为inline-block,table-cell ,table-caption
5. position 的值,为absolute 或fixed
  1. 选择器的优先级?
    CSS2的时候 (若是面试官更倾向于这个就这样回答)
!important 优先级最高  权值无穷大
style 写在元素行的内联样式其次   权值1000
id   权值100 
class/伪类/属性     权值10 
标签选择器       权值1
通配符        权值0
更准确的说法
1.越具体,优先级越高
2.写在后面的,会覆盖写在前面的
3.important优先级最高,可是要少用

6.清除浮动的方法:

方法
1:给父元素设置高度
2:给父元素设置overflow:hidden
3:  最佳实践  
.clearfix::after{
  content:'';
  display:block;
  clear:both;
}
.clearfix加到容器上,里边的子元素浮动就被清除了

原生JS面试题

1. ES6语法知道哪些?分别怎么用

A-------新增声明命令 let 和 const, let表示变量 const表示常量

特色:
1. 都是块级做用域,以{}代码块做为做用域范围,只能在代码块里面使用。
2. 不存在变量提高,只能先声明再使用,不然会报错。在代码块内,在声明变量以前,该变量都是不可用的,这在语法上成为'暂时性死区'
3. 在一个代码块内,不容许重复声明
4. const声明的是一个只读常量,在声明的时候就要赋值,不然会摆错,(若是const声明的是一个对象,对象所包含的值是能够修改的,抽象一点说,对象所指的地址是不可以改变的,变量成员是能够修改的)

B--------模板字符串
用一对反引号(``)标识,它能够当作普通字符串使用,也能够用来定义多行字符串。也能够在字符串中嵌入变量,JS表达式,或函数。须要写在${}中

var str = `abc
def
gh`;
console.log(str);
let name = '明';
function a(){
  return 'ming'
}
console.log(`个人名字叫作${name},年龄${17+5}岁,个人性别是${'男'},游戏ID:${a()})

C------函数的扩展

  • 函数的默认参数
    ES6为参数提供了默认值,在定义函数的时候,便初始化了这个参数,以便在参数没有被传递进去的时候使用。
function A(a,b=1){
  console.log(a+b)  
  console.log(a);
  console.log(b);
}
A(1);   // 2, 1,1
A(2,3);   //5,2,3
  • 箭头函数
    ES6中提供了一种简洁的函数的写法,箭头函数。
//只有一个参数时候,能够省略参数的括号。
//若是没有参数,或者参数在2个及两个以上,必须带括号
//当代码只有一行,而且有当即返回值的时候,能够省略花括号{}
var f = a => a+1

var sayHi = ()=>{
  console.log('hi')
}

var sum = (num1,num2)=>{
    console.log(num1+mun2)
}

箭头函数的特色
箭头函数的this是在定义函数的时候绑定,而不是在执行函数的时候绑定。
所谓在定义函数的时候绑定的意思是,箭头函数的this是继承自父执行上下文。箭头函数没有本身的this.

D---------对象的扩展

  • 属性的简写
    ES6容许在对象之中,直接写变量,这时属性名为变量名,属性的值为变量的值。
var foo = 'bar';
var baz = {foo};   //至关于 var bar={foo:foo}
  • 方法的简写
    省略冒号和function关键字
var o = {
  sayHi:function(){
      console.log('hi')
}
}

至关于
var o = {
  sayHi(){
  console.log('hi')
}
}
  • Object.keys()方法,获取对象的全部属性名和方法名。不包括原型的内容,返回一个数组
var person = {name:"john",age:20,study(){alert('study')}};
console.log(Object.keys(person))    //  ["name", "age", "study"]

console.log(Object.keys(['aa','bb','cc']);      //["0", "1", "2"]
console.log(Object.keys('abcdef'));      //["0", "1", "2", "3", "4", "5"]
  • Object.assign()方法
    这个方法将多个原对象的属性和方法,合并到了目标对象上面。
    能够接收多个参数,第一个参数是目标对象,后边都是源对象
var target ={}
var obj1 = {
  name:'petter',
  age:20,
  sex:'女'
}
var obj2 = {
  sex:'男',
  score:100
}

Object.assign(target,obj1,obj2);
console.log(target);  
//{age: 20,name: "petter", score: 100, sex: "男"}

E---------- for...of循环
是遍历全部数据结构的统一方法。for...of循环可使用的范围包括数组,Set,Map结构,某写类数组对象(arguments, DOM NodeList对象)以及字符串。

var arr = ["水星","金星","地球","火星"];
for(var s of arr){
  console.log(s);   //"水星"   "金星"   "地球"   "火星"
}

F--------import 和 export
ES6标准中,JavaScript 原生支持模块(module)了,这种将JS代码分割成不一样功能的小块进行模块化,将不一样功能的代码分别写在不一样的文件中,各模块只须要导出公共接口部分,而后经过模块的导入方式能够在其余地方使用。

export 用于对外输出本模块(一个文件能够看作是一个模块)的变量接口
import 用于在一个模块中加载另一个含有export接口的模块
import和export命令只能在模块的顶部,不能在代码块之中。

G-----Promise对象
Promise是异步编程的一种解决方案,将异步操做以同步操做的流程表达出来,避免了层层嵌套的回调函数。

它有三种状态,分别是 pending-进行中,resolved-已完成,rejected-已失败

Promise构造函数接收一个参数,这个参数是函数,而且这个函数传入两个参数,分别是 resolve 和 reject,分别是异步操做执行成功后的回调函数,和异步操做执行失败后的回调函数。(按照标准来说,resolve是将Promise的状态置为fullfiled,reject是将Promise的状态置为rejected。)
因此咱们用Promise的时候通常是包在一个函数中,在须要的时候去运行这个函数,如:

function runAsync(){
  var p = new Promise((resolve,reject)=>{
      setTimeout(function(){
        console.log('执行完成');
        resolve('随便什么数据')
},2000)
})  
return p;  
}

runAsync();

在咱们包装好的函数最后,会return出Promise对象,也就是说,执行这个函数咱们获得了一个Promise对象。
Promise对象上有then 和 catch方法。

runAsync().then(function(data){
  console.log(data)    //'随便什么数据'
  //后面能够用传过来的数据作些其余操做
  //......
})

在runAsync()的返回上直接调用then方法,then接收一个参数,是函数,而且会拿到咱们在runAsync中调用resolve时传的的参数。

这时候你应该有所领悟了,原来then里面的函数就跟咱们平时的回调函数一个意思,可以在runAsync这个异步任务执行完成以后被执行。这就是Promise的做用了,简单来说,就是能把原来的回调写法分离出来,在异步操做执行完后,用链式调用的方式执行回调函数。

而Promise的优点在于,能够在then方法中继续写Promise对象并返回,而后继续调用then来进行回调操做。

链式操做的用法

从表面上看,Promise只是可以简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数可以及时调用,它比传递callback函数要简单、灵活的多。因此使用Promise的正确场景是这样的:

function runAsync1(){
    var p = new Promise(function(resolve, reject){
        //作一些异步操做
        setTimeout(function(){
            console.log('异步执行1完成');
            resolve('随便什么数据1');
        }, 2000);
    });
    return p;            
}
function runAsync2(){
    var p = new Promise(function(resolve, reject){
        //作一些异步操做
        setTimeout(function(){
            console.log('异步执行2完成');
            resolve('随便什么数据2');
        }, 2000);
    });
    return p;            
}
function runAsync3(){
    var p = new Promise(function(resolve, reject){
        //作一些异步操做
        setTimeout(function(){
            console.log('异步执行3完成');
            resolve('随便什么数据3');
        }, 2000);
    });
    return p;            
}


runAsync1()
.then(function(data){
  console.log(data);
  return runAsync2()
})
.then(function(data){
  console.log(data);
  return runAsync3()
})
.then(function(data){
  console.log(data)
})

//2秒后打印
"异步执行1完成"
"随便什么数据1"

//又2秒后打印
"异步执行2完成"
"随便什么数据2"

//又2秒后打印
"异步执行3完成"
"随便什么数据3"

在then方法中,能够直接return 数据,而不是Promise对象,在后边的then中就能够接受到数据了。

runAsync1()
.then(function(data){
  console.log(data);
  return runAsync2()
})
.then(function(data){
  console.log(data);
  return "直接返回数据"
})
.then(function(data){
  console.log(data)
})

//2秒后打印
"异步执行1完成"
"随便什么数据1"

//又2秒后打印
"异步执行2完成"
"随便什么数据2"
"直接返回数据"

reject的用法

咱们前面的例子都是只有“执行成功”的回调,尚未“失败”的状况
reject就是把Promise的状态设置为 rejected,这样咱们在then中就能捕捉到,而后执行失败状况的回调。

function getNumber(){
  return new Promise((resolve,reject)=>{
    setTimeout(function(){
      var num = Math.ceil(Math.random()*10) //生成0-10之间的整数
      if(num<=5){
        resolve(num)
      }else{
        reject('数字太大了')
      }
    },2000)
  })
}

getNumber()
.then(
  function(data){
    console.log('resolved')
    console.log(data);
  },
  function(reason,data){
    console.log('rejected')
    console.log(reason)
  }
)

getNumber函数用来异步获取一个数字,2秒后执行完成,若是数字小于等于5,咱们认为是“成功”了,调用resolve修改Promise的状态。不然咱们认为是“失败”了,调用reject并传递一个参数,做为失败的缘由。 运行getNumber而且在then中传了两个参数,then方法能够接受两个参数,第一个对应resolve的回调,第二个对应reject的回调。因此咱们可以分别拿到他们传过来的数据

catch 的用法

咱们知道 Promise除了有一个then方法,还有一个catch方法,它是干什么的呢?
其实它跟then方法的第二个参数的做用是同样的。用来指定reject的回调。
它的用法:

getNumber()
.then(
  function(data){
    console.log('resolved')
    console.log(data);
  }
)
.catch(
  function(reason){
    console.log('rejected')
    console.log(reason)
  }
)

效果和写在then的第二个参数里面同样。不过它还有另一个做用,在执行resolve的回调(也就是then方法的第一个参数)时,若是抛出异常(代码出错了),那么并不会报错卡死JS,而是会进入到catch方法中

getNumber()
.then(function(data){
    console.log('resolved');
    console.log(data);
    console.log(somedata); //此处的somedata未定义
})
.catch(function(reason){
    console.log('rejected');
    console.log(reason);

在 resolve的会调用,咱们console.log(somedata),而somedata是未定义的,若是不用Promise,代码运行到这里,就直接在控制台报错了,不往下运行了。可是在这里会获得这样的结果:

 

image.png

 

也就是说进到catch方法里面去了,并且把错误缘由传到了reason参数中。即使是有错误的代码也不会报错了,这与咱们的try/catch语句有相同的功能。

all方法

Promise.all()提供了并行执行异步操做的能力。而且在全部异步操做执行完成之后,才执行回调。

Promise
.all([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
    console.log(results);
});

Promise.all(),接收一个数组做为参数,数组里的元素最终都返回一个Promise对象。这样,三个异步的操做就并行执行了,等到他们都执行完之后,才会进入到then里边,那么三个异步操做执行之后返回的数据哪里去了呢? all会把全部异步操做的结果放进一个数组,而且把数组传递给then,就是上边的results.
因此上边代码输出的结果是:

 

image.png

 

有了all方法,你就能够并行执行多个异步操做,而且在一个回调中处理全部的返回数据,有一个场景很适合用这个方法。
好比一些游戏类的素材比较多的应用,打开网页时预先加载,须要用到各类资源,好比图片,flash,以及各类静态文件。全部都加载完之后,再进行页面的初始化。

race的用法

Promise.all方法,其实是谁跑的慢,以谁为准执行回调。那么相对的就有另一个方法,以谁跑的块,以谁为准执行回调。
就是race方法,这个词原本就是赛跑的意思。race的用法与all同样。咱们修改下上边的计时器的时间:

function runAsync1(){
    var p = new Promise(function(resolve, reject){
        //作一些异步操做
        setTimeout(function(){
            console.log('异步执行1完成');
            resolve('随便什么数据1');
        }, 1000);
    });
    return p;            
}
function runAsync2(){
    var p = new Promise(function(resolve, reject){
        //作一些异步操做
        setTimeout(function(){
            console.log('异步执行2完成');
            resolve('随便什么数据2');
        }, 3000);
    });
    return p;            
}
function runAsync3(){
    var p = new Promise(function(resolve, reject){
        //作一些异步操做
        setTimeout(function(){
            console.log('异步执行3完成');
            resolve('随便什么数据3');
        }, 2000);
    });
    return p;            
}
Promise
.race(runAsync1(),runAsync2(),runAsync3())
.then(function(results){
  console.log(results);
})

//1秒后打印
"异步执行1完成"
//再过1秒后打印
"异步执行3完成"
//再过2秒后打印
"异步执行2完成"

这个race有什么用呢?使用场景仍是不少的,好比咱们能够用race给某个异步请求设置超时时间,而且在超时后执行相应的操做,代码以下:

//请求某个图片资源
function requestImg(){
    var p = new Promise(function(resolve, reject){
        var img = new Image();
        img.onload = function(){
            resolve(img);
        }
        img.src = 'xxxxxx';
    });
    return p;
}
 
//延时函数,用于给请求计时
function timeout(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            reject('图片请求超时');
        }, 5000);
    });
    return p;
}
 
Promise
.race([requestImg(), timeout()])
.then(function(results){
    console.log(results);
})
.catch(function(reason){
    console.log(reason);
});

requestImg函数会异步请求一张图片,我把地址写为"xxxxxx",因此确定是没法成功请求到的。timeout函数是一个延时5秒的异步操做。咱们把这两个返回Promise对象的函数放进race,因而他俩就会赛跑,若是5秒以内图片请求成功了,那么遍进入then方法,执行正常的流程。若是5秒钟图片还未成功返回,那么timeout就跑赢了,则进入catch,报出“图片请求超时”的信息。运行结果以下:

 

image.png

H-------解构赋值
ES6容许按照必定的模式,从数组和对象中提取值,对变量进行赋值。这杯成为解构赋值。

  • 数组的解构赋值;
    数组中的值会被自动解析到对应接收该值的变量中,数组的解构赋值要一一对应,若是有对应不上的,就是undefined
let [a,b,c,d] = [1,2,3];
console.log(a);  1
console.log(b);  2
console.log(c);  3
console.log(d);  undefined

-对象的结构赋值
对象的结构赋值与数组有一个重要的不一样,数组的元素是按照次序排列的,变量的取值由它的位置决定。而对象的属性是没有次序的,变量必须与属性同名,才能取到正确的值。

var person = {name:'小明',age:20,sex:'男'};
var {name,age,sex} = person;
console.log(name);
console.log(age);
console.log(sex);


//若是想要变量名和属性名不一样,能够这样写

let {name:foo,age:baz} = {name:'小明',age:20,sex:'男'}
console.log(foo);  '小明'
console.log(baz);  20

I--------set数据结构
Set的数据结构,相似数组,全部的数据都是惟一的,没有重复的值,它自己是一个构造函数;

var arr = [1,2,2,3,4,4];
var s = new Set(arr);
console.log(s);   //{1,2,3,4}

属性和方法:

size 数据的长度
add() 添加某个值,返回 Set 结构自己。
delete() 删除某个值,返回一个布尔值,表示删除是否成功。
has() 查找某条数据,返回一个布尔值。
clear() 清除全部成员,没有返回值。


console.log(s.size);  //4
console.log(s.add(5));  //{1,2,3,4,5}
console.log(s.delete(1));  //true
console.log(s.has(2));   //true
s.clear();

2.函数节流和函数防抖

前提条件:一个函数在短期内被屡次执行。
可是这种短期内屡次重复执行,一般状况下是没有必要的。咱们须要优化这种高频执行的JS。

优化的方法就是 函数节流 和 函数防抖
函数节流:
让函数在特定的时间以内只执行一次。特定时间内若是屡次触发函数,只有一次生效。
应用场景 :下拉加载。

函数防抖:
频繁触发某一事件,其中两次触发间隔时间大于等于特定时间,才执行代码一次。若是在特定时间内,又触发了一次事件,那么从新开始计算函数执行时间。简单的说,一个动做连续触发,只执行最后一次。
应用场景:搜索框搜索输入,好比用户输入字母间隔超过2秒再发送请求。

函数节流的例子
<button id="btn">大招<button>

<script>
  var cd = false;  //cd用来记录大招是否冷却,默认是没有冷却

  function attack(){
        console.log('发起攻击')
  }
  var button = document.queryselector('#btn');
   button.onclick = function(){
      if(cd){
           //点击大招按钮的时候判断一下,若是大招冷却,给出提示。
           console.log('大招冷却,没法攻击!')
      }else{
            //若是大招没有冷却,发起攻击
            attack();
            cd = true;     //发起攻击后,技能状态调整为冷却
            var timer = setTimeout(function(){
                cd = false;  //3秒钟后,技能冷却结束
            },3000)
      }
  }
</script>
//函数防抖例子
//公交关门
function close(){
  console.log('关门');
}

var button = document.querySelector('#btn');

var timer = null ;  //定时器一开始是空的
button.onclick = function(){
  //若是点击了按钮,发现上一个计时器还存在,那么就把它清除掉。
  if(timer){
    window.clearTimeout(timer);
  }
  //若是5秒种以内没有再点,就设置一个定时器,并执行关门函数,而且把定时器清除掉。
    timer = setTimeout(function(){
    close();
    timer= null;
  },5000)                   
}

3.手写AJAX

var xhr = new XMLHttpRequest();
xhr.open('GET','/xxx',true);

xhr.onreadystatechange = function(){
  if(xhr.readystate === 4){
    console.log('请求完成')
    if(xhr.status>=200&&xhr.status<300||xhr.status === 304){
          console.log('请求成功')
    }else{
          console.log('请求失败')
    }
  }
}

xhr.send();

4.这段代码里的this是什么?

主要是看函数怎么调用的!

fn()   // this指向 window/global
obj.fn()  //  this 指向 obj
fn.call(xx)  //this 指向 xx
fn.bind(xx)  //this指向 xx
new Fn()   //this指向 新的对象
fn  = ()=>{}   //this 指向外边的this



判断的通用方法
function fn(a,b){
    console.log(this)
}

fn(1,2);//这句等价于下边
fn.call(undefined,1,2);
fn.apply(undefined,[1,2]);
注意:
在严格模式下 'use strict'  ,此时 fn里的this,就是call和apply的第一个参数,也就是 undefined
在非严格模式下,也就是不用 'use strict' ,call和apply里的第一个参数若是是undefined或者是null,那么this会默认替换为 window


在看一个例子:
var  obj = {
    fn:function(a,b){
        console.log(this)
    },
    child:{
        fn2:function(){
            console.log(this)
        }
    }
}
obj.fn(1,2); 等价于  
obj.fn.call(obj,1,2);
obj.fn.apply(obj,[1,2]);  //因此this是obj

obj.child.fn2();等价于
obj.child.fn2.call(obj.child);
obj.child.fn2.apply(obj.child);   //因此this是 obj.child

5. 闭包和当即执行函数是什么?

  • 闭包:
    就是能读取其余函数内部变量的函数,在JS中,只有函数内部的子函数,可以读取函数内部的局部变量。因此闭包能够理解为,定义在一个函数内部的函数。

  • 闭包的缺点:
    让变量始终保持在内容中,内存消耗会很大。

  • 什么是词法做用域?
    词法做用域就是函数建立的时候所在的做用域。
    因此,函数在执行的时候,使用的变量,会先从本身内部去找,若是本身内部没有定义,就到本身的词法做用域(scopes)去找。

function car(){
  var speed = 0;
  function fn(){
      speed++;
      console.log(speed);
  }
  return fn;
}

var speedUp = car();
speedUp();  //1
speedUp();  //2

// 若是在car函数里,再声明一个函数fn,并返回fn,fn()内部又使用了car声明的变量,而后调用car函数并赋值给 一个全局变量speedUp
// 由于speedUp 属于全局做用域,而全局做用域在页面没有关闭的时候,是不会销毁的。因此也就致使了fn函数不会被销毁
// 执行speedUp就至关于 执行fn(),而fn()函数内部有用到局部变量speed,按照做用域链来寻找,fn()函数内没有声明
// 继续往上一级找,fn()函数是声明在car()内的,而speed是在car内声明的
// 因此第一次执行 speedUp() 的时候,结果是1;执行以后,speed是没有被销毁的
// 再次执行就是2



//再看个例子
var fnArr = [];
for(var i=0;i<10;i++){
  fnArr[i] = function(){
      return i;
  }
}

console.log(fnArr[1]);   //  这个时候数组里存的都是函数 function(){ return i}
console.log(fnArr[2]())   //10
console.log(fnArr[5]())   //10
console.log(fnArr[7]())   //10

//为何会输出10呢?  分析一下
fnArr[2]自己是一个函数,加括号,表示执行它指向的那个函数。
函数内部 return i,用到了i这个变量,因此会从本身内部去找这个变量,发现没有声明i,而后在去函数声明的做用域去找,函数是在for里声明的,可是for自己并非函数,因此function函数声明的做用域是全局做用域。而全局做用域里,for循环完之后,i已经变为10了,因此fnArr[i]()会输出10.

那么若是咱们想输出 0,1,2,3,4,5,....怎么办呢?
用当即执行函数
var fnArr = [];
for(var i=0;i<10;i++){
  fnArr[i] =( function(j){
      return j;
  })(i)
}

console.log(fnArr[0])   //0
console.log(fnArr[5])   //5
console.log(fnArr[7])   //7



再看个例子
for(var i=0;i<5;i++){
  setTimeout(function(){
    console.log(i)
  },2000)
}
//这里2秒后会输出5个5


for(var i=0;i<5;i++){
  (function(j){
    setTimeout(function(){
    console.log(j)
  },2000)
  })(i)
}
//这里2秒后会输出 0,1,2,3,4

再熟悉一下做用域链

var x = 10
function foo(){
  console.log(x);
}
foo();   //10  

function bar(){
  var x = 20;
  foo();
}
bar();  //10
执行bar就是执行foo,foo输出x,先从本身内部去找
发现没有声明x,而后到foo声明的做用域去找
foo是声明在全局做用域的,而全作做用域下,有声明变量x,因此输出10


//下边之因此输出30 是由于foo是在bar内部声明的。
 var x = 10;
 bar();  //30

 function bar(){
 var x = 30;
  function foo(){
        console.log(x); 
   }
    foo();
}

6.什么是JSONP,什么是CORS,什么是跨域?

什么是跨域?
--------是指不一样源的网站之间的访问。

同源策略
---------提到跨域,就不得不说一下同源策略,同源策略是NetScape提出的浏览器的一种安全策略,也就是指a网站,不能随便读取b网站的内容。

所谓同源:
--------指的是,协议、域名、端口、相同

最多见的应用是,当咱们调用ajax接口时,若是不设置跨域,浏览器会报错。这证实使用XMLHttpRequest对象不能发送跨域请求。

有疑惑的小伙伴确定会问,那我用a标签请求其余网站,是否是就是跨域了呢?
这里要明白跨域是指在当前域下调用其余域下的东西,而连接则是直接跳转到对方的域下了,跟你以前的域名毫无关系。

若是想从你的网站跨域去另外一个网站拿到一些资源,有一个前提是另一个网站的服务器支持跨域,这个须要在服务器端设置,不一样服务器的设置方法不同,咱们在这里很少说,就看客户端跨域如何解决?

解决跨域最多见的方式是 jsonp。

先弄清楚 json和jsonp的区别
json (JavaScript Object Notation)是一种轻量级的数据交换格式,用来在浏览器和服务器之间交换数据。
jsonp (JSON With Padding) 就是打包在函数中调动的json,或者包裹的json
json 是一种数据格式;jsonp是一种数据调用方式

//json
{
  "name":"小明"
}

//jsonp
callback({
 "name":"小明"
})

jsonp是jquery给咱们封装的一个方法,使用方法以下:

$.ajax({
  ulr:'xxx',
  type:'GET',
  dataType:'jsonp',
  success:function(data){
    console.log(data)
  }
})

上边的代码是当咱们调用外部的一个接口时,经过设置jquery里的ajax方法里的datatype属性的值为jsonp,就能够成功调用这个接口了。

首先咱们须要明白,在页面上直接发起一个跨域的ajax请求是不能够的,可是,在页面上引入不一样域上的js脚本倒是能够的,就像你能够在本身的页面上使用<img src=""> 标签来随意显示某个域上的图片同样。
看下怎么利用<script src="">来完成一个跨域请求。

<div class="container">
        <ul class="news"></ul>
        <button class="show">show news</button>
 </div>

 $('.show').addEventListener('click', function () {
            var script = document.createElement('script');
            script.src = 'http://127.0.0.1:8080/getNews?callback=appendHtml';
            document.head.appendChild(script);
            // document.head.removeChild(script);  //为了保持代码的整洁,直接删除了新增的标签,可是标签的做用已经起到了
        })

        function appendHtml(news) {
            var html = '';
            for (var i = 0; i < news.length; i++) {
                html += '<li>' + news[i] + '</li>';
            }
            console.log(html);
            $('.news').innerHTML = html;
        }

jsonp跨域的原理:

  1. 使用script标签发送请求,这个标签支持跨域访问
  2. 在script标签里,给服务器传递一个callback
  3. callback的值对应到页面上定义的一个全局函数(为何是全局函数呢?由于服务器接收到callback函数后,会返回页面中的script去寻找,若是不写到全局做用域中,根本找不到)。
    4.服务器端返回的是一个函数的调用,调用的时候会把返回数据做为参数包裹在这个函数里。

缺点: jsonp只能解决get方式的跨域。若是传输的数据比较大,这种方式就不行了。

cors跨域

cors (cross origin resource sharing) 全称 '跨域资源共享'
相对于jsonp, cors支持全部类型的HTTP请求,而且实施起来很是简单。

cors背后的基本思想是,使用自定义的HTTP头部容许浏览器和服务器互相了解对方,从而决定请求和响应成功与否。

cors原理:
当使用XMLHttpRequest发送请求的时候,浏览器发现该请求不符合同源策略,会给该请求加一个请求头 origin,后台会进行一系列的处理,若是肯定接受请求,则在返回结果中,加入一个响应头 Access-Control-allow-origin;浏览器判断该响应头中是否包含 origin的值,若是有,则浏览器会处理响应,咱们就拿到数据。若是没有,则浏览器会直接驳回,咱们就拿不到数据。

IE10以上支持,现代浏览器也都支持cors

JSONP和CORS的区别?

  1. JSONP只支持GET请求,而CORS支持全部类型的HTTP请求。
  2. 使用CORS,开发者可使用普通的XMLHttpRequest对象发起请求和得到数据。比起JSONP有更好的错误处理。
    3.JSONP主要被老的浏览器支持,它们每每不支持CORS,而绝大多数的现代浏览器都已经支持了CORS

7. async/await 怎么用? 怎么捕获异常?

async是异步的缩写。而await 能够认为是 async wait的缩写。

async做为一个关键字放在函数前面,用来声明这个函数是一个异步函数。既然是异步函数就意味着该函数的执行不会阻塞后边的代码;
async函数返回的是一个Promise对象

await用于等待一个异步方法执行完成,await等待的是一个表达式,而表达式的计算结果是一个Promise对象或者其余值。
(await至关于运算符,用于组成表达式,await表达式的运算结果取决于它等到的东西)
若是等到的不是一个Promise对象,那么await表达式的运算结果就是它等到的东西。
若是它等到的是一个Promise对象,await就忙起来了,它会阻塞后面的代码,等待Promise对象resolve,而后获得resolve的值。做为await表达式的运算结果。
await只能用到async函数中。

function sayHi(name){
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve('Hi'+name);
    },2000)
  })
}

async function test(){
  var result = await sayHi('小明');
  console.log(result);
}

test();

async和 await捕获异常,须要用到 try/catch的方式:

由于await后面跟着的是 Promise对象,当有异常的状况下,会被Promise对象内部的catch捕获。而await就是一个then的语法糖,并不会捕获异常,那么就须要借助try/catch来捕获异常,并进行相应的逻辑处理。

function sayHi(name){
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      //生成0-10的随机整数
      var num  = Math.ceil(Math.random()*10);
      if(num>5){
         //若是随机数大于5,就resolve出结果
         resolve('Hi'+name);
      }else{
        //不然就抛出错误
        reject('出错了');
      }
      
    },2000)
  })
}

async function test(){
   try{
     var result = await sayHi('小明');
     console.log(result);
   }catch(error){
     console.log(error)
   }
}

test();

8.如何实现深拷贝?

关键字:
一、递归
二、判断类型
三、检查循环引用(环)
四、不可能拷贝 _proto_

拷贝分为浅拷贝和深拷贝
在这以前,咱们先弄清楚两个概念。

  • 基本数据类型
Number ,String,Boolean,Null,Undefined 都是基本数据类型
基本数据类型时按值访问的,由于能够直接操做保存在保存在变量中的实际值。
var a = 10;
var b = a;    //b只是保存了a复制的一个副本。因此 a和b是互相独立的。
console.log(a);   //10
console.log(b);  //10

//a值的改变不会影响b的值
a = 20;
console.log(a);  //20
console.log(b);  //10
  • 引用数据类型
引用数据类型也就是对象类型 Object type.好比 Object,Array,Function,Data等。
JavaScript的引用数据类型是保存在堆内存中的对象。
与其余语言不一样的是,你不能够直接访问堆内存空间中的位置和操做堆内存空间。只能操做对象在栈内存中的引用地址。
因此,引用类型数据在栈内存中保存的是对象在堆内存中的引用地址,经过这个引用地址能够快速查找到保存在堆内存中的对象。
var obj1 = new Object();
var obj2 = obj1;
obj2.name = '小明';
console.log(obj1.name);
说明这两个引用数据类型指向了同一个堆内存对象。obj1赋值给onj2,实际上这个堆内存对象在栈内存的引用地址复制了一份给了obj2,可是实际上他们共同指向了同一个堆内存对象。
实际上改变的是堆内存对象。

下面咱们来演示这个引用数据类型赋值过程:

image.png

好了,上边两个概念了解清楚之后,咱们进入正题。

对象的浅拷贝

对象浅拷贝比较简单,就是将一个变量赋值给另一个变量

var obj1 = {
  name:"小明",
  age:20
}

var obj2 = obj1;
console.log(obj2.age)  //20
obj2通过浅拷贝,拥有了obj1的属性

封装浅拷贝方法

var easyCopy = function(oldObj){
      //constructor 属性应用了该对象的构造函数
      var newObj = oldObj.constructor === Array?[]:{};
      if(typeof oldObj != 'object')  return;
      for(var key in oldObj){
           if(oldObj.hasOwnProperty(key)){
               newObj[key] = oldObj[key]
           }
      }
      return newObj;
}

var obj1 = {
    name:'小明',
    age:20
}

var obj2 = easyCopy(obj1);
console.log(obj2)

浅拷贝存在的问题:
咱们知道,引用数据类型的赋值,实际上是变量a把对象在堆内存中的地址,复制了一份给变量b,这个时候,a和b指向的都是同一个对象。经过a或者b均可以改变堆内存中的对象(好比添加删除属性),a和b是能够相互影响的。因此这种浅拷贝会带来BUG。

对象的深拷贝

深拷贝的目的就是为了实现 复制出2个彻底相同,却又彻底独立,互不干扰的对象。

var deepCopy = function(obj){
  var cloneObj = Array.isArray(obj)? []:{};
  if(obj && typeof obj === 'object' ){
    for(var key in obj){
      if(obj.hasOwnProperty(key)){
        cloneObj[key] = typeof obj[key] === 'object'?deepCopy(obj[key]):obj[key]
      }
    }
  }
  return cloneObj;
}

var obj = {
  name:'小红',
  age:18,
  friend:{
    name:'小明',
    sex:'男',
    study:["数学","英文","物理"]
  }
}
var obj2 = deepCopy(obj);
console.log(obj2)

obj.friend = {
  name:'小明明明',
  sex:"女"
}
console.log(obj);
console.log(obj2);

深拷贝的缺点:虽然深拷贝可以避免浅拷贝出现的问题。可是却会带来性能上的问题,若是一个对象很是复杂且数据庞大,所消耗的性能将会是很可观的。

关于for in

for..in能够用来遍历任何一个对象,它会将对象上的全部的属性所有遍历出来,包裹原型链上的属性。因此上边代码中须要hasOwnProperty来判断这个属性究竟是不是对象自己的属性。由于数组也是对象,for..in也能够用来遍历数组,可是for in损耗性能较多,因此尽可能不要用它来遍历数组。

关于递归

一个方法,重复调用自身的状况,叫作递归。
须要注意的是,必定要有一个条件来结束递归,不然将会陷入无限的循环。

var num = 0;
function recursion(){
    if(num < 50){
        num++;
        recursion()
    }
}
recursion()

9 如何用正则实现trim()?

trim()方法去掉字符串两端的空格,不管有多少个空格都会去掉,字符串中间的空格不会被去掉。
function  trim(string){
    return string.replace(/^\s+|\s+$/g);
}
var str = '   ab  cd ef   ';
var res = replace(str);
console.log(res);   //'ab  cd ef'

10.不用class如何实现继承,用class如何实现继承?

//不用class
function Person(name,age){
  this.name = name;
  this.age = age;
}

Person.prototype.printName =function(){
  console.log(this.name);
}

// 继承属性
function Mail(name,age,sex){
  Person.call(this,name,age);
  this.sex = sex;
}

// 继承方法
//Object.create 建立一个空对象,空对象的_proto_指向Person.prototype
Mail.prototype = Object.create(Person.prototype);
Mail.prototype.printSex = function(){
  console.log(this.sex);
}
//由于Mail.prototype此时是指向Person.prototype的,因此Mail.prototype.contructor是指向 Person的。咱们须要修改它的指向。
Mail.prototype.contructor = Mail

var john = new Mail('约翰',20,'男');
console.log(john.name);
john.printName();
john.printSex();
//用class
class Person {
  constructor(name,age){
    this.name = name;
    this.age = age;
  }
  
  sayAge(){
    console.log(`i am ${this.age}`)
  }
}

class Student extends Person{
  constructor(name,age,score){
    supre(name,age);
    this.socre = score;
  }
  
  sayScore(){
    console.log(`i get ${this.score}`);
  }
}

11.如何实现数组去重?

//双循环去重
function fn(arr){
  if(Array.isArray(arr)){
    //声明一个新的数组,而后把要遍历的数组的第一个元素放入这个新数组
    var newArr = [arr[0]];
    //而后从第二个元素开始遍历老的数组,同时遍历新数组
   //把老数组和新数组的已有元素作对比,若是不相等,就放入新数组。
    for(let i=1;i<arr.length;i++){
      let flag = true;
      for(let j=0;j<newArr.length;j++){
        if(arr[i] === newArr[j]){
          flag = false;
          break;
        }
      }
      if(flag){
        newArr.push(arr[i])
      }
    }
  }
  return newArr
}

var arr = [1,1,2,2,3,4,5,5];
var arr2 = fn(arr);
console.log(arr2)  [1, 2, 3, 4, 5]




//indexOf去重
//两个关键点 一、声明新数组 二、判断老数组里的元素是否在新数组里,没有就push进新数组
function fn(arr){
  if(!Array.isArray(arr)){
       console.log('type error');
        return 
   }
   var newArr = [];
   for(let i=0;i<arr.length;i++){
       if(newArr.indexOf(arr[i]) == -1){
              newArr.push(arr[i])
        }
    }
   return newArr;
}
var arr = [1,1,2,2,3,4,5,5];
var arr2 = fn(arr);
console.log(arr2)   [1, 2, 3, 4, 5]



//set 去重  
//ES6?新增了一个数据类型 set,set的一个最大的特色就是数据不重复。
//Set()函数能够接收一个数组(或者类数组对象)做为参数来初始化。
function fn(arr){
  if(!Array.isArray(arr)){
    console.log('type error');
    return;
  }
  return [...new Set(arr)]
}

var arr = [1,1,2,2,3,4,5,5];
var arr2 = fn(arr);
console.log(arr2)   [1, 2, 3, 4, 5]

DOM面试题

1. DOM事件模型是什么?

  1. 事件冒泡-----事件开始时,由最具体的元素接收,而后逐级向上传播到较为不具体的元素。
  2. 事件捕获------不太具体的节点更早的接收事件,而最具体的元素最后接收事件,和事件冒泡相反。
  3. DOM事件流------DOM2级事件规定,事件流包括三个阶段,事件捕获阶段,处于目标阶段,事件冒泡阶段。首先发生的是事件捕获,为截取事件提供机会,而后是实际目标接受事件,最后是事件冒泡

Opera、Firefox、Chrome、Safari都支持DOM事件流,IE不支持事件流,只支持事件冒泡

事件处理程序是什么?

响应某个事件的函数,就叫作事件处理程序

  • 事件处理程序分为4种
//HTML事件处理程序
<input type="button" name="btn" value="点击" onclick="alert('clicked')"

//DOM0级事件处理程序
<button id="box">点击</button>
var box = document.querySelector('#box');
box.onclick = function(){
  console.log('DOM0级事件处理程序')
}
box.onclick = null    //删除绑定的事件



//DOM2级事件处理程序
<button id="box">点击</button>
var box = document.querySelector('#box');
box.addEventListener('click',function(){
  console.log('DOM2级事件处理程序')
},true)
//删除绑定的事件
box.removeEventListener('click',函数名,boolean)




//IE事件处理程序
function showMes(){
  console.log('我是IE事件处理程序')
}

var box = document.getElementById('box');
box.attatchEvent('onclick',showMes);
//删除绑定的事件处理程序
box.detachEvent('onclick',showMes);

跨浏览器事件处理程序

function showMes(){
  console.log('我被点击了')
}

var box = document.getElementById('box');
var EventUtil = {
    addHandler:function(element,type,handle){
      if(element.addEventListener){
        element.addEventListener(type,handle,false);
      }else if(element.attatchEvent){
        element.attatchEvent('on'+type,handle)
      }else{
        element['on'+type] = handle
      }
    },
    removeHandler:function(element,type,handle){
      if(element.removeEventListener){
        element.removeEventListener(type,handle,false);
      }else if(element.detachEvent){
        element.detachEvent('on'+type,handle);
      }else{
        element['on'+type] = null;
      }
    }
}
EventUtil.addHandler(box,'click',showMes);
EventUtil.removeHandler(box,'click',showMes);

事件对象

在触发DOM上的事件时,都会产生一个事件对象Event
Event 对象表明事件的状态,好比事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。
兼容DOM的浏览器,会将一个对象,传入事件处理程序中。
例如:

var box = document.querySelector('#box');
box.addEventListener('click',function(event){
  console.log(event.type)   //输出事件的类型
})

event对象的属性
event.target    获取事件的类型
event.type      获取事件的目标(就是时间绑定到哪一个元素上了)
...等等

event对象的属性
event.stopPropagation :
再也不派发事件。就是终于事件在传播过程的捕获,目标处理,冒泡阶段的传播,事件再也不被派到其余节点。

event.preventDefault :
通知浏览器不要执行与事件相关的默认动做。好比a标签


IE事件对象 
window.event
常见的属性
window.event.type   至关于 event.type
window.event.srcElement 至关与 event.target
window.event.cancleBubble 至关于  event.stopPropagation()
window.event.returnValue   至关于 event.preventDefault()

2.移动端的触摸事件了解吗?

touch 触摸事件,有4种之分
1. touch start   手指触摸到屏幕会出发
2. touch move  当手指在屏幕上移动时会触发
3. touch end    当手指离开屏幕时会触发
4. touch cancel  可由系统进行的触发,好比手指触摸屏幕的时候,忽然alert了一下,则能够触发该事件。

tap事件
触碰事件,通常用于代替click
tap:手指碰一下屏幕会触发
longTap :手指长按屏幕会触发
singleTap:手指碰一下屏幕会触发
doubleTap:手指双击屏幕会触发

swipe事件
滑动事件
swipe:手指在屏幕上滑动时触发
swipeLeft:手指在屏幕上向左滑动时会触发
swipeRight:手指在屏幕上向右滑动时会触发
swipeUp:手指在屏幕上向上滑动时会触发
swipeDown:手指在屏幕上向下滑动时会触发


模拟swipe事件:思路 记录两次touchmove的位置差,若是后一次在前一次的右边,说明向右滑了。

3.事件委托(也叫事件代理)是什么?

  • 事件委托就是利用事件冒泡,只指定一个事件处理程序,就能够管理某一类型的全部事件。

  • 举个取快递的例子:
    有三个同事预计会在周一收到快递。为签收快递,有两种办法:一是三我的在公司门口等快递;二是委托给前台MM代为签收。
    前台MM收到快递后,她会判断收件人是谁,而后按照收件人的要求签收,甚至代为付款。
    这种方案还有一个优点,那就是即便公司里来了新员工(无论多少),前台MM也会在收到寄给新员工的快递后核实并代为签收。
    这个例子包含2层意思
    1.如今委托前台的同事,是能够代为签收的(即程序中,现有的DOM节点是有事件的)
    2.新员工也是能够被前台代为签收的(即程序中,新添加的DOM节点也是有事件的)。

<div class="container">
  <div class="box">box1</div>
  <div class="box">box2</div>
</div>
 <button class="add">add</button>

var con = document.querySelector('.container');
var box = document.querySelectorAll('.box');
var addBtn = document.querySelector('.add');

//给每一个box都绑定一个事件
// box.forEach(function(node){
//   node.onclick = function(){
//     console.log(this.innerText);
//   }
// })

//用父级div作事件代理
con.onclick = function(e){
  if(e.target.classList.contains('box')){
    console.log(e.target.innerText)
  }
}


//有了事件代理之后,哪怕新增的box,也会被绑定事件。
var i = 4;
addBtn.onclick =function(){
  var box = document.createElement('div');
  box.classList.add('box');
  box.innerText = 'box' + i++;
  con.appendChild(box);
}