7、svg、D3、ES6相关、TS相关、jQuery相关、Error与try|catch、Date日期时间、判断数据类型void、预解释|闭包|内存泄漏|函数执行|高阶|重载、节流|去抖|柯里化、点透、for|while循环|if条件|逗号、对象|原型链|多态|创建|create|defineProperty、JS设计模式、parseFloat、业务逻辑、造轮子(3400行)

一、svg
  <html>
    <head>
      <meta charset="utf-8">
      <title>svg子标签</title>
      <style>
        .color{
          color: rgb(185, 125, 125);
        }
        .fontWeight{
          font-weight: 900;
        }
        .flex{
          display: flex;
        }
        .flex > pre{
          margin: 0;
        }
        .svgUp{ /* svg标签,默认值,width:300px; height:150px */
          width: 500px;
          height: 130px;
        }
        .svgMiddle{ /* svg标签,默认值,width:300px; height:150px */
          width: 500px;
          height: 100px;
        }
        .svgDown{ /* svg标签,默认值,width:300px; height:150px */
          width: 500px;
          height: 80px;
        }
      </style>
    </head>
    <body style="zoom:1">
      <div>
        <div class="fontWeight">SVG</div>
        <div>教程,https://www.cainiaojc.com/svg/svg-tutorial.html</div>
        <div>使用,"<"img src="svg-icon.svg"">"</div>
        <div>坐标,左上角的坐标是0,0--这点特别重要</div>  
      </div>
      <div>
        <div class="fontWeight">一、svg标签下-基本标签-形状</div>
        <div class="flex">
          <pre>
            1、直线
              "<"line x1="0" y1="20" x2="200" y2="70" 
                stroke="gray" 
                stroke-width="2"
              /">"
            <svg class="svgUp">
              <line x1="0" y1="20" x2="200" y2="70" stroke="gray" stroke-width="2"/>
            </svg>
          </pre>
          <pre>
            2、折线
              "<"polyline points="20,10 30,100 170,50 70,50"
                  style="fill:#ccc;
                    stroke:#000;
                    stroke-width:2"
              /">"
            <svg class="svgUp">
              <polyline points="20,10 30,100 170,50 70,50" style="fill:#ccc;stroke: #000;stroke-width:2"/>
            </svg>
          </pre>
          <pre>
            3、多边形
              "<"polygon points="20,10 30,100 170,50 70,50"
                  style="fill:#ccc; 
                    stroke:#000;
                    stroke-width:2"
              /">"
            <svg class="svgUp">
              <polygon points="20,10 30,100 170,50 70,50" style="fill:#ccc; stroke:#000;stroke-width:2"/>
            </svg>
          </pre>
        </div>
        <div class="flex">
          <pre>
            4、矩形
              fill-opacity,属性定义填充颜色透明度(合法的范围是:0到1)
              stroke-opacity,属性定义笔触颜色的透明度(合法的范围是:0到1)
              "<"rect x="20" y="20" width="250" height="40"
                  fill="gray" 
              /">"
            <svg class="svgMiddle">
              <rect x="20" y="20" width="250" height="40" fill="gray"/>
            </svg>
          </pre>
          <pre>
            5、圆形
              cx和cy,圆点的x和y坐标,默认为(0,0);r,圆的半径
              "<"circle cx="50" cy="50" r="40" 
                  style="fill:gray;"
              /">"
            <svg class="svgMiddle">
              <circle cx="50" cy="50" r="40" style="fill:gray;"/>
            </svg>
          </pre>
          <pre>
            6、椭圆
            cx和cy,圆点的x和y坐标,默认为(0,0);rx和ry,水平和垂直半径
            "<"ellipse cx="110" cy="50" rx="100" ry="40"
                style="fill:grey;"
            /">"
            <svg class="svgMiddle">
              <ellipse cx="110" cy="50" rx="100" ry="40" style="fill:grey;"/>
            </svg>
          </pre>  
        </div>
      </div>
      <div class="flex">
        <div>
          <div class="fontWeight">二、svg标签下-组合标签-形状</div>
          <pre>
            1、示例
              <svg class="svgDown">
                <symbol id="shapeA">
                  <circle cx="0" cy="0" r="60" ></circle>
                </symbol>
                <symbol id="shapeB">
                  <path d="M0 50 L50 0 L50 50 Z" />
                </symbol>
                <defs>
                  <g id="shapeC">
                    <rect x="0" y="0" width="50" height="50" ></rect>
                  </g>
                  <g id="shapeD">
                    <circle cx="24" cy="24" r="24" ></circle>
                    <path d="M0 50 L120 0 L120 50 Z" />
                  </g>
                </defs>
                <use xlink:href="#shapeA" x="0" y="20" ></use>
                <use xlink:href="#shapeB" x="100" y="30" ></use>
                <use xlink:href="#shapeC" x="200" y="30" ></use>
                <use xlink:href="#shapeD" x="300" y="30" ></use>
              </svg>
            附、示例
              <!-- <template>
                <svg :class="svgClass">
                  <use :xlink:href="iconName" :fill="color" />
                </svg>
              </template> --> 
          </pre>
          <pre class="color">
            2、标签
              "<"use xlink:href="#shapeA" x="0" y="30"">""<"/use">"
              (1)标签
                A、symbol,定义一个形状,被use引用后显示
                B、defs,定义多个形状,被use引用后显示
                  g,定义多个形状里的一个形状
                C、use,使用形状
              (2)属性
                A、xlink:href,是svg的一种属性,
                  用于定义svg文档中的元素与外部资源之间的链接,包括图片、音频、视频等
          </pre>
        </div>
        <div>
          <div class="fontWeight">三、svg标签下-属性</div>
          <pre>
            1、示例
            <?xml version="1.0" standalone="no"?>
            <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
            <svg 
              viewBox="0 0 1024 1024" 
              xmlns="http://www.w3.org/2000/svg" 
              xmlns:xlink="http://www.w3.org/1999/xlink"
              id="svgID" 
              class="icon" 
              t="1701073968299" 
              version="1.1"  
              width="见下面script" 
              height="见下面script"
            >
              <path 
                d="
                  M1010.036364 465.454545L539.927273 9.309091c-13.963636-13.963636-41.890909-13.963636-55.854546 0L13.963636 465.454545c-18.618182 13.963636-18.618182 41.890909 0 55.854546 13.963636 13.963636 41.890909 13.963636 55.854546 0l414.254545-395.636364c13.963636-13.963636 41.890909-13.963636 55.854546 0l414.254545 395.636364c13.963636 13.963636 41.890909 13.963636 55.854546 0 18.618182-13.963636 18.618182-41.890909 0-55.854546z
                  M372.363636 698.181818v186.181818c0 27.927273-18.618182 46.545455-46.545454 46.545455H232.727273c-27.927273 0-46.545455-18.618182-46.545455-46.545455v-279.272727c0-27.927273-18.618182-46.545455-46.545454-46.545454s-46.545455 18.618182-46.545455 46.545454v372.363636c0 27.927273 18.618182 46.545455 46.545455 46.545455h279.272727c27.927273 0 46.545455-18.618182 46.545454-46.545455v-186.181818c0-27.927273 18.618182-46.545455 46.545455-46.545454h93.090909c27.927273 0 46.545455-18.618182 46.545455-46.545455s-18.618182-46.545455-46.545455-46.545454H418.909091c-27.927273 0-46.545455 18.618182-46.545455 46.545454z
                  M837.818182 605.090909v279.272727c0 27.927273-18.618182 46.545455-46.545455 46.545455h-93.090909c-27.927273 0-46.545455 18.618182-46.545454 46.545454s18.618182 46.545455 46.545454 46.545455h186.181818c27.927273 0 46.545455-18.618182 46.545455-46.545455v-372.363636c0-27.927273-18.618182-46.545455-46.545455-46.545454s-46.545455 18.618182-46.545454 46.545454z" 
                p-id="4219" 
                fill="#000"
              ></path>
            </svg>
          </pre>
          <pre class="color">
            2、属性
            "<" 标签内容,免 /">"
              (1)viewBox,svg元素的视图框坐标是x y width height,如"0 0 1024 1024" 
              (2)xmlns,xml命名空间,xml namespace,防止来自不同技术的元素发生冲突
              (3)xmlns:xlink,xlink在XML文档中创建超级链接的语言
              (4)id,见下面script
              (5)class,标签的样式
              (6)t,本代码生成时的时间
              (7)version,图片的版本
              (8)width,svg宽,见下面script
              (9)height,svg高,见下面script
            3、附xml标签的属性
              (1)standalone="no",文档会引用一个外部文件,!DOCTYPE后的内容,即为引用的内容
          </pre>
        </div>
        <div>
          <div class="fontWeight">四、path标签下-属性</div>
          <pre>
            1、示例
            <svg class="svgDown">
              <path d="M120 30 L10 80 L250 80 Z" fill="#000" stroke="red" stroke-width="2"/>
            </svg>
          </pre>
          <pre class="color">
            2、属性
              "<"path d="M120 30 L10 80 L250 80 Z" 
                  fill="#000" stroke="red" stroke-width="2" /">" 
              (1)属性
                A、d,包含所有的绘图命令,见(2)(3)
                B、fill,轮廓内的填充颜色
                C、stroke,轮廓内的描边颜色
                D、stroke-width,轮廓内的描边宽度
                附、关于样式的属性,可以写到class或style里
              (2)常用命令
                A、M:x,y moveto 将笔移动到指定点x,y而不绘图
                B、L:x,y Lineto 绘制一条从当前笔位置到指定点x,y的直线
                C、Z:封闭路径 通过从当前点到第一个点画一条线来封闭路径
              (3)不常用命令
                A、H:X 水平线 画一条水平线到定义的点,(指定的x,笔当前的y)
                C、V:y 垂直线 在由(定义的当前x,指定y)定义的点上画一条垂直线
          </pre>
        </div>
      </div>
    </body>
  </html>
  <script>
    document.getElementById("svgID").setAttribute("width", "50");
    document.getElementById("svgID").setAttribute("height", "50");
  </script>

二、D3之基础用法、综合用法、数据平放、数据立放、饼图
1、基础用法(绑定文字)
  <html> 
      <head> 
          <meta charset="utf-8"> 
          <title>Hello World</title> 
          <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> 
      </head>
      <body>
          <p></p>
          <p></p>
          <p></p>
      </body> 
  </html>
  <script>  
      var dataset = ["sun","moon","you"];
      var body = d3.select("body");
      var p = body.selectAll("p");
      p.data(dataset).text(function(d, i){
          return "I love " + d;
    });
    console.log(d3.range(5)); 
  </script> 
2、综合用法(柱形图,含选择集、数据绑定、比例尺、坐标轴)
  <html> 
      <head> 
          <meta charset="utf-8"> 
          <title>Hello World</title> 
          <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> 
          <style>
      .axis path,
      .axis line{
        fill: none;
        stroke: black;
        shape-rendering: crispEdges;
      }
      .axis text {
        font-family: sans-serif;
        font-size: 11px;
      }
      </style>
      </head>
      <body>
      </body> 
  </html>
  <script>  
    //画布大小
      var width = 400;
      var height = 400;
      //在 body 里添加一个 SVG 画布   
      var svg = d3.select("body")
      .append("svg")
      .attr("width", width)
      .attr("height", height);
      //画布周边的空白
      var padding = {left:30, right:30, top:20, bottom:20};
      //定义一个数组
      var dataset = [10, 20, 30, 40, 33, 24, 12, 5];
      //x轴的比例尺
      var xScale = d3.scale.ordinal()
      .domain(d3.range(dataset.length))
      .rangeRoundBands([0, width - padding.left - padding.right]);
      //y轴的比例尺
      var yScale = d3.scale.linear()
      .domain([0,d3.max(dataset)])
          .range([height - padding.top - padding.bottom, 0]);    
      //定义x轴
      var xAxis = d3.svg.axis()
      .scale(xScale)
      .orient("bottom");
      //定义y轴
      var yAxis = d3.svg.axis()
      .scale(yScale)
          .orient("left");
      //矩形之间的空白
    var rectPadding = 4;
      //添加矩形元素
      var rects = svg.selectAll(".MyRect")
          .data(dataset)
          .enter()
          .append("rect")
          .attr("class","MyRect")
          .attr("transform","translate(" + padding.left + "," + padding.top + ")")
          .attr("x", function(d,i){
              return xScale(i) + rectPadding/2;
          })
          .attr("y",function(d){
              return yScale(d);
          })
          .attr("width", xScale.rangeBand() - rectPadding )
          .attr("height", function(d){
              return height - padding.top - padding.bottom - yScale(d);
          })
          .attr("fill","steelblue");
      //添加文字元素
      var texts = svg.selectAll(".MyText")
          .data(dataset)
          .enter()
          .append("text")
          .attr("class","MyText")
          .attr("transform","translate(" + padding.left + "," + padding.top + ")")
          .attr("x", function(d,i){
              return xScale(i) + rectPadding/2;
          } )
          .attr("y",function(d){
              return yScale(d);
          })
          .attr("dx",function(){
              return (xScale.rangeBand() - rectPadding)/2;
          })
          .attr("dy",function(d){
              return 20;
          })
          .text(function(d){
              return d;
          })            
          .style({
              "fill":"#FFF",
              "text-anchor":"middle"
          });
      //添加x轴
      svg.append("g")
          .attr("class","axis")
          .attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
          .call(xAxis); 
      //添加y轴
      svg.append("g")
          .attr("class","axis")
          .attr("transform","translate(" + padding.left + "," + padding.top + ")")
          .call(yAxis);
  </script> 
3、数据平放
(1)数据平放(无比例尺)
  <html> 
      <head> 
          <meta charset="utf-8"> 
          <title>Hello World</title> 
          <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> 
      </head>
      <body>
      </body> 
  </html>
  <script>  
    var dataset = [ 250 , 210 , 170 , 130 , 90 ];//数据(表示矩形的宽度)
      var width = 300;//画布的宽度
    var height = 300;//画布的高度
    var svg = d3.select("body")//选择文档中的body元素
      .append("svg")//添加一个svg元素
      .attr("width", width)//设定宽度
      .attr("height", height);//设定高度  
    var rectHeight = 25;//每个矩形所占的像素高度(包括空白)
    svg.selectAll("rect")
      .data(dataset)
      .enter()
      .append("rect")
      .attr("x",20)
      .attr("y",function(d,i){
          return i * rectHeight;
      })
      .attr("width",function(d){
          return d;
      })
      .attr("height",rectHeight-10)
      .attr("fill","steelblue");
  </script> 
(2)数据平放(有比例尺)
  <html> 
      <head> 
          <meta charset="utf-8"> 
          <title>Hello World</title> 
          <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> 
      </head>
      <body>
      </body> 
  </html>
  <script>  
    var dataset = [ 250 , 210 , 170 , 130 , 90 ];//数据(表示矩形的宽度)
    var width = 300;//画布的宽度
    var height = 300;//画布的高度
    var svg = d3.select("body")//选择文档中的body元素
      .append("svg")//添加一个svg元素
      .attr("width", width)//设定宽度
      .attr("height", height);//设定高度  
    var rectHeight = 25;//每个矩形所占的像素高度(包括空白)
    var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];
      var linear = d3.scale.linear()
          .domain([0, d3.max(dataset)])
          .range([0, 250]);
      var rectHeight = 25;//每个矩形所占的像素高度(包括空白)
      svg.selectAll("rect")
          .data(dataset)
          .enter()
          .append("rect")
          .attr("x",20)
          .attr("y",function(d,i){
              return i * rectHeight;
          })
          .attr("width",function(d){
              return linear(d);//在这里用比例尺
          })
          .attr("height",rectHeight-10)
          .attr("fill","steelblue");
  </script> 
4、数据立放
(1)数据立放(无比例尺)
  <html> 
      <head> 
          <meta charset="utf-8"> 
          <title>Hello World</title> 
          <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> 
      </head>
      <body>
      </body> 
  </html>
  <script>  
    var dataset = [ 250 , 210 , 170 , 130 , 90 ];//数据(表示矩形的宽度)
      var width = 300;//画布的宽度
    var height = 300;//画布的高度
    var svg = d3.select("body")//选择文档中的body元素
      .append("svg")//添加一个svg元素
      .attr("width", width)//设定宽度
      .attr("height", height);//设定高度  
    var rectHeight = 25;//每个矩形所占的像素高度(包括空白)
    svg.selectAll("rect")
      .data(dataset)
      .enter()
      .append("rect")
      .attr("x",function(d,i){
        return i * rectHeight;
      })
      .attr("y",function(d,i){
        return height - d;
      })
      .attr("width",rectHeight-10)
      .attr("height",function(d){
        return d;
      })
      .attr("fill","steelblue");
  </script> 
(2)数据立放(有比例尺)
  <html> 
      <head> 
          <meta charset="utf-8"> 
          <title>Hello World</title> 
          <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> 
      </head>
      <body>
      </body> 
  </html>
  <script>  
    var dataset = [ 250 , 210 , 170 , 130 , 90 ];//数据(表示矩形的宽度)
      var width = 300;//画布的宽度
    var height = 300;//画布的高度
    var svg = d3.select("body")//选择文档中的body元素
      .append("svg")//添加一个svg元素
      .attr("width", width)//设定宽度
      .attr("height", height);//设定高度  
    var rectHeight = 25;//每个矩形所占的像素高度(包括空白)
    var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];
      var linear = d3.scale.linear()
          .domain([0, d3.max(dataset)])
          .range([0, 250]);
      var rectHeight = 25;//每个矩形所占的像素高度(包括空白)
      svg.selectAll("rect")
          .data(dataset)
          .enter()
          .append("rect")
          .attr("x",function(d,i){
              return i * rectHeight;
          })
          .attr("y",function(d,i){
              return 250 - linear(d) ;
          })
          .attr("width",rectHeight-10)
          .attr("height",function(d){
              return linear(d);//在这里用比例尺
          })
          .attr("fill","steelblue");
  </script> 
5、饼图
  <!DOCTYPE html>
  <html>
    <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
      <title></title>
      <script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
      <script src="https://cdn.bootcdn.net/ajax/libs/d3/3.2.8/d3.js"></script>
      <script type="text/javascript">
        //准备数据
        var chartData = [
          { label: '漯河', value: 2 },
          { label: '舞阳', value: 3 },
          { label: '郭庄', value: 2 },
          { label: '北京', value: 3 },
          { label: '漯河3', value: 2 },
          { label: '北京3', value: 3 },
          { label: '漯河4', value: 2 },
          { label: '北京4', value: 3 },
          { label: '郭庄2', value: 4 }
        ];
        var colors = [
          '#2484c1',
          '#65a620',
          '#7b6888',
          '#a05d56',
          '#961a1a',
          '#d8d23a',
          '#e98125',
          '#d0743c',
          '#635222'
        ];
        var title = [
          {
            text: 'www.guowenke.tk',
            color: '#333333',
            fontSize: 18,
            font: 'arial'
          }
        ];
        //定义画布大小
        var canvasWidth = 600;
        var canvasHeight = 400;
        $(function () {
          var svg = d3
            .select('#pie')
            .append('svg')
            .attr('width', canvasWidth)
            .attr('height', canvasHeight);
          //标题
          svg
            .selectAll('.title')
            .data(title)
            .enter()
            .append('text')
            .text(function (d) {
              return d.text;
            })
            .attr('class', 'title')
            .attr('fill', function (d) {
              return d.color;
            })
            .style('font-family', function (d) {
              return d.font;
            })
            .attr('x', canvasWidth / 2)
            .attr('y', 30);
          //绘图
          //确定饼图中心
          var pieChartElement = svg
            .append('g')
            .attr(
              'transform',
              'translate(' + canvasWidth / 2 + ',' + canvasHeight / 2 + ')'
            );

          //创建弧生成器
          var arc = d3.svg
            .arc()
            .innerRadius(canvasHeight * 0)
            .outerRadius((canvasHeight * 0.8) / 3)
            .startAngle(0)
            .endAngle(function (d) {
              return (d.value / getSumData(chartData)) * 2 * Math.PI;
            });

          var arcs = pieChartElement
            .selectAll('g')
            .data(chartData)
            .enter()
            .append('g')
            .attr('transform', function (d, i) {
              var angle = 0;
              if (i > 0) {
                angle = getSegmentAngle(i - 1, chartData, getSumData(chartData));
              }
              return 'rotate(' + angle + ')';
            });
          arcs
            .append('path')
            .attr('fill', function (d, i) {
              return colors[i];//设定弧的颜色
            })
            .attr('d', function (d) {
              return arc(d);//使用弧生成器
            });

          //添加标签组
          var outerLabelGroupData = [];
          var lineCoordGroups = [];
          var labels = svg.append('g').attr('class', 'labels');
          var labelGroup = labels
            .selectAll('.labelGroup')
            .data(chartData)
            .enter()
            .append('g')
            .attr('class', 'labelGroup');
          labelGroup.append('text').text(function (d, i) {
            return d.label + ':' + d.value;
          });
          d3.selectAll('.labelGroup')
            .each(function (d, i) {
              var labelGroupDims = $('.labelGroup').get(i).getBBox();
              var angle = getSegmentAngle(i, chartData, getSumData(chartData), {
                midpoint: true
              });
              var originalX = canvasWidth / 2;
              var originalY = canvasHeight / 2 - ((canvasHeight * 0.8) / 3 + 3);
              var newCords = rotate(
                originalX,
                originalY,
                canvasWidth / 2,
                canvasHeight / 2,
                angle
              );
              if (angle > 180) {
                newCords.x -= labelGroupDims.width + 28;
              } else {
                newCords.x += 28;
              }
              outerLabelGroupData[i] = {
                x: newCords.x,
                y: newCords.y,
                w: labelGroupDims.width,
                h: labelGroupDims.height
              };
            })
            .attr('transform', function (d, i) {
              return (
                'translate(' +
                outerLabelGroupData[i].x +
                ',' +
                outerLabelGroupData[i].y +
                ')'
              );
            });
          d3.selectAll('.labelGroup').each(function (d, i) {
            var angle = getSegmentAngle(i, chartData, getSumData(chartData), {
              midpoint: true
            });
            var originalX = canvasWidth / 2;
            var originalY = canvasHeight / 2 - ((canvasHeight * 0.8) / 3 + 3);
            var newCords = rotate(
              originalX,
              originalY,
              canvasWidth / 2,
              canvasHeight / 2,
              angle
            );
            var labelXMargin = 6;
            var originCoords = newCords;
            var heightOffset = outerLabelGroupData[i].h / 5;
            var quarter = Math.floor(angle / 90);
            var midPoint = 4;
            var x2, y2, x3, y3;
            if (quarter === 2 && angle === 180) {
              quarter = 1;
            }
            switch (quarter) {
              case 0:
                x2 =
                  outerLabelGroupData[i].x -
                  labelXMargin -
                  (outerLabelGroupData[i].x - labelXMargin - originCoords.x) / 2;
                y2 =
                  outerLabelGroupData[i].y +
                  (originCoords.y - outerLabelGroupData[i].y) / midPoint;
                x3 = outerLabelGroupData[i].x - labelXMargin;
                y3 = outerLabelGroupData[i].y - heightOffset;
                break;
              case 1:
                x2 =
                  originCoords.x +
                  (outerLabelGroupData[i].x - originCoords.x) / midPoint;
                y2 =
                  originCoords.y +
                  (outerLabelGroupData[i].y - originCoords.y) / midPoint;
                x3 = outerLabelGroupData[i].x - labelXMargin;
                y3 = outerLabelGroupData[i].y - heightOffset;
                break;
              case 2:
                var startOfLabelX =
                  outerLabelGroupData[i].x +
                  outerLabelGroupData[i].w +
                  labelXMargin;
                x2 = originCoords.x - (originCoords.x - startOfLabelX) / midPoint;
                y2 =
                  originCoords.y +
                  (outerLabelGroupData[i].y - originCoords.y) / midPoint;
                x3 =
                  outerLabelGroupData[i].x +
                  outerLabelGroupData[i].w +
                  labelXMargin;
                y3 = outerLabelGroupData[i].y - heightOffset;
                break;
              case 3:
                var startOfLabel =
                  outerLabelGroupData[i].x +
                  outerLabelGroupData[i].w +
                  labelXMargin;
                x2 = startOfLabel + (originCoords.x - startOfLabel) / midPoint;
                y2 =
                  outerLabelGroupData[i].y +
                  (originCoords.y - outerLabelGroupData[i].y) / midPoint;
                x3 =
                  outerLabelGroupData[i].x +
                  outerLabelGroupData[i].w +
                  labelXMargin;
                y3 = outerLabelGroupData[i].y - heightOffset;
                break;
            }
            lineCoordGroups[i] = [
              { x: originCoords.x, y: originCoords.y },
              { x: x2, y: y2 },
              { x: x3, y: y3 }
            ];
          });
          var lineGroups = svg
            .append('g')
            .attr('class', 'lineGroups')
            .style('opacity', 1);

          var lineGroup = lineGroups
            .selectAll('.lineGroup')
            .data(lineCoordGroups)
            .enter()
            .append('g')
            .attr('class', 'lineGroup');
          var lineFunction = d3.svg
            .line()
            .interpolate('basis')
            .x(function (d) {
              return d.x;
            })
            .y(function (d) {
              return d.y;
            });
          lineGroup
            .append('path')
            .attr('d', lineFunction)
            .attr('stroke', function (d, i) {
              return colors[i];
            })
            .attr('stroke-width', 1)
            .attr('fill', 'none');
          function rotate(x, y, xm, ym, a) {
            a = (a * Math.PI) / 180;// 转为弧度
            var cos = Math.cos,
              sin = Math.sin,
              xr = (x - xm) * cos(a) - (y - ym) * sin(a) + xm,
              yr = (x - xm) * sin(a) + (y - ym) * cos(a) + ym;
            return { x: xr, y: yr };
          }
          function getSumData(data) {
            var sumData = 0;
            var countData = data.length;
            for (var i = 0; i < countData; i++) {
              sumData += data[i].value;
            }
            return sumData;
          }
          function getSegmentAngle(index, data, sumValue, opts) {
            var options = $.extend(
              {
                compounded: true,
                midpoint: false
              },
              opts
            );
            var currValue = data[index].value;
            var fullValue;
            fullValue = 0;
            for (var i = 0; i <= index; i++) {
              fullValue += data[i].value;
            }
          //值转为角度
            var angle = (fullValue / sumValue) * 360;
            if (options.midpoint) {
              var currAngle = (currValue / sumValue) * 360;
              angle -= currAngle / 2;
            }
            return angle;
          }
        });
      </script>
    </head>
    <body>
      <div id="pie"></div>
    </body>
  </html>
6、拓扑图
  <!DOCTYPE>
  <html>
    <meta charset="utf-8">
    <title>Radial Dendrogram</title>
    <link rel="stylesheet" type="text/css" href="https://unpkg.com/@observablehq/notebook-inspector@1/dist/notebook-inspector-style.css">
    <body>
      <div id="console" style="width: 100%;"></div>
    </body>
  </html>
  <script type="module">
    import { Inspector, Runtime } from "https://unpkg.com/@observablehq/notebook-runtime@1?module";
    //URL: https://observablehq.com/@13716164418/radial-dendrogram
    const m0 = {
      id: "myId",
      variables: [
        {
          inputs: ["md"],
          value: (function (md) {
            return md`# Radial Dendrogram`
          })
        },
        {
          name: "chart",
          inputs: ["tree", "d3", "data", "DOM", "width"],
          value: (function (tree, d3, data, DOM, width) {
            const root = tree(d3.hierarchy(data)
              .sort((a, b) => (a.height - b.height) || a.data.name.localeCompare(b.data.name)));
            const svg = d3.select(DOM.svg(width, width))
              .style("width", "100%")
              .style("height", "auto")
              .style("padding", "10px")
              .style("box-sizing", "border-box")
              .style("font", "10px sans-serif");
            const g = svg.append("g");
            const link = g.append("g")
              .attr("fill", "none")
              .attr("stroke", "#555")
              .attr("stroke-opacity", 0.4)
              .attr("stroke-width", 1.5)
              .selectAll("path")
              .data(root.links())
              .enter().append("path")
              .attr("d", d3.linkRadial().angle(d => d.x).radius(d => d.y));
            const node = g.append("g")
              .attr("stroke-linejoin", "round")
              .attr("stroke-width", 3)
              .selectAll("g")
              .data(root.descendants().reverse())
              .enter().append("g")
              .attr("transform", d => `rotate(${d.x * 180 / Math.PI - 90}) translate(${d.y},0)`);
            node.append("circle")
              .attr("fill", d => d.children ? "#555" : "#999")
              .attr("r", 2.5);
            node.append("text")
              .attr("dy", "0.31em")
              .attr("x", d => d.x < Math.PI === !d.children ? 6 : -6)
              .attr("text-anchor", d => d.x < Math.PI === !d.children ? "start" : "end")
              .attr("transform", d => d.x >= Math.PI ? "rotate(180)" : null)
              .text(d => d.data.name)
              .filter(d => d.children)
              .clone(true).lower()
              .attr("stroke", "white");
            document.body.appendChild(svg.node());
            const box = g.node().getBBox();
            svg.remove()
              .attr("width", box.width)
              .attr("height", box.height)
              .attr("viewBox", `${box.x} ${box.y} ${box.width} ${box.height}`);
            return svg.node();
          })
        },
        {
          name: "data",
          inputs: ["require"],
          value: (function (require) {
            return require("@observablehq/flare")
          })
        },
        {
          name: "width",
          value: (function () {
            return 932
          })
        },
        {
          name: "radius",
          inputs: ["width"],
          value: (function (width) {
            return width / 2
          })
        },
        {
          name: "tree",
          inputs: ["d3", "radius"],
          value: (function (d3, radius) {
            return d3.cluster().size([2 * Math.PI, radius - 100])
          })
        },
        {
          name: "d3",
          inputs: ["require"],
          value: (function (require) {
            return require("d3@5")
          })
        }
      ]
    };
    const notebook = {
      id: "myId",
      modules: [m0]
    };
    export default notebook;
    Runtime.load(notebook, Inspector.into(document.getElementById("console")));
  </script>

三、ES6之基础
  来源,http://caibaojian.com/es6
1、Set,唯一值数组,ES6提供的新的数据结构,类似于数组,但是成员的值都是唯一的,没有重复的 
  (1)去重
    Array.from(new Set([1,2,3,3,1,4])); //[1, 2, 3, 4]
  (2)并集
    var a = new Set([1, 2, 3]);
    var b = new Set([4, 3, 2]);
    var union = new Set([...a, ...b]); //{1, 2, 3, 4}
  (3)交集
    var a = new Set([1, 2, 3]);
    var b = new Set([4, 3, 2]);
    var intersect = new Set([...a].filter(x => b.has(x))); //{2, 3}
  (4)删除
    var list = new Set([1,20,30,40]) 
    list.delete(30) //删除值为30的元素,这里的30并非下标
  (5)清除所有元素
    var list = new Set([1,2,3,4])
    list.clear()
  (6)添加元素add
    var list=new Set();
    list.add(1)
    list.add(2).add(3).add(3) //2只被添加了一次
2、Map,任意键对象,ES6提供的新的数据结构,类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键 
  (1)示例1,
    var obj = new Map()
    obj.set(0, "attack_type") //键是数值
    obj.set("age", 20) //键是字符串
    obj.set(undefined, "definition") //键是undefined
  (2)示例2,
    var obj = new Map([['name', '张三'], ['age', 18], ['sex', '男']])
    obj.size //3
3、Symbol,ES6提供的新的数据类型
  (1)原生函数,String()、Number()、Boolean()、Array()、Object()、Function()、RegExp()、Date()、Error()、Symbol()
  (2)原始数据类型,未定义(undefined)、空值(null)、布尔值(boolean)、字符串(string)、数值(number)、对象(object)、符号(symbol)
  (3)ES5的对象属性名都是字符串,这容易造成属性名冲突的问题
  (4)symbol表示独一无二的值,"真实值"无法获取,Symbol类型没有字面量,感觉类似于对象的实例
  (5)示例1,A页面和B页面的变量名有可能相同,造成覆盖,因此Symbol的作用不大
    var aaa = Symbol('foo'); //A页面
    var bbb = Symbol('foo'); //B页面
    console.log(aaa === bbb); //false
  (5)示例2,
    let ccc = Symbol.for('foo'); //创建新符号
    let ddd = Symbol.for('foo'); //重用已有符号
    console.log(ccc === ddd); //true
  (6)示例3,
    var s1 = Symbol('foo');
    var s2 = Symbol('bar');
    var s3 = Symbol('baz');
    var s4 = Symbol('qux');
    var sss = { //[属性],会对属性进行读取,并且转换成字符串。[s1]是读取了Symbol的字符串键'foo'
      [s1]: 'foo val'
    };
    //或 sss[s1] = 'foo val';
    console.log(sss); // { [Symbol(foo)]: 'foo val' }
4、解构赋值
  var person = {name: 'zhangsan', address: {province: '江苏', city: '南京'}};
  var {name, address:{province, city}}=person;
  console.log(name, province, city);
5、var、let、const的区别 
  (1)块级,var不是块级作用域,let和const是块级{}作用域
    示例1, 
      var b=[];
      for(var j=0;j<10;j++){
        b[j]=function(){
          console.log(j);
        };
      }
      b[6]();
    示例2,
      var a=[];
      for(let i=0;i<10;i++){
        a[i]=function(){
          console.log(i);
        };
      }
      a[6]();
    示例3,
      var a=[];
      for(const i=0;i<10;i++){
        a[i]=function(){
          console.log(i);
        };
      }
      a[6]();
  (2)提声,var不存在暂时性死区、有变量提声、声明前可用,let和const,存在暂时性死区、没有变量提声、声明前不可用
    附、变量提升,将变量声明提升到它所在作用域的最开始的部分,通过var定义(声明)的变量,在定义语句之前就可以访问到
    示例1,
      console.log(a); //undefined
      var a = 1;
    示例2,
      console.log(a); //a is not defined
    示例3,
      console.log(a)
      let a = 1;
    示例4,
      console.log(a)
      const a = 1;
  (3)重声,var允许重复声明变量,let和const不允许重复声明变量
    示例1,
      var a = 1;
      var a = 1;
      console.log(a)
    示例2,
      let a = 1;
      let a = 1;
      console.log(a)
    示例3,
      const a = 1;
      const a = 1;
      console.log(a)
  (4)初始值,var和let可以不设置初始值、可以重新赋值,const必须设置初始值、不能重新赋值
    示例1,
      var a;
      a = 1;
      a = 2;
      console.log(a)
    示例2,
      let a;
      a = 1;
      a = 2;
      console.log(a)
    示例3,
      const a;
      a = 1;
      a = 2;
      console.log(a)
6、箭头函数的this
  注意:宿主对象的this,即箭头函数的this
  示例一
  var value = 6;
  var obj = {
    value: 5,
    fn: function fnIn() {
      setTimeout(() => {
        console.log(this.value); //5,宿主对象为fnIn,其this为obj
      });
    },
  };
  obj.fn();
  示例二
  var value = 6;
  var obj = {
    value: 5,
    fn: () => {
      console.log(this.value); //6,宿主对象为obj,其this为window
    },
  };
  obj.fn();
7、箭头函数和普通函数的区别(new、arguments、this、call、apply、prototype、Generator、yield),
  (1)箭头函数作为匿名函数,是不能作为构造函数的,不能使用new,
  (2)箭头函数不绑定arguments,取而代之用rest参数…解决,
  (3)箭头函数会捕获其所在上下文的 this 值,作为自己的 this 值,即箭头函数所在对象的 this,
  (4)使用call()和apply()调用只是传入了参数而已,对 this 并没有什么影响,
  (5)箭头函数没有原型属性prototype,
  (6)箭头函数不能当做Generator函数,不能使用yield关键字,
  (7)箭头函数参数:0或多个参数,需要用();1个参数,不需要(),
  (8)箭头函数函数体:多个分句,用{}和return;1个分句,不用{}和return;1个分句,返回值为对象,用({})。
8、普通函数与箭头函数对照
  (1)普通函数:function fn(){return 5}
    箭头函数:var fn=()=>{return 5}
  (2)普通函数:function fn(v){return 5}
    箭头函数:var fn=(v)=>v
  (3)普通函数:map(function(){return 5})
    箭头函数:map(()=>5)
  (4)普通函数:map(function(v){return 5})
    箭头函数:map(v=>5)
  附:var 变量=(参数)=>函数体
9、模板字符串
(1)在模板字符串html中,用`<div>${ /* 注释内容 */'' }</div>`加注释
(2)在模板字符串js中,用`${变量}`加变量

四、ES6之Proxy(内部拦截)
1、Proxy实例(Proxy必须针对Proxy实例进行操作,否则Proxy不起作用)
(1)实例一
  var obj = {};
  var thisObj = new Proxy(obj, {
    get: function (target, key, receiver) {
      console.log(obj === target); //true
      if (key === 4) {
        return 5;
      }
      return 6;
    },
    set: function (target, key, value, receiver) {
      return Reflect.set(target, key, value, receiver);
    },
    has: function (target, key) {
      if (key[0] === "c") {
        return false;
      }
      return key in target;
    },
  });
  thisObj.count = 1;
  console.log(thisObj.count);
  console.log("count" in thisObj);
(2)实例二
  var fn=function(){ };
  var thisFn = new Proxy(fn,{
    construct:function(target,args){
      console.log(target===fn)//true
      return { value: args[0]*10 }//construct方法返回的必须是一个对象,否则会报错。
    }
  });
  console.log(new thisFn(1));
  console.log(Reflect.construct(thisFn,[1]));
2、Proxy配置
(1)get(target, propKey, receiver)
  拦截对象属性的读取,比如proxy.starFunction 和 proxy['starFunction']。
  如果propKey属性部署了读取函数,则读取函数的this绑定receiver(一个对象)。
(2)set(target, propKey, value, receiver)
  拦截对象属性的设置,比如proxy.starFunction = v或proxy['starFunction'] = v,返回一个布尔值。
(3)has(target, propKey)
  拦截propKey in proxy的操作,以及对象的hasOwnProperty方法,返回一个布尔值。
(4)deleteProperty(target, propKey)
  拦截delete proxy[propKey]的操作,返回一个布尔值。
(5)ownKeys(target)
  拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy),返回一个数组。
  该方法返回对象所有自身的属性,而Object.keys()仅返回对象可遍历的属性。
(6)getOwnPropertyDescriptor(target, propKey)
  拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
(7)defineProperty(target, propKey, propDesc)
  拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
(8)preventExtensions(target)
  拦截Object.preventExtensions(proxy),返回一个布尔值。
(9)getPrototypeOf(target)
  拦截Object.getPrototypeOf(proxy),返回一个对象。
(10)isExtensible(target)
  拦截Object.isExtensible(proxy),返回一个布尔值。
(11)setPrototypeOf(target, proto)
  拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。
  如果目标对象是函数,那么还有两种额外操作可以拦截。
(12)apply(target, object, args)
  拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...) 。
(13)construct(target, args)
  拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
另外,vue2用Object.defineProperty实现双向绑定,vue3用Proxy实现双向绑定
 
五、ES6之Reflect(外部操纵)
1、Reflect概述
  Reflect对象与Proxy对象一样,也是ES6为了操作对象而提供的新API。Reflect对象的设计目的有这样几个。
  (1)将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。
  (2)修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。
  (3)让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。
  (4)Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。
2、Reflect方法
(1)Reflect.get(target, name, receiver)
  查找并返回target对象的name属性,如果没有该属性,则返回undefined。
  如果name属性部署了读取函数,则读取函数的this绑定receiver。
  var antzone = {
    webName: "蚂蚁部落",
    url:"www.softwhy.com",
    get age() { return this.myAge; }
  }
    console.log(Reflect.get(antzone, "age", { myAge: 4 }));
(2)Reflect.set(target, name, value, receiver)
  设置target对象的name属性等于value。如果name属性设置了赋值函数,则赋值函数的this绑定receiver。
(3)Reflect.has(obj, name)
  等同于name in obj。
(4)Reflect.deleteProperty(obj, name)
  等同于delete obj[name]。
(5)Reflect.construct(target, args)
  等同于new target(...args),这提供了一种不使用new,来调用构造函数的方法。
  function func1(a, b, c) {
    this.sum = a + b + c;
  }
  const args = [1, 2, 3];
  const object1 = new func1(...args);
  const object2 = Reflect.construct(func1, args);
  console.log(object2.sum);//expected output: 6
  console.log(object1.sum);//expected output: 6
(6)Reflect.getPrototypeOf(obj)
  读取对象的__proto__属性,对应Object.getPrototypeOf(obj)。
(7)Reflect.setPrototypeOf(obj, newProto)
  设置对象的__proto__属性,对应Object.setPrototypeOf(obj, newProto)。
(8)Reflect.apply(fun,thisArg,args)
  等同于Function.prototype.apply.call(fun,thisArg,args)。一般来说,如果要绑定一个函数的this对象,可以这样写fn.apply(obj, args),但是如果函数定义了自己的apply方法,就只能写成Function.prototype.apply.call(fn, obj, args),采用Reflect对象可以简化这种操作。
  console.log(Reflect.apply(Math.floor, undefined, [1.75]));//expected output: 1
 
六、ES6之Iterator(遍历器)
  //以下遍历器(迭代器)模拟代码
  function createIterator(items) {
    var i = 0;
    return {
        next: function() {
            var done = (i >= items.length);
            var value = done ? undefined : items[i++] ;
            return {
                done: done,
                value: value
            };
        }
    };
  }
  var iterator = createIterator([1, 2, 3]);
  console.log(iterator.next()); //"{ value: 1, done: false }"
  console.log(iterator.next()); //"{ value: 2, done: false }"
  console.log(iterator.next()); //"{ value: 3, done: false }"
  console.log(iterator.next()); //"{ value: undefined, done: true }"
  JavaScript原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6又添加了Map和Set。这样四种数据结构只要部署了Iterator接口,就是”可遍历的“(iterable)。ES6规定,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。
  const obj = {
    [Symbol.iterator] : function () {
      return {
        next: function () {
          return {
            value: 1,
            done: true
          };
        }
      };
    }
  };
  上面代码中,对象obj是可遍历的(iterable),因为具有Symbol.iterator属性。
 
七、ES6之Generator 函数(生成器)
1、Generator函数有两个特征
(1)function关键字与函数名之间有一个星号;
(2)函数体内部使用yield(即“产出”)语句,定义不同的内部状态。
2、调用Generator函数后,该函数并不执行,返回的是一个指向内部状态的指针对象,也就是遍历器对象(Iterator Object)。
3、调用遍历器对象的next方法,使得指针移向下一个状态。
4、也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield语句(或return语句)为止。
5、换言之,Generator函数是分段执行的,yield语句是暂停执行的标记,而next方法可以恢复执行。
6、示例1
  function* starFunction(one) {
    console.log(one);
    var two = 2 * (yield one + 1); //6,7
    console.log("1111111111111111111111111111111");
    console.log(two);
    var three = yield 2 * two; //4,8
    console.log("2222222222222222222222222222222");
    console.log(three);
    var four = yield 2 * three; //2,4
    console.log("3333333333333333333333333333333");
    console.log(four);
    console.log(one, two, three, four);
    console.log("4444444444444444444444444444444");
    return one + two + three + four;
  }
  var useFunction = starFunction(6);
  console.log(useFunction.next());
  console.log(useFunction.next(2));
  console.log(useFunction.next(2));
  console.log(useFunction.next(2));
  next第1次执行,返回“第1个yield语句的计算结果”;
  next第2次执行,返回“第2个yield语句的计算结果”,传入的参数取代“第1个yield语句的计算结果”;
  next第3次执行,返回“第3个yield语句的计算结果,传入的参数取代“第2个yield语句的计算结果””;
  next第n次执行时,如果没有第n个yield语句,那就一直执行到return,如果没有return,就返回undefined。
7、示例2  
  来源:http://www.ruanyifeng.com/blog/2015/04/generator.html
  Fetch模块返回的是一个Promise对象,因此要用then方法调用下一个next方法。
  var fetch = require('node-fetch');
  function* generatorFn(){
    var urlA = 'https://api.github.com/users/githubA';
    var urlB = 'https://api.github.com/users/githubB';
    yield fetch(urlA);
    yield fetch(urlB);
  }
  var generatorOne = generatorFn();
  var result = generatorOne.next();
  result.value.then(function(data){
    return data.json();
  }).then(function(data){
    generatorOne.next(data);
  });
  8、如果想要第一次调用next方法时,就能够输入值,可以在Generator函数外面再包一层。如下
  function wrapper(generatorFunction) {
    return function (num) {
      let generatorObject = generatorFunction(num);
      generatorObject.next();
      return generatorObject;
    };
  }
  const wrapped = wrapper(function* starFunction(a) {
    console.log(a);
    var b = 2 * (yield a + 1); //6,7
    console.log("1111111111111111111111111111111");
    console.log(b);
    var c = yield 2 * b; //4,8
    console.log("2222222222222222222222222222222");
    console.log(c);
    var d = yield 2 * c; //2,4
    console.log("3333333333333333333333333333333");
    console.log(d);
    console.log(a, b, c, d);
    console.log("4444444444444444444444444444444");
    return a + b + c + d;
  });
  var b = wrapped(6);
  console.log(b.next(2));
  console.log(b.next(2));
  console.log(b.next(2));
 
八、ES6之async(then)函数及其内部的await(promise)指令
(1)嵌套回调、Promise、Generators不够优雅,
(2)从外向里,使用async-await-Promise-resolve,resolve的参数是await的返回值,async的返回值是async的then参数的参数
(3)await语句只能在async函数内部使用,该语句的异步执行结束后,才能执行下面的语句
  示例一、
    function thisPromise(x) {
      return new Promise(function (resolve) {
        //此处把x(加工后)作为参数向后台发送请求
        setTimeout(function () {
          //此处获得后台返回结果并加工为x+50,通过resolve暴露出去
          resolve(x + 50);
        }, 500);
      });
    }
    async function thisAdd(x) { //var thisAdd = async function(x) {
      var a = await thisPromise(20);
      var b = await thisPromise(30);
      //此处可以加工a,b
      return x + a + b; //这个结果作为参数传给后面的success
    }
    thisAdd(10).then(function (total) {
      console.log(total); //1秒后,出打印结果160
    });
  示例二、
    router.beforeEach(async(to, from, next) => {
        if (getToken()) {
            if (to.path === '/login') {
                next({ path: '/' });
            } else {
                if (userInfo) {
                    next();
                } else {
                    try {
                        await userStore.getInfo();
                    } catch (error) {
              
                    }
                }
            }
        } else {
        
        }
    });
  
九、ES6中模块规范(在前端模块化开发中使用)
1、默认导出和引入,export default,1个文件只能有1个默认导出
  (1)先定义,再默认导出
    function ABC(){};
    export default ABC;
  (2)直接默认导出
    export default function /*ABC*/(){};
  (3)上面对应的import引入为
    import DEF from "./abc"
2、普通导出和引入,export
  (1)先定义,再普通导出
    var abc="abc";
    function ABC(){};
    export {acb,ABC}
  (2)直接普通导出
    export var abc="abc";
    export function ABC(){};
  (3)上面对应的import引入为
    import {abc,ABC} from "./abc" 
  (4)通配符导入
    import * as echarts from 'echarts';相当于 
    import { a, b, c, d } from 'echarts'
3、混合导出和引入    
  (1)默认和普通,分别导出    
  (2)默认和普通,同时引入
    import DEF,{abc,ABC} from "./def" 指的是,def.js模块默认导出了abc,普通导出了ABC 
4、改名导出和引入
  (1)定义
    var abc="abc";
    function ABC(){};
    export {abc as abc1, ABC as ABC1}
  (2)引入
    import {abc1,ABC1} from "./abc";
    import {abc1 as abc2, ABC1 as ABC2} from "./abc";

十、AMD、CMD、UMD、CommonJS模块规范
1、AMD和require.js,AMD依赖模块加载跳过所有异步,运行后面的回调函数,运行中所有依赖模块的语句,都定义在另一个回调函数中,等到加载完成之后,这个回调函数才会运行。
  (1)定义模块
    define("abc", ["a", "b", "c", 'd'], function (a, b, c, d) {
      var a = require('a');
    });
  (2)引用模块
    require(['abc','def'],function(abc,def){});
2、CMD和sea.js,在回调函数里,需要某个依赖,才会加载这个依赖。
  (1)定义模块
    define("abc", ["a", "b", "c", 'd'], function (require, exports, module) {
      var a = require('a');
    });
  (2)引用模块
    require("abc")
3、UMD用于判断代码运行环境
  (1)UMD:Universal Module Definition,通用模块定义
  (2)UMD实例
    (function fn(root, factory) {
      if(typeof exports === 'object' && typeof module === 'object'){//非原生commonjs环境下,如nodejs环境
        module.exports = factory();
      }else if(typeof define === 'function' && define.amd){//AMD环境下,如requirejs加载器
        define([], factory);
      }else if(typeof define === "function" && define.cmd){//CMD环境下,如seajs加载器,一个模块就是一个文件
        define([], factory);
      }else if(typeof exports === 'object'){//原生commonjs环境下
        exports["aaa"] = factory();
      }else if(typeof self === 'object'){//使用了web worker技术的服务器环境,不会影响页面的性能。
        self["aaa"] = factory();
      }else if(typeof global === 'object'){//如果所有的全局环境变量都统一为global
        self["aaa"] = factory();
      }else{//其它环境下,如window
        root["aaa"] = factory();
      }       
    })(this, function() {})
4、CommonJS中模块规范(在后台nodejs开发中使用,在前端框架处理兼容时使用)
  (1)导出模块 module.exports = obj;
  (2)导入模块 var example = require('./example.js'); var area = example.area;//使用模块
  (3)commonJS 规范主要特点
    来源,https://blog.csdn.net/jieyucx/article/details/131548528
    A、模块化组织代码:CommonJS 允许将代码按功能或逻辑分类成独立的模块,每个模块只负责特定的功能,使代码更加可维护和可复用。
    C、隔离命名空间:每个模块都有自己独立的作用域,不会与其他模块中的变量和函数冲突,有效避免命名冲突。
    C、代码加载管理:CommonJS 规范提供了模块的加载和缓存机制,可以确保模块只会被加载一次,避免重复加载和执行,提高性能。
    D、跨平台使用:CommonJS 规范不限于在浏览器中使用,也可以在其他 JavaScript 运行环境中使用,如 Node.js 等。
5、commonjs和esmodule的区别
   注、2019年发布的node13.2.0开始支持ES6模块,.vue文件被vue-loader转化为ES6模块
  (1)语法不同
    A、CommonJS使用require和module.exports进行模块导入和导出;exports有s
    B、ESModule使用import和export关键字进行模块导入和导出;export没有s
  (2)动态导入不同
    A、CommonJS不支持动态导入
    B、ESModule支持动态导入,可以在代码运行时根据需要导入模块
  (3)作用域不同
    A、CommonJS的模块作用域是动态的,模块中定义的变量和函数会被添加到全局作用域
    B、ESModule的模块作用域是静态的,模块中定义的变量和函数不会污染全局作用域
  (4)异步加载不同
    A、CommonJS只能同步加载模块
    B、ESModule可以异步加载模块,以提高性能和减少启动时间
  (5)循环依赖处理不同
    A、CommonJS可能会因为循环依赖导致程序崩溃
    B、ESModule可以处理循环依赖
  (6)浏览器兼容性不同
    A、CommonJS可以在所有现代浏览器和Node.js中使用
    B、ESModule在现代浏览器中得到广泛支持,但在旧版浏览器中可能无法使用
  (7)导出方式不同
    A、CommonJS导出的是一个值的拷贝,会对加载结果进行缓存,内部再修改这个值不会同步到外部;
    B、ESModule导出的是一个引用,内部修改可以同步到外部
  (8)加载方式不同
    A、CommonJS加载的是整个模块,将所有的接口全部加载进来
    B、ESModule可以单独加载其中的某个接口
 
十一、TS泛型(Generics)是指在定义函数、接口或类的时候,不预先指定参数和返回值的具体类型,而在使用的时候再指定类型的一种特性。
1、泛型函数,泛型函数与非泛型函数没什么不同,只是有一个类型参数在最前面,像函数声明一样
(1)泛型函数的普通类型
  function createArray<T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
      result[i] = value;
    }
    return result;
  }
  createArray<string>(3, 'x'); //['x', 'x', 'x']
(2)泛型函数的默认类型
  function createArray<T = string>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
      result[i] = value;
    }
    return result;
  }
2、泛型接口
(1)泛型接口写法一
  interface CreateArrayFunc {
    <T>(length: number, value: T): Array<T>;
  }
(2)泛型接口写法二
  interface CreateArrayFunc<T> {
    (length: number, value: T): Array<T>;
  }
(3)使用接口
  let createArray: CreateArrayFunc;
  createArray = function<T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
      result[i] = value;
    }
    return result;
  }
  createArray(3, 'x'); //['x', 'x', 'x']
3、泛型类
(1)TypeScript的核心原则之一是对值进行类型检查
(2)在TypeScript里,接口的作用就是为这些类型命名和为代码定义契约
(3)https://www.tslang.cn/docs/handbook/interfaces.html
(4)泛型类普通写法
  class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
  }
  let myGenericNumber = new GenericNumber<number>();
  myGenericNumber.zeroValue = 0;
  myGenericNumber.add = function(x, y) { return x + y; };
(5)泛型约束
  interface Lengthwise {
    length: number;
  }
  function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
  }
(6)对泛型进行约束,只允许这个函数传入那些包含 length 属性的变量
(7)我们定义一个接口来描述约束条件。 创建一个包含 .length属性的接口,使用这个接口和extends关键字来实现约束

十二、ES6和TS的类和实例
1、ES6的属性、方法、继承
  (1)在类中定义的属性,是实例的属性。相当于ES5中实例的构造函数。
  (2)在类中定义的属性前,加上static,那么该属性就是静态属性即类本身的属性,通过类名.aaa来获取。
  (3)在类中定义的方法,是实例的方法。相当于ES5中实例的原型。
  (4)在类中定义的方法前,加上static,那么该方法就是静态方法即类本身的方法,通过类名.aaa来调用。方法可以被子类继承,通过子类名.aaa来调用,里面的this指的是类而不是实例。
  (5)ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面。
  (6)ES6的继承,实质是先创造父类的实例对象this,然后再用子类的构造函数修改this。子类内部的super是父类的构造函数,必须首先调用。
  (7)static的内容,在ES5中,只能写在类AAA的外部,通过AAA.bbb定义、获取、调用。
    //以下实例必须在浏览器控制台里运行,不能在node环境里运行
    class Parent {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
      getX() {
        console.log("this为Parent实例,实例的getX方法被调用,实例的x为:" + this.x);
      }
      static name = "animal";
      static age = "20";
      static getY() {
        console.log("this为Parent类,类的getY方法被调用,类的name为:" + this.name);
      }
    }
    class Son extends Parent {
      constructor(...props) {
        super(...props);
        this.z = 4;
      }
    }
    var parent = new Parent(2, 3);
    var son = new Son(2, 3);
    parent.getX();
    Parent.getY();
    son.getX();
    Son.getY();
    console.log(parent.x, parent.y);
    console.log(Parent.name, Parent.age);
    console.log(son.x, son.y, son.z);
2、TS的类和实例
  知识来源,https://www.tslang.cn/docs/handbook/classes.html
  代码运行,https://www.json.cn/runcode/run_typescript/
(1)公共public:类内可访问、子类内可访问、实例可访问、子实例可访问
(2)受保protected:类内可访问、子类内可访问
(3)私有private:类内可访问
  class Person {
    public pubName: string;
    protected proName: string;
    /* protected */ constructor(pubName: string,proName: string) {
        this.pubName = pubName;
        this.proName = proName;
    };
  }
  class Employee extends Person {
    public departmentA: string;
    protected departmentB: string;
    private departmentC: string;
    constructor(pubName: string,proName: string, department: string) {
      super(pubName,proName);
      this.departmentA = department;
      this.departmentB = department;
      this.departmentC = department;
      console.log(this.departmentC+"A:类内可访问,private")
      console.log(this.departmentB+"B:类内可访问,protected")
      console.log(this.proName+":子类可访问,protected")
      console.log(this.departmentA+"C:类内可访问,public")
      console.log(this.pubName+":子类可访问,public")
    }
  }
  console.log(new Employee("姓名人A","姓名人B", "售货员").departmentA+"D:实例可访问,public")
  console.log(new Employee("姓名人A","姓名人B", "售货员").pubName+"D:实例可访问,public")

十三、jQuery知识
1、四种宽
  (1)width():其宽度范围是所匹配元素的宽度width;
  (2)innerWidth():其宽度范围是所匹配元素的宽度width+padding;
  (3)outerWidth():其宽度范围是所匹配元素的宽度width+padding+border;
  (4)outerWidth(true)其宽度范围是所匹配元素的宽度width+padding+border+margin;
2、attr和prop
   附、总说明
    A、2011年,jQuery.1.6.1发布,在本版本及以后的版本中
    B、设置和获取-自定义属性用attr
    C、设置和获取-固有属性用prop
    D、其它的不要深究
  (1)低版本
    <!DOCTYPE html>
    <html>
      <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>jquery.1.5.1及以前</title>
        <script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.5.1/jquery.js"></script>  
      </head>
      <body>
        <pre>
          <h3><input type="checkbox" id="check" checked=""  aaa="aaa"/>jquery.1.5.1及以前</h3>
            input type="checkbox" id="check" aaa="aaa" checked=""
            1、checked="",初始化时,不管如何赋值,只要出现,就是选中
            2、attr可以设置、获取固有属性、自定义属性
              (1)设置固有属性,
                $('#check').attr('checked',false)//不选中
                $('#check').attr('checked','')//不选中
                $('#check').attr('checked','111')//选中
                $('#check').attr('checked',true)//选中
              (2)获取固有属性,
                console.log($('#check').attr('checked'));//true
                console.log($('#check').attr('checked',false).attr('checked'));//false
                console.log($('#check').attr('checked','').attr('checked'));//false
                console.log($('#check').attr('checked','111').attr('checked'));//true
                console.log($('#check').attr('checked',true).attr('checked'));//true
              (3)获取自定义属性,console.log($('#check').attr('aaa'));//aaa
            3、prop不存在
              (1)console.log($('#check').prop);//undefined
        </pre>
      </body>
    </html>
    <script type="text/javascript">
      console.log($('#check').attr('checked'));//true
      console.log($('#check').attr('checked',false).attr('checked'));//false
      console.log($('#check').attr('checked','').attr('checked'));//false
      console.log($('#check').attr('checked','111').attr('checked'));//true
      console.log($('#check').attr('checked',true).attr('checked'));//true
    </script>  
  (2)高版本
    <!DOCTYPE html>
    <html>
      <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>jquery.1.6.1及以后</title>
        <script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.6.1/jquery.js"></script>    
      </head>
      <body>
        <pre>
          <h3><input type="checkbox" id="check" checked=""  aaa="aaa"/>jquery.1.6.1及以后</h3>
            input type="checkbox" id="check" aaa="aaa" checked=""
            1、checked="",不管如何赋值,只要出现,就是选中
            2、attr可以设置、获取自定义属性、固有属性;
              (1)设置固有属性,
                $('#check').attr('checked',false)//不选中
                $('#check').attr('checked','')//选中
                $('#check').attr('checked','111')//选中
                $('#check').attr('checked',true)//选中
              (2)获取固有属性,
                console.log($('#check').attr('checked'));//checked
                console.log($('#check').attr('checked',false).attr('checked'));//undefined
                console.log($('#check').attr('checked','').attr('checked'));//undefined
                console.log($('#check').attr('checked','111').attr('checked'));//undefined
                console.log($('#check').attr('checked',true).attr('checked'));//checked
              (3)获取自定义属性,
                console.log($('#check').attr('aaa'));//aaa
            3、prop可以设置、获取固有属性;
              (1)设置固有属性,
                $('#check').prop('checked',false)//不选中
                $('#check').prop('checked','')//不选中
                $('#check').prop('checked','111')//选中
                $('#check').prop('checked',true)//选中
              (2)获取固有属性,
                console.log($('#check').prop('checked'));//true
                console.log($('#check').prop('checked',false).prop('checked'));//false
                console.log($('#check').prop('checked','').prop('checked'));//false
                console.log($('#check').prop('checked','111').prop('checked'));//true
                console.log($('#check').prop('checked',true).prop('checked'));//true
              (3)获取自定义属性,
                console.log($('#check').prop('aaa'));//undefined
        </pre>
      </body>
    </html>
    <script type="text/javascript">
    </script>
3、实例无new构建
  (function(window) {
    var jQuery = function(selector, context) {
      return new jQuery.prototype.init(selector, context, rootjQuery);
    },
    jQuery.prototype = {
      init: function( elem, options, prop, end, easing, unit ) {
        this.elem = elem;
        this.prop = prop;
        this.easing = easing || jQuery.easing._default;
        this.options = options;
        this.start = this.now = this.cur();
        this.end = end;
        this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
      },
    }
    jQuery.prototype.init.prototype = jQuery.prototype;
  })(window);

十四、jQuery示例
1、jQuery实现鼠标跟随
  说明,
    (1)所谓鼠标跟随,一般就是指鼠标移到哪张图片上,那该张图片的放大图片就会出现,并且放大图片会随着鼠标在该张图片上移动而移动。
  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <title></title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      img {
        border: none;
      }
      .box {
        width: 660px;
        position: relative;
      }
      .box .mark {
        position: absolute;
        width: 400px;
        height: 300px;
        display: none;
      }
      .box .mark img {
        width: 100%;
      }
      .box img {
        width: 150px;
        float: left;
        margin: 5px;
      } 
    </style>
  </head>
  <body>
  <div class="box" id="box">
    <img src="http://www.qdfuns.com/misc.php?mod=attach&genre=editor&aid=e95708d565639d99576ae7cb00729334"
      realImg="http://www.qdfuns.com/misc.php?mod=attach&genre=editor&aid=5328802dc943fc046e109f70359add0a" alt=""/>
    <img src="http://www.qdfuns.com/misc.php?mod=attach&genre=editor&aid=9e5459a7c0098c27adf4bdd73889caa9"
      realImg="http://www.qdfuns.com/misc.php?mod=attach&genre=editor&aid=846f4d1987765dc4cfd5a06fcdd2dcc1" alt=""/>
    <img src="http://www.qdfuns.com/misc.php?mod=attach&genre=editor&aid=3cd1c8e301007f0c94850139ac79cb5a"
      realImg="http://www.qdfuns.com/misc.php?mod=attach&genre=editor&aid=747bf3f7092ebd2b0bf9fcd27e28bbe5" alt=""/>
    <img src="http://www.qdfuns.com/misc.php?mod=attach&genre=editor&aid=f391169b2cf678aa6fd253cf40d9821d"
      realImg="http://www.qdfuns.com/misc.php?mod=attach&genre=editor&aid=fec8d2f20fad1f28d540337a831e89d0" alt=""/>
    <div id="mark" class="mark"><img src="" alt=""/></div>
  </div>
  <script src="http://s0.kuaizhan.com/res/skin/js/lib/jquery-2.0.3.min.js"></script>
  <script>
    //1.鼠标移入哪张图片的时候,让他对应的大图显示;
    //2.当鼠标在img中移动的时候,大图跟着走;
    var $box=$('.box');
    var $aImg=$box.children('img');
    var $mark=$('.mark');
    var $offset=$box.offset();
    $aImg.mouseover(function(){
      //当鼠标移入每张图片的时候,让mark显示,并且,让mark里面的img标签,src属性值为当前这个图片的realImg属性上拿到的值;
      $mark.show().find('img').attr('src',$(this).attr('realImg'));
    });
    $aImg.mousemove(function(e){
      //拿鼠标的x坐标,减去$box距离body的left位置;
      var left= e.clientX-$offset.left+10;
      var top= e.clientY-$offset.top+10;
      $mark.css({left:left,top:top})
    });
    $aImg.mouseout(function(){
      $mark.hide();
    })
  </script>
  </body>
  </html>
2、jQuery实现文档树效果
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title></title>
      <style>
        * {
          margin: 0;
          padding: 0;
          list-style: none;
        }
        .box {
          width: 250px;
          height: auto;
          padding: 20px;
          background: lightgrey;
          margin: 0 auto;
        }
        .box li {
          line-height: 30px;
          /*注意:height没有被设置,可以根据实际需要自动调整*/
          position: relative;
        }
        .box li em {
          position: absolute;
          left: 0;
          top: 7px;
          width: 16px;
          height: 16px;
          background-image: url("");
          background-size: 100%;
          cursor: pointer;
        }
        .box li em.open {
          background-image: url("");
          background-size: 100%;
        }
        .box li span {
          padding-left: 20px;
          /*因为span前面的em已经绝对定位,脱离文档流了,所以span的左边界直达 li*/
        }
        .box ul {
          display: none;
        }
        .two {
          margin-left: 20px;
        }
        .three {
          margin-left: 40px;
        }
        .four {
          margin-left: 40px;
        }
        /*ul.box下的li显示,其中有折叠的li加em;
        ul.box下的ul隐藏,其内部的li是没法显示的*/
      </style>
    </head>
    <body>
    <ul class="box">
      <li><em></em><span>第一级第一个</span>
        <ul class="two">
          <li><span>第二级第一个</span></li>
          <li><em></em><span>第二级第二个</span>
            <ul class="three">
              <li><em></em><span>第三级第一个</span>
                <ul class="four">
                  <li><span>第四级第一个</span></li>
                  <li><span>第四级第二个</span></li>
                </ul>
              </li>
              <li><span>第三级第二个</span></li>
            </ul>
          </li>
          <li><em></em><span>第二级第三个</span>
            <ul class="three">
              <li><span>第三级第一个</span></li>
              <li><span>第三级第二个</span></li>
            </ul>
          </li>
        </ul>
      </li>
      <li><em></em><span>第一级第一个</span>
        <ul class="two">
          <li><span>第二级第一个</span></li>
          <li><em></em><span>第二级第二个</span>
            <ul class="three">
              <li><em></em><span>第三级第一个</span>
                <ul class="four">
                  <li><span>第四级第一个</span></li>
                  <li><span>第四级第二个</span></li>
                </ul>
              </li>
              <li><span>第三级第二个</span></li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
    <script src="http://s0.kuaizhan.com/res/skin/js/lib/jquery-2.0.3.min.js"></script>
    <script>
      /*思路:
        * 1.让前面有em的span加上小手效果;
        * 2.点击span or em的时候,看他父容器下是否有ul,如果有,让其显示,否则,隐藏
        * */
      var $box=$('.box');
      var $aSpan=$box.find('span');
      //1.让前面有em的span加上小手效果;
      $aSpan.each(function(index,item){
        //if($(item).prev().length){ $(item).css('cursor','pointer');};思路1:
        $(item).prev('em').next('span').css('cursor','pointer'); //思路2:
      });
      //2.点击span or em的时候,看他父容器下是否有ul,如果有,让其显示,否则,隐藏
      $box.click(function(e){
        //当点击的事件源是em or span的时候,我们看其父级下是否有ul
        //如果有:展开让其闭合,闭合就让其展开;
        if(e.target.tagName.toLowerCase()=='em' || e.target.tagName.toLowerCase()=='span'){
          var $parent=$(e.target).parent();
          var $ul=$parent.children('ul');
          if($ul){
            if($ul.css('display')=='block'){//展开,让其闭合
              //当闭合的时候,让当前容器下,所有的em都移除open,所有的ul都隐藏;
              $parent.find('ul').hide();
              $parent.find('em').removeClass('open');
            }else{ //闭合让其展开
              $ul.show();
              $parent.children('em').addClass('open');
    
            }
          }
        }
      })
    </script>
    </body>
  </html>
3、jQuery方法扩展(选项卡)
  说明,
    (1)在jQuery上扩展方法,通过点击实现选项卡的切换。本实例在jQuery的类上扩展,即$.extend({chooseCard:函数}),通过$.chooseCard('#box')调用;
    (2)有别于$.fn.extend({chooseCard:函数})扩展,通过$().chooseCard('#box')调用。
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
      <style>
        * {
          margin: 0;
          padding: 0;
        }
        .box {
          width: 312px;
          border: 2px red solid;
          margin: 0 auto;
        }
        ul {
          overflow: hidden;
        }
        li {
          list-style: none;
          background: red;
          float: left;
          width: 100px;
          height: 30px;
          line-height: 30px;
          text-align: center;
          border: 2px solid orange;
        }
        li.on {
          background: green;
        }
        .box div {
          background: green;
          display: none;
          width: 312px;
          height: 200px;
          font-size: 30px;
          border-top: none;
        }
        .box div.on {
          display: block;
        }
      </style>
    </head>
    <body>
    <div class="box" id="box">
        <ul>
          <li class="">中国</li>
          <li>日本</li>
          <li>韩国</li>
        </ul>
        <div class="on">中国是老大</div>
        <div>日本是老二</div>
        <div>韩国是老三</div>
    </div>
    <script src="http://s0.kuaizhan.com/res/skin/js/lib/jquery-2.0.3.min.js"></script>
    <script>
      (function ($) {
        $.extend({
          chooseCard: function (idStr) {
            var $box = $(idStr);
            var $li = $box.find("li");
            console.log($li);
            var $aDiv = $box.find("div");
            $li.click(function () {
              $(this)
                .css({ height: "32px", "border-bottom": "none" })
                .siblings("li")
                .css({ height: "30px", "border-bottom": "2px solid orange" });
              $(this).addClass("on").siblings("li").removeClass("on");
              $aDiv
                .eq($(this).index())
                .addClass("on")
                .siblings("div")
                .removeClass("on");
            });
          },
        });
      })(jQuery);
    </script>
    <script>
      $(function(){
        $.chooseCard('#box');
      })
    </script>
    </body>
  </html>
4、jQuery复选框全选、反选(<input type='checkbox'/>)
  说明,
    (1)获取复选框状态:$("#allSelect").prop("checked")
    (2)改变复选框状态:$("#allSelect").prop("checked",false)
    (3)翻转复选框状态:item.checked = !item.checked;
    (4)判断复选框是否被选中:if ($(this).is(':checked'))
    (5)找到所有被选中的复选框:myDiv.find("input:checked");
    (6)获取所有复选框:$("#single input:checkbox")或$("#single input[type=checkbox]");
    (7)获取所有被选中的复选框:$("#single input:checkbox:checked")或$("#single input[type=checkbox]:checked");
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
      <script type="text/javascript" src="https://cdn.bootcss.com/jquery/1.9.0/jquery.js"></script>
    </head>
    <body>
      <span>全选</span><input type="checkbox" id="allSelect"><br>
      <span>反选</span><input type="checkbox" id="reverseSelect">
      <div id="single">
        <input type="checkbox">
        <input type="checkbox">
        <input type="checkbox">
        <input type="checkbox">
      </div>
    </body>
  </html>
  <script>
    $("#allSelect").click(function () {
      $("#single")
        .children()
        .each(function (index, item) {
          item.checked = $("#allSelect").prop("checked");
        });
      $("#reverseSelect").prop("checked", false);
    });
    $("#reverseSelect").click(function () {
      $("#single")
        .children()
        .each(function (index, item) {
          item.checked = !item.checked;
        });
      $("#reverseSelect").prop("checked", true);
      singleInput();
    });
    $("#single")
      .children()
      .click(function (index, item) {
        $("#reverseSelect").prop("checked", false);
        singleInput();
      });
    function singleInput() {
      if (
        $("#single input:checkbox:checked").length ==
        $("#single input:checkbox").length
      ) {
        $("#allSelect").prop("checked", true);
      } else {
        $("#allSelect").prop("checked", false);
      }
    }
  </script>
 
十五、Error对象详解
 来源:https:www.jb51.net/article/124210.htm
1、说明,解释器会为每个错误情形创建并抛出一个Error对象,并包含该错误的描述信息
2、常用词汇
  (1)Invalid,无效的
  (2)malformed,畸形的,格式不正确的
  (3)Uncaught,未捕获的
  (4)Unexpected,意外的
  (5)escape sequence,转译序列
3、七种错误
  (1)EvalError,eval错误,eval()函数没有正确执行
  (2)RangeError,范围错误,参数超范围,例如
    A、[].length = -5
      a、Uncaught RangeError: Invalid array length
      b、未捕获的范围错误:无效的数组长度
  (3)ReferenceError,引用错误,例如
    A、console.log(b)
      a、Uncaught ReferenceError: b is not defined
      b、未捕获的引用错误:b没有被定义
  (4)SyntaxError,语法错误,例如
    A、var 1
      a、Uncaught SyntaxError: Unexpected number
      b、未捕获的语法错误:意外的数字
      c、变量名首字符不应该为数字
    B、var 1a
      a、Uncaught SyntaxError: Invalid or unexpected token
      b、未捕获的语法错误:无效的或意外的标记
      c、变量名以数字开始了
    C、new Object()
      a、Uncaught SyntaxError: Invalid or unexpected token
      b、未捕获的语法错误:无效的或意外的标记
      c、半角括号写成全角括号了
    D、function = 5
      a、Uncaught SyntaxError: Unexpected token =
      b、未捕获的语法错误:意外的标记=
      c、function后面不应该是=
    E、console.log(1+2)
      a、Uncaught SyntaxError: missing ) after argument list
      b、未捕获的语法错误:在参数列表后,缺少)
      c、+为中文加号
    F、if(true)
      a、Uncaught SyntaxError: Unexpected end of input
      b、未捕获的语法错误:输入意外结束
      c、后面应该有花括号
    G、JSON.parse(function(){})
      a、Uncaught SyntaxError: Unexpected token u in JSON at position 1
      b、未捕获的语法错误:意外的标记u
    H、json.parse(jsonData)
      a、Uncaught (in promise) SyntaxError: "[object Object]" is not valid JSON 
      b、未捕获(承诺中)语法错误:“[object Object]”不是有效的JSON
      c、jsonData多了不必要的回车、最后一个逗号、不必要的双引号
    I、.js文件中的路径转义(\)错误
      a、Uncaught SyntaxError: Invalid Unicode escape sequence
      b、未捕获的语法错误:无效的Unicode转义序列
    J、eval("{\"name\":\"123\"}"),将json字符串转换为json对象
      a、Uncaught SyntaxError: Unexpected token ':'
      b、未捕获的语法错误:意外的标记:
      c、正确的用法为,
        console.log( eval("("+"{\"name\":\"123\"}"+")") );
        console.log( eval("("+"[{\"name\":\"123\"}]"+")") );
        console.log( eval("[{\"name\":\"123\"}]") );
  (5)TypeError,类型错误,例如(7)
    A、123()
      a、Uncaught TypeError: 123 is not a function
      b、未捕获的类型错误:123不是一个函数
    B、new 456
      a、Uncaught TypeError: 456 is not a constructor
      b、未捕获的类型错误:456不是一个构造函数
  (6)URIError,URI错误,例如(7)
    A、decodeURI("%")
      a、Uncaught URIError: URI malformed(畸形的) at decodeURI
      b、未捕获的URI错误:decodeURI处URI格式不正确
  (7)手动抛错,中断代码执行如
    throw new Error("提示文字")
4、其它报错
  (1)Parsing error: Unexpected token解析错误:意外符号通常是eslint解析错误
5、错误处理
  try{
    如果此处代码运行不出错,则catch函数不会执行
    如果此处代码运行出错,则自动生成错误信息对象error传给catch函数,
  }catch(error){
    此处通过error.message读出错误信息
  }finally{
    无论 try/catch 结果如何都会执行此处的代码块
  }

十六、Date日期时间
附、https://www.runoob.com/jsref/jsref-obj-date.html
1、获取当前毫秒数的5种方法:
  var str1 = Date.now();
  var str2 = +new Date();
  var str3 = new Date() * 1;
  var str4 = new Date().getTime();
  var str5 = new Date().valueOf();
  var str = [str1, str2, str3, str4, str5];
  for (var i = 0; i < str.length; i++) {
    console.log(str[i]);
  }
2、参数问题
(1)参数为多数字(年月日时分秒毫秒):获取的月份与实际一致,如new Date(2023, 11, 22, 10).getMonth()
  var d = new Date(year, month, day, hours, minutes, seconds, milliseconds);
(2)其它情形参数:获取的月份比实际少1,如空、字符串、单数字(毫秒)
  var d = new Date();
  var d = new Date(dateString);
  var d = new Date(milliseconds); 
(3)示例
  var array = [
    "2017-6-2",
    "2017-6-2 12:00:00",
    "2017/6/2",
    "2017/6/2 12:00:00",
    "6 2,2017",
    "6 2,2017 12:00:00",
    "6,2,2017",
    "6,2,2017 12:00:00",
    "Jun 2,2017",
    "Jun 2,2017 12:00:00",
    "Jun,2,2017",
    "Jun,2,2017 12:00:00",
    "Jun/2/2017",
    "Jun/2/2017 12:00:00",
  ];
  var length = array.length;
  var month, str, milliseconds;
  var text1 = "获取的月份比实际少1,比如你获取的是";
  var text2 = "获取的月份与实际一致,比如你获取的是";
  for (var i = 0; i <= length + 2; i++) {
    if (i < length) {
      month = new Date(array[i]).getMonth();
      str =
        "参数为:字符串," + text1 + month + "月,而实际上却是" + (month + 1) + "月";
    }
    if (i === length) {
      month = new Date(2023, 11, 22, 10).getMonth();
      str = "参数为:多数字," + text2 + month + "月,实际上就是" + (month + 0) + "月";
    }
    if (i === length + 1) {
      month = new Date().getMonth();
      str = "参数为:空," + text1 + month + "月,而实际上却是" + (month + 1) + "月";
    }
    if (i === length + 2) {
      milliseconds = new Date().getTime();
      month = new Date(milliseconds).getMonth(); 
      str = "参数为:单数字," + text1 + month + "月,而实际上却是" + (month + 1) + "月";
    }
    console.log(str);
  }
3、日期时间的格式化-手写
(1)把-秒数-转化为-日期时间,年月日时分秒
  function secondsToDate(seconds) {
    var dateTime = new Date(seconds*1000);
    var yearMonthDay =  dateTime.getFullYear() + '年' + (dateTime.getMonth() + 1) + '月' + dateTime.getDate() + '日' ;
    var hourMinuteSecond =  dateTime.getHours() + '时' + (dateTime.getMinutes() + 1) + '分' + dateTime.getSeconds() + '秒' ;
    return {
      yearMonthDay: yearMonthDay,
      hourMinuteSecond: hourMinuteSecond,
      yearMonthDayHourMinuteSecond: yearMonthDay + hourMinuteSecond
    }
  }
(2)把-秒数-转化为-时长,天时分秒
  function formatSeconds(seconds) {
    function addZero(number) {
      return number.toString()[1] ? number : "0" + number;
    }
    var client = "";
    var day = Math.floor(seconds / 86400);
    var hour = Math.floor((seconds % 86400) / 3600);
    var minute = Math.floor(((seconds % 86400) % 3600) / 60);
    var second = Math.floor(((seconds % 86400) % 3600) % 60);
    if (day > 0) {
      client += day + "天";
    }
    if (day > 0 || hour > 0) {
      client += addZero(hour) + "小时";
    }
    if (day > 0 || hour > 0 || minute > 0) {
      client += addZero(minute) + "分";
    }
    client += addZero(second) + "秒";
    return client;
  }
(3)把-秒差-转化为-时长,天时分秒,倒计时
  function countDown(future) {
    function addZero(number) {
      return number.toString()[1] ? number : "0" + number;
    }
    var now = new Date();
    var future = new Date(future);
    var nowMilliseconds = now.getTime();
    var futureMilliseconds = future.getTime();
    var seconds = Math.floor((futureMilliseconds - nowMilliseconds)/1000)
    var day = Math.floor(seconds / 86400);
    var hour = Math.floor((seconds % 86400) / 3600);
    var minute = Math.floor(((seconds % 86400) % 3600) / 60);
    var second = Math.floor(((seconds % 86400) % 3600) % 60);
    var dayHourMinuteSecond = "";
    var dateTimeObj = {};
    if (day > 0) {
      dateTimeObj.day = day;
      dayHourMinuteSecond += day + "天";
    }
    if (day > 0 || hour > 0) {
      dateTimeObj.hour = hour;
      dayHourMinuteSecond += addZero(hour) + "小时";
    }
    if (day > 0 || hour > 0 || minute > 0) {
      dateTimeObj.minute = minute;
      dayHourMinuteSecond += addZero(minute) + "分";
    }
    dateTimeObj.second = second;
    dayHourMinuteSecond += addZero(second) + "秒";
    dateTimeObj.dayHourMinuteSecond = dayHourMinuteSecond;
    return dateTimeObj
  }
  /* 
    var num = 0;
    var futureStr1 = "2030-10-01 00:00:00";
    var futureStr2 = 1000*1000*1000*1000*1.8;
    var dateObj = countDown(futureStr1);
    console.log( dateObj );
    var timer = setInterval(function(){
      dateObj = countDown(futureStr1);
      console.log( dateObj );
      num++;
      if(num>2)clearInterval(timer)
    },1000);
  */
(4)距-某日期时间-某秒-的-日期时间
  function distanceDateTime (fixDateTime, distanceSeconds, separatorFront, separatorMiddle, separatorBack) {
    function addZero(number) {
      return number.toString()[1] ? number : "0" + number;
    }
    separatorFront = separatorFront || '-';//前分隔符,用来分割年月日
    separatorMiddle = separatorMiddle || ' ';//中分隔符,用来分割年月日与时分秒
    separatorBack = separatorBack || ':';//后分隔符,用来分割时分秒
    var oldMilliseconds = (fixDateTime && new Date(fixDateTime).getTime()) || Date.now();//由固定日期时间生成的旧毫秒数
    var newMilliseconds = oldMilliseconds + (distanceSeconds || 0) * 1000;//由旧毫秒数和间隔秒数生成的新毫秒数
    var newObj = new Date(newMilliseconds);//由新毫秒数生成的日期时间对象
    var newFullYear = newObj.getFullYear();
    var newMonth = addZero(newObj.getMonth() + 1);
    var newDate = addZero(newObj.getDate());
    var newHours = addZero(newObj.getHours());
    var newMinutes = addZero(newObj.getMinutes());
    var newSeconds = addZero(newObj.getSeconds());
    var clientDate = newFullYear + "年" + newMonth + "月" + newDate + "日";//客户端日期
    var clientTime = newHours + "时" + newMinutes + "分" + newSeconds + "秒";//客户端时间
    var serverDate = newFullYear + separatorFront + newMonth + separatorFront + newDate;//服务端日期
    var serverTime = newHours + separatorBack + newMinutes + separatorBack + newSeconds;//服务端时间
    return {
      clientDate: clientDate,
      clientTime: clientTime,
      clientDateTime: clientDate + clientTime,
      serverDate: serverDate,
      serverTime: serverTime,
      serverDateTime: serverDate + separatorMiddle + serverTime,
    };
  }  
4、日期时间的格式化-框架内置
(1)过滤
  $filter('date')(
    new Date(new Date().getTime() - 2 * 60 * 1000),
    'yyyy-MM-dd HH:mm:ss'
  );
(2)获取中国标准时间
  console.log(new Date('2020-12-5'))//Sat Dec 05 2020 15:21:57 GMT+0800 (中国标准时间)
5、日期时间中的汉字和分隔符互相转换
(1)汉字转换为分隔符
  function toServerDatetime(str) {
    var regA = /\d+/g;
    var regB = /[年月日时分秒]/g;
    var tempReplace = str.replace(regA, function (match) {
      return match.toString()[1] ? match : "0" + match;
    });
    var resultReplace = tempReplace.replace(regB, function (match) {
      var str = "";
      if (match === "年" || match === "月") {
        str = "-";
      } else if (match === "日") {
        str = " ";
      } else if (match === "时" || match === "分") {
        str = ":";
      } else if (match === "秒") {
        str = "";
      }
      return str;
    });
    return resultReplace;
  }
  var str = "2022年5月25日16时15分6秒";
  console.log(toServerDatetime(str));
(2)分隔符转换为汉字
  function toClientDatetime(str) {
    var regA = /\d+/g;
    var regB = /[- :]/g;
    var tempReplace = str.replace(regA, function (match) {
      var numStr = match.toString();
      if (numStr.length === 2) {
        match = numStr[0] == "0" ? numStr[1] : match;
      }
      return match;
    });
    var index = 0;
    var resultReplace = tempReplace.replace(regB, function (match) {
      var str = "";
      if (index === 0) {
        str = "年";
      } else if (index === 1) {
        str = "月";
      } else if (index === 2) {
        str = "日";
      } else if (index === 3) {
        str = "时";
      } else if (index === 4) {
        str = "分";
      }
      index++;
      return str;
    });
    return resultReplace + "秒";
  }
  var str = "2022-05-25 16:15:06";
  console.log(toClientDatetime(str));
6、现在时间和倒计时:
  <!DOCTYPE html>
  <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>现在时间和倒计时</title>
    </head>
    <body>
      <p style="text-align: center;">现在时间是</p>
      <p style="text-align: center;" id="nowTime"></p>
      <p style="text-align: center; width: 1000px; height: 50px; line-height: 50px; margin: 50px auto; background: gray;">因为月份有28、29、30、31之分,所以为了避免计算错误,倒计时不出现年月</p>
      <p style="text-align: center;">距离2049年10月1日还有</p>
      <p style="text-align: center;" id="futureTime"></p>
    </body>
    <script>
      function addZero(number) {
        return number.toString()[1] ? number : "0" + number;
      }
      function countDown(future) {
        //以下是现在时间
        var now = new Date();
        var nowYear = now.getFullYear();
        var nowMonth = now.getMonth() + 1;
        var nowDate = now.getDate();
        var nowHour = now.getHours();
        var nowMinute = now.getMinutes();
        var nowSecond = now.getSeconds();
        var nowStr = addZero(nowYear) + "年" + addZero(nowMonth) + "月" + addZero(nowDate) + "日" + 
                addZero(nowHour) + "时" + addZero(nowMinute) + "分" + addZero(nowSecond) + "秒";
        //以下是倒计时 
        var future = new Date(future);
        var nowMilliseconds = now.getTime();
        var futureMilliseconds = future.getTime();
        var seconds = Math.floor((futureMilliseconds - nowMilliseconds)/1000) 
        var str = "";
        var day = Math.floor(seconds / 86400);
        var hour = Math.floor((seconds % 86400) / 3600);
        var minute = Math.floor(((seconds % 86400) % 3600) / 60);
        var second = Math.floor(((seconds % 86400) % 3600) % 60);
        if ( day > 0) {
          str += day + "天";
        }
        if ( day > 0 || hour > 0) {
          str += addZero(hour) + "小时";
        }
        if ( day > 0 || hour > 0 || minute > 0) {
          str += addZero(minute) + "分";
        }
        str += addZero(second) + "秒";
        //以下是返回值
        return {
          now:nowStr,
          future:str
        }
      }
      var nowDiv = document.getElementById('nowTime');
      var futureDiv = document.getElementById('futureTime');
      var futureStr = "2049-10-01 00:00:00";
      var dateObj=countDown(futureStr);
      nowDiv.textContent = dateObj.now;
      futureDiv.textContent = dateObj.future;
      setInterval(function(){
        var dateObj=countDown(futureStr);
        nowDiv.textContent = dateObj.now;
        futureDiv.textContent = dateObj.future;
      },1000);
    </script>
  </html>

十七、js判断数据类型的几种方法
1、数据类型
(1)五种基本数据类型:number、string、boolean、undefined(变量声明未赋值)、null(变量声明赋此值,typeof为'object')
  void,操作符,计算但不返回值,等同于undefined,console.log(void 0 === undefined && void(0) === undefined ) //true
    用于html:<a href="javascript:void(0)">单击此处什么也不会发生</a>
    用于JS:if(abc !== void 0)//if(abc !== undefined)
(2)两种引用数据类型:函数、对象(实例获取的途径有,数组字面量、对象字面量、new类)
(3)使用从未声明的变量a,报错为"a is not defined"
2、检测方法
//以下来源:https://www.cnblogs.com/dushao/p/5999563.html
附1、null与对象
  console.log( value == null ) // 为true,则value为null
  console.log( typeof value == "object" && value ) // 为true,则value为普通对象
附2、以下是被检测的数据
  var myNull = null;
  var myUndefined = undefined;
  var myString = "string";
  var myNumber = 222;
  var myBoolean = true;
  var myFunction = function(){};
  var myArray= [1,2,3];
  var myObject = {a:1};
  var myRegexp = /test/;
  var myDate = new Date();
  var myError = new Error();
(1)typeof,返回值是字符串
  console.log(typeof myNull === 'object') // true
  console.log(typeof myUndefined === 'undefined') // true
  console.log( '----------------------------------------' );
  console.log(typeof myString === 'string') // true
  console.log(typeof myNumber === 'number') // true
  console.log(typeof myBoolean === 'boolean') // true
  console.log(typeof myFunction === 'function') // true
  console.log(typeof myArray === 'object') // true
  console.log(typeof myObject === 'object') // true
  console.log(typeof myRegexp === 'object') // true
  console.log(typeof myDate === 'object') // true
  console.log(typeof myError === 'object') // true 
  console.log( '----------------------------------------' );
  var obj = {
    1: 'one',
  }
  for(var attr in obj){
    console.log( typeof attr );//为什么是string,不是number
  }
(2)instanceof,返回值是布尔
  console.log(new String() instanceof String) // true 
  console.log(new Number() instanceof Number) // true 
  console.log(new Boolean() instanceof Boolean) // true 
  console.log( '----------------------------------------' );
  console.log(myString instanceof String) // false
  console.log(myNumber instanceof Number) // false 
  console.log(myBoolean instanceof Boolean) // false 
  console.log(myFunction instanceof Function) // true 
  console.log(myArray instanceof Array) // true
  console.log(myObject instanceof Object) // true
  console.log(myRegexp instanceof RegExp) // true
  console.log(myDate instanceof Date) // true
  console.log(myError instanceof Error) // true   
(3)constructor,返回值是类
  console.log(myString.constructor === String) // true
  console.log(myNumber.constructor === Number) // true
  console.log(myBoolean.constructor === Boolean) // true
  console.log(myFunction.constructor === Function) // true 
  console.log(myArray.constructor === Array) // true
  console.log(myObject.constructor === Object) // true
  console.log(myRegexp.constructor === RegExp) // true
  console.log(myDate.constructor === Date) // true
  console.log(myError.constructor === Error) // true  
(4)prototype,返回值是字符串 
  console.log(Object.prototype.toString.call(myNull) === "[object Null]") // true;
  console.log(Object.prototype.toString.call(myUndefined) === "[object Undefined]") // true;
  console.log( '----------------------------------------' );
  console.log(Object.prototype.toString.call(myString) === "[object String]") // true;
  console.log(Object.prototype.toString.call(myNumber) === "[object Number]") // true;
  console.log(Object.prototype.toString.call(myBoolean) === "[object Boolean]") // true;
  console.log(Object.prototype.toString.call(myFunction) === "[object Function]") // true;
  console.log(Object.prototype.toString.call(myArray) === "[object Array]") // true;
  console.log(Object.prototype.toString.call(myObject) === "[object Object]") // true;
  console.log(Object.prototype.toString.call(myRegexp) === "[object RegExp]") // true;
  console.log(Object.prototype.toString.call(myDate) === "[object Date]") // true;
  console.log(Object.prototype.toString.call(myError) === "[object Error]") // true; 

十八、预解释、闭包、内存泄漏、函数调用、高阶函数、重载
 附、预解释,在代码执行之前,JavaScript引擎会先对代码进行一次扫描,将变量声明和函数声明提升到当前作用域的顶部
1、闭包简略定义
  (1)函数内部不会受外部干扰,这种机制叫做闭包。
  (2)有权访问另一个函数作用域中的变量的函数,叫做闭包。
2、闭包详细定义及来源
  (1)内部函数可以访问外部函数作用域,外部函数不能直接访问内部函数的作用域,即函数内部不会受外部干扰。函数的这种机制叫做闭包。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures
  (2)有权(能夠)访问另一个函数作用域中的变量的函数。《JavaScript高级程序设计》第178页
3、闭包优缺点
  好处:避免污染全局变量;
  坏处:有可能造成内存泄漏;
4、内存泄漏的条件和使用情形:
  (1)条件:函数里面的函数作为返回值被函数外面的变量引用
  (2)使用情形:两函数中间的变量需要自增
5、函数的执行方式 
  (1)直接调用;
  (2)对象调用;
  (3)new调用;
  (4)call、apply调用
  (5)自执行
    A、开始+、-、!、void
      !function () {
        console.log(444);
      }();
    B、前面()
      (function () {
        console.log(222);
      })();
    C、外部()
      (function () {
        console.log(111);
      }());
    D、外部[]  
      [function () {
        console.log(333);
      }()];
   附、减少全局变量
    for(var i=0; i<aBtn.length; i++){
      (function(index){
        aBtn[index].onclick=function click(){
          alert(index);
        }
      })(i);
    }
   附、连续3次自执行 
    (function (allFn) {
      allFn()
    })(function () {
      return (function outerFn(m) {
        function recursion(n) {
          console.log( n );
          return n
        }
        recursion(m)
        return recursion
      })(1)(16)
    });
   附、return 逗号
    function m() {
      return 1,2,3
    }
    console.log(m());
   附、变量滞后
    function n() {
      console.log(o);
    }
    var o = {};
    n()  
   附、JS的逗号,用来将多个表达式连接为一个表达式,新表达式的值为最后一个表达式的值
    (1)语句
      var a,b,c,d;
      a = (b = 'bb', c = 'cc', d = 'dd');
      console.log( a,b,c,d );//dd bb cc dd
    (2)函数
      function aaa(){
        var a = 'a';
        var b = 'b';
        var c = 'c';
        return a, b, c
      }
      console.log( aaa() );//c
6、高阶函数:操作其他函数的函数,比如map、forEach
7、重载:Java一个函数可以有多个定义,只要这每个定义的签名(参数的类型和数量)不同即可。JS没有签名,所以不能重载。

十九、节流、去抖和柯里化
1、函数节流(throttle):不重新开始计时。
  var last = 0;//上次点击时间
  function clickIt(li) {
    var curr = +new Date();
    if (curr - last < 2000) {//2秒钟
      return
    }else{
      last = curr;
    }
    //执行逻辑
  }
2、函数去抖(debounce):重新开始计时。
  var last = 0;//上次点击时间
  function clickIt(li) {
    var curr = +new Date();
    if (curr - last < 2000) {//2秒钟
      last = curr;
      return
    }
    //执行逻辑
  }
3、柯里化函数:把接受多个参数的函数变换成接受(第)一个参数的函数,然后用返回值函数接受余下的参数并返回结果。
  js 如何实现sum(2,3)===sum(2)(3)
  function sum() {
    var arguments0 = arguments[0];
    if (arguments.length === 1) {
      return function (arguments1) {
        return arguments0 + arguments1;
      }
    }
    if (arguments.length === 2) {
      return arguments[0] + arguments[1]
    }
  }
  console.log(sum(2,3)==sum(2)(3));

二十、点透
1、为什么移动端浏览器的click事件延迟300毫秒执行?原因:要观察用户是否进行第2次点击,进而判断用户是想单击页面,还是想双击缩放页面
2、点透发生的情形:click移动端浏览器,立即触发touch事件,如果touch执行的结果是该元素从文档树上消失,300ms后系统要触发click的时候,发现该元素的位置是a或input元素,那么把click事件作用在这个元素上面了,这个现象给人的感觉就是“点透”了
3、点透的解决方案:在touch事件里,通过延迟执行touch事件的代码或阻止默认的click事件,来解决因单击而产生的点透问题
(1)阻止默认事件
  ele.addEventListener("touchend",function (event) {
      ele.style.display="none";
      event.preventDefault();
  });
  //以下zepto的tap事件点透解决
  ele.on("touchend", function (e) {
      $("#alertBox").hide();
      e.preventDefault();
  });
(2)定时器延迟
  ele.addEventListener("touchend",function (event) {
      setTimeout(function () {
          ele.style.display="none";
      },400);
  });
  //以下zepto的tap事件点透解决
  ele.on("touchend", function () {
      setTimeout(function () {
        $("#alertBox").hide();
      }, 350);
  });

二十一、循环和if条件(含逗号)
1、for循环三种写法
  for (语句 1; 语句 2; 语句 3){
    //被执行的代码块
    //语句1:在循环前执行
    //语句2:循环的条件
    //语句3:在循环后执行,缺失不会报错
  }
(1)写法一
  for(var i=0;i<10;i++){
    console.log(i);
    //如果我们用for循环要输出0到9,我们可以这么写
}
(2)写法二
  for(var i=10;i-=2;){//循环条件(为真时)、循环方式(递减),合二为一
    console.log(i);
    //在语句2中,如果返回true循环会继续执行。在js中0,null,undefined,false,'',””作为条件判断时,其结果为false,也就说当i--到0的时候就是false,循环就终止了。
  }
(3)写法三
  var rules = [-2, -1, 0, 2, 1];
  for (var i = 0, rule; rule = rules[i++];) {
    console.log(rule);
    //var i = 0, rule;这是声明变量,是语句1,在循环前执行;
    //rule = rules[i++];这是循环的条件,是语句2;当成为undefined时就会终止循环。
  }
2、for循环特殊案例
(1)外部可读
  for(var i=0,j=0;i<10,j<6;i++,j++){
    var k=j+i;
  }
  console.log(i,j,k);//6,6,10
(2)外部不可读
  for(let i=0,j=0;i<10,j<6;i++,j++){
    let k=j+i;
  }
  console.log(i,j,k);//i is not defined
(3)只终止内层
  for (var i = 0; i < 10; i++) {
    for (var j = 0; j < 10; j++) {
      if (i > 3) { // j > 3; i === j
        break;
      }
      console.log(i, j)
    }
  }
3、while循环
(1)执行代码放在while前
  var i = 0;
  do {
    i++;
    console.log(i)
  }
  while (i < 4);
(2)执行代码放在while后
  var i=4;
  while (i--) {//条件
    console.log(i)
  }
4、如果if条件成立,就执行代码 
  (1)if(isTrue)done()
  (2)isTrue && done();

二十二、对象、原型链(__proto__)、多态
1、对象基本概念(面对对象编程)
(1)类,是以函数的形式来定义的,函数也是一个对象,函数对象都有一个子对象prototype
  function Abc(){}; Abc.prototype.aaa = function(){};
(2)实例,通过new创建一个类的实例对象,
  var abc = new Abc(); abc.__proto__.aaa
(3)原型链,构造函数原型与实例对象之间的连接,类的属性prototype对象的成员都是实例对象的成员,可以通过实例的属性__proto__获取,访问对象的一个属性或方法的时候,如果这个对象没有这个方法或属性,那么Javascript引擎将会访问这个对象的__proto__属性所指向的对象,若仍不能找到,就会继续向上查找,直到Object对象,还没找到该属性,则返回undefined
  console.log(Abc.prototype ===  abc.__proto__)//true
  console.log(Abc.prototype.aaa ===  abc.__proto__.aaa)//true
  console.log(aaa.__proto__.__proto__ === Object.prototype)//true
2、特殊原型链示例
  var obj = {}
  function fn(){}
(1)函数类或对象类或普通函数的原型
  Object.prototype={};
  Function.prototype={};
  fn.prototype = {};
(2)函数类或对象类或普通函数或普通对象_所在类的原型
  Function.__proto__ === Function.prototype
  Object.__proto__ === Function.prototype
  fn.__proto__ === Function.prototype;
  obj.__proto__ === Object.prototype;
(3)函数类或对象类或普通函数或普通对象_所在类的原型_之所在类的原型...
  Function.__proto__.__proto__ === Function.prototype.__proto__ === Object.prototype
  Object.__proto__.__proto__ === Function.prototype.__proto__ === Object.prototype
  Function.__proto__.__proto__.__proto__ === Function.prototype.__proto__.__proto__ === Object.prototype.__proto__ === null
  Object.__proto__.__proto__.__proto__ === Function.prototype.__proto__.__proto__ === Object.prototype.__proto__ === null
(4)原型链继承
  Drag.prototype.__proto__ = EventEmitter.prototype;//这是更安全的继承方法,一般在Node里都是采用这种方式实现继承。IE不支持
  Drag.prototype = new EventEmitter;//相对这种方式来说,上边的写法更安全
3、三个基本特征
(1)封装,可以隐藏实现细节,使得代码模块化,实现代码重用
(2)继承,可以扩展已存在的代码模块(类),实现代码重用
(3)多态,实现接口重用
4、多态
 同一操作作用于不同的对象上面,可以产生不同的执行结果。比如你说“叫”,鸭子听了会发出“嘎嘎声”,鸡听了会发出“咯咯声”。
(1)非多态代码示例
  var makeSound = function(animal) {
    if(animal instanceof Duck) {
      console.log('嘎嘎嘎');
    } else if (animal instanceof Chicken) {
      console.log('咯咯咯');
    }
  }
  var Duck = function(){}
  var Chiken = function() {};
  makeSound(new Chicken());
  makeSound(new Duck());
(2)多态的代码示例
  var makeSound = function(animal) {
    animal.sound();
  }
  var Duck = function(){}
  Duck.prototype.sound = function() {
    console.log('嘎嘎嘎')
  }
  var Chiken = function() {};
  Chiken.prototype.sound = function() {
    console.log('咯咯咯')
  }
  makeSound(new Chicken());
  makeSound(new Duck());
  另外,组件化开发的组件就相当于一个“对象”,一个操作“封装”成一个函数,通过调用内置方法“继承”内部的功能,“多态”没有体现
5、创建对象
(1)工厂模式:
  function creatAnimal(name,age) {
    var animal = new Object();
    animal.name = name;
    animal.age = age;
    animal.eat = function(){
      this.name + "会吃饭"
    }
    return animal
  }
  var dog = creatAnimal('小狗','2')
  console.log(dog);
(2)构造函数模式:
  function Animal(name, age) {
    this.name = name;
    this.age = age;
  }
  this.prototype.eat = function () {
    console.log(this.name + "会吃饭");
  }
  var dog = creatAnimal('小狗','2')
  console.log(dog);
6、Object.create(proto[, propertiesObject])
(1)参数,
  A、proto:新创建对象的原型对象
  B、propertiesObject:新创建对象的(可)枚举属性
(2)返回值,一个新对象,带着指定的原型对象和属性
(3)实例
  var myProto={proto:1};
  var youProto={
    enumer:{
      enumerable: true,
      value: "custom"
    }
  };
  var thisProto=Object.create(myProto,youProto)
  console.log(thisProto)
  console.log(thisProto.enumer)
  console.log(thisProto.__proto__===myProto)
7、Object.defineProperty(myObject, prop, descriptor)
(1)参数,
  A、myObject要定义或修改的属性所在的对象
  B、prop要定义或修改的属性的名称
  C、descriptor要定义或修改的属性的描述符,共4个,
(2)返回值,myObject
(3)作用,定义或者修改myObject的属性
(4)描述符--限制描述符
  A、configurable,默认为false。为true时,prop可删,描述符可改
  B、enumerable,默认为false。为true时,prop可枚举
(5)描述符--数据描述符,和存取描述符不能并存
  A、value,属性值,可以是数值、对象、函数等,默认为undefined
  B、writable,默认为false。为true时,prop可重写
(6)描述符--存取描述符,和数据描述符不能并存
  A、get,属性的getter函数,当访问prop时,会调用此函数,该函数的返回值被用作属性值,默认为undefined。
  B、set,属性的setter函数,当属性值被修改时,会调用此函数,该函数接受1个参数,默认为undefined。
(7)数据描述符和存取描述符使用示例
  A、数据描述符
    var myObj = { thisValue: 5 };
    Object.defineProperty(myObj, "key", {
      configurable: false,
      enumerable: false,
      value: function () {
        console.log(this);
        return this.key+1;
      },
      writable: true,
      //value: "myValue",
      //writable: false,
    });
    myObj.key = 5;
    console.log(myObj.key);
  B、存取描述符
    var myObj = { thisValue: 5 };
    Object.defineProperty(myObj, "key", {
      configurable: false,
      enumerable: false,
      get: function () {
        console.log(this);
        return key+2;
      },
      set: function (value) {
        console.log(this);
        key = value + 1;
      },
    });
    myObj.key = 5;
    console.log(myObj.key);
(8)vue响应式相关代码
  function def(obj, key, val, enumerable) {
    Object.defineProperty(obj, key, {
      configurable: true,
      enumerable: !!enumerable,
      value: val,
      writable: true,
    });
  }
  Object.defineProperty(obj, key, {
    configurable: true,
    enumerable: true,
    get: function reactiveGetter() {
      var value = getter ? getter.call(obj) : val;
      return value;
    },
    set: function reactiveSetter(newVal) {
      var value = getter ? getter.call(obj) : val;
      dep.notify();
    },
  });
 
二十三、JS设计模式
来源:https://www.cnblogs.com/younghxp/p/16397059.html
扩展:https://blog.csdn.net/qq_41311259/article/details/119254280
  包含:(1)策略模式(2)单例模式(3)代理模式(4)模板模式(5)享元模式(6)装饰器模式(7)观察者模式(8)发布-订阅模式
  联想:车模、想装、担待
1、策略模式,算法使用与算法本身分离开来,避免“多重平行条件”选择语句,代码复用性高
  另外,“多重嵌套条件”选择语句,仍然需要if-else嵌套或递归函数,不能使用策略模式
(1)策略模式1
  let strategies = {
    'S': function (salary) {
      return salary * 4
    },
    'A': function (salary) {
      return salary * 3
    },
    'B': function (salary) {
      return salary * 2
    },
  }
  function calculateBouns (level, salary) {
    return strategies[level](salary);
  }
  console.log(calculateBouns('S', 20000))
  console.log(strategies[level](salary))
(2)策略模式2
  var message = [
    [!roleName,'角色名称不能为空!'],
    [!selfName,'注册名称不能为空!'],
    [outIds.length === rolesNum,'当前状态不能为空!'],
  ]
  for(var index = 0; index < message.length; index++){
    if(message[index][0]){
      return layer.open({
        title: '系统信息',
        content: message[index][1],
        skin: 'layui-ext-yourskin'
      });
    }
  }
(3)策略模式3
  //下面情形,用策略模式,不管范围有多少种,都只需1+2个判断,不用策略模式,则需要n+2个判断
  var num = 400;
  var msg = '';
  var rang = [
    [0, 100, '检测数据' + num + '处于[0-100)之间'],
    [100, 200, '检测数据' + num + '处于[100-200)之间'],
    [200, 300, '检测数据' + num + '处于[200-300)之间'],
    [300, 400, '检测数据' + num + '处于[300-400)之间'],
    [400, 500, '检测数据' + num + '处于[400-500)之间'],
    [500, 600, '检测数据' + num + '处于[500-600)之间'],
    [600, 700, '检测数据' + num + '处于[600-700)之间'],
  ];
  var tip = {
    front: '检测数据' + num + '小于' + rang[0][0],
    back: '检测数据' + num + '大于' + rang[rang.length - 1][1]
  }
  for (var i = 0; i < rang.length; i++) {
    if (i == 0 && num < rang[i][0]) {
      msg = tip.front;
      break;
    }
    if (i == rang.length - 1 && num > rang[i][1]) {
      msg = tip.back;
      break;
    }
    if (num >= rang[i][0] && num < rang[i][1]) {
      msg = rang[i][2];
      break;
    }
  }
  console.log(msg)
2、单例模式,一个类仅有一个实例,提供访问他的全局api,如VueX,React-redux
(1)惰性创建单例
  var createDiv = (function () {
    var instance;
    return function () {
      if (!instance) {
        var div = document.createElement('div');
        div.innerHTML = '我是登录窗口';
        div.style.display = 'none';
        document.body.appendChild(div);
        instance = div;
      }
      return instance
    }
  })()
  button.addEventListener('click', function () {
    let div = createDiv();
    div.style.display = 'block';
  })
(2)Class创建单例
  class Singleton {
    constructor (name) {
      this.name = name
    }
    static getInstance (name) {//静态方法
      if (!this.instance) {
        this.instance = new Singleton(name)
      }
      return this.instance
    }
  }
  let a = Singleton.getInstance('a1');
  let b = Singleton.getInstance('b2');
  console.log(a == b)//true
3、代理模式,为一个对象提供一个占位符,以便控制对它的访问;
(1)不使用代理加载图片
  function myImage(id,src){
    var imgNode = document.createElement("img");
    imgNode.src = src;
    document.getElementById(id).appendChild(imgNode);
    
  };
  myImage.setSrc("id","pic.png");
(2)使用代理加载图片
  function myImage(id,src,load){
    //src:图片较大,下载慢,后出现
    //load:图片较小,下载快,先占位
    var imgNode = document.createElement("img");
    imgNode.src = load;
    document.getElementById(id).appendChild(imgNode);
    var img = new Image();
    img.src = src;
    img.onload = function(){
      imgNode.setSrc(img.src);
    };
    
  };
  myImage.setSrc("id","loading.gif","pic.png");
(3)图片加载出错处理
  $timeout(function () {
    var thisImg = new Image();
    thisImg.src = $scope.r_g_company.logoPage.logo.src;
    thisImg.onerror= function() {
      $scope.r_g_company.logoPage.logo.src = $scope.r_g_company.logoPage.logo.srcCY
    }
  },1000);
  $.get('/app_v3/oem/info?' +new Date().getTime()).then(function (res) {
    $scope.r_g_company.logoPage.logo.src = '/audit-html/static/img/cy_oem/logo.png';
  }).catch(function(){
    $scope.r_g_company.logoPage.logo.src = '/audit-html/static/img/cy_oem/logo.png';
    $scope.r_g_company.logoPage.logo.srcCY = '/audit-html/static/img/cy_tech/logo.png';
  });
4、模板模式,实现方式为继承
  function Sport() {}
  Sport.prototype = {
    constructor: Sport,
    init: function() {
      this.stretch();
      this.deepBreath();  
    },
    stretch: function() {
      console.log('拉伸');
    },
    deepBreath: function() {
      console.log('深呼吸');
    },
  };
  function Basketball() {};
  Basketball.prototype = new Sport();
  Basketball.prototype.start = function() {
    console.log('先投上几个三分');
  };
  basketball.init();
5、享元模式,减少对象的数量
  function Health(sex) {
    this.name = name;
    this.sex = sex;
    this.age = age;
    this.height = height;
    this.weight = weight;
  }
  Health.prototype.judge = function(name) {
    HealthManager.updateHealthData(name, this);
    var ret = name + ': ';
    if (this.sex === 'male') {
      ret += this.judgeMale();
    } else {
      ret += this.judgeFemale();
    }
  };
  Health.prototype.judgeMale = function() {
    var ratio = this.height / this.weight;
    return this.age > 20 ? (ratio > 3.5) : (ratio > 2.8);
  };
  Health.prototype.judgeFemale = function() {
    var ratio = this.height / this.weight;
    return this.age > 20 ? (ratio > 4) : (ratio > 3);
  };
  var HealthFactory = {
    objs: {},
    create: function(sex) {
      if (!this.objs[sex]) {
        this.objs[sex] = new Health(sex);
      }
      return this.objs[sex];
    }
  };
  var HealthManager = {
    healthData: {},
    add: function(name, sex, age, height, weight) {
      var health = HealthFactory.create(sex);
      // 存储变化的数据
      this.healthData[name] = {
        age: age,
        height: height,
        weight: weight
      };
      return health;
    },
    // 从存储的数据中获取,更新至当前正在使用的对象
    updateHealthData: function(name, obj) {
      var healthData = this.healthData[name];
      for (var item in healthData) {
        if (healthData.hasOwnProperty(item)) {
          obj[item] = healthData[item];
        }
      }
    }
  };
  var one = HealthManager.add('A', 'male', 18, 160, 80);
  var two = HealthManager.add('B', 'male', 21, 180, 70);
  one.judge('A'); // A: false
  two.judge('B'); // B: false
6、装饰器模式,在原来方法的基础上,添加一些新功能,有点类似代理模式,实际用于ajax请求的拦截器等
(1)创建主类
  class Circle {
    draw() {
      console.log('画一个圆形');
    }
  }
(2)创建装饰器
  class Decorator {
    constructor(circle) {
      this.circle = circle;
    }
    draw() {
      this.circle.draw();
      this.setRedBorder(circle);
    }
    setRedBorder(circle) {
      console.log('画一个红色边框');
    }
  }
(3)使用装饰器
  let circle = new Circle();
  let decorator = new Decorator(circle);
  decorator.draw();
7、观察者模式
(1)观察者类
  class Observer {
    //name需要观察的参数
    //callback 观察的参数达到边界条件触发的事件
    constructor(name, callback) {
      this.name = name;
      this.callback = callback;
    }
  }
(2)被观察者类
  class Subject {
    //未传值初始为空
    constructor(state) {
      //初始状态
      this.state = state;
      //观察者方法队列
      this.observsers = [];
    }
    //设置自己的状态
    setState(val) {
    //告诉观察者目前改变成了什么状态
      var that = this;
      this.state = val;
      //同时需要把自己身上的观察者方法全部触发
      this.observsers.map(function(item){
        //item是每一个观察者,每一个观察者是一个对象
        item.callback(that.state);
      })
    }
    //添加观察者
    addObserver(observer) {
      //把观察者传递进来
      this.observsers.push(observer);
    }
    //删除观察者
    removeObserver(observer) {
      //过滤出来非当前观察者的观察者
      this.observsers = this.observsers.filter(function(obs){
        return obs !== observer;
      });
    }
  }
(3)使用
  let obj = {
    name: "abc"
  };
  //创建观察者与被观察者
    //创建观察者
      const observer = new Observer('watchName', function(newState){
        console.log(newState);
      });
    //创建一个被观察者
      const subject = new Subject(obj.name);
    //添加一个观察者
      subject.addObserver(observer);
    //触发观察者方法
      subject.setState('123');
8、发布-订阅模式
(1)发布者
  class Publisher {
    constructor(name, channel) {
      this.name = name;
      this.channel = channel;
    }
    //添加目录
    addTopic(topicName) {
      this.channel.addTopic(topicName);
    }
    //取消添加
    removeTopic(topicName) {
      this.channel.removeTopic(topicName);
    }
    //发布晨报
    publish(topicName) {
      this.channel.publish(topicName);
    }
  }
(2)订阅者
  class Subscriber {
    constructor(name, channel) {
      this.name = name;
      this.channel = channel;
    }
    //订阅目录
    subscribe(topicName) {
      this.channel.subscribeTopic(topicName, this);
    }
    //取消订阅
    unSubscribe(topicName) {
      this.channel.unSubscribeTopic(topicName, this);
    }
    //接收发布后,更新
    update(topic) {
      console.log(`${topic}已经送到${this.name}家了`);
    }
  }
(3)第三方平台
  class Channel {
    constructor() {
      this.topics = {};
    }
    //发布者在平台添加目录
    addTopic(topicName) {
      this.topics[topicName] = [];
    }
    //发布者取消添加
    removeTopic(topicName) {
      delete this.topics[topicName];
    }
    //订阅者订阅目录
    subscribeTopic(topicName, sub) {
      if (this.topics[topicName]) {
        this.topics[topicName].push(sub);
      }
    }
    //订阅者取消订阅
    unSubscribeTopic(topicName, sub) {
      this.topics[topicName].forEach(function(item, index){
        if (item === sub) {
          this.topics[topicName].splice(index, 1);
        }
      });
    }
    //平台通知某个目录下所有订阅者
    publish(topicName) {
      this.topics[topicName].forEach(function(item){
        item.update(topicName);//发布的内容-传给-订阅者的函数
      });
    }
  }
(4)应用
  A、定义1个调度中心channel
      var channel = new Channel();
  B、定义2个发布者publisher1、publisher2
      var publisher1 = new Publisher("发布者1", channel);
      var publisher2 = new Publisher("发布者2", channel);
  C、定义3个订阅者subscriber1、subscriber2、subscriber3
    var subscriber1 = new Subscriber("订阅者1", channel);
    var subscriber2 = new Subscriber("订阅者2", channel);
    var subscriber3 = new Subscriber("订阅者3", channel);
  D、2家发布者在平台上添加了晨报1、晨报2和晨报3三种晨报,如{'晨报1':[]}
    publisher1.addTopic("晨报1");
    publisher1.addTopic("晨报2");
    publisher2.addTopic("晨报3");
    publisher2.removeTopic("晨报3");//取消添加
  E、3个订阅者在平台上订阅了晨报,将自己的实例注入到数组中,如{'晨报1':[thisInstance1]}
    subscriber1.subscribe("晨报1");
    subscriber2.subscribe("晨报1");
    subscriber2.subscribe("晨报3");
    subscriber3.subscribe("晨报2");
    subscriber3.subscribe("晨报3");
    subscriber3.unSubscribe("晨报3");//取消订阅
  F、发布者推送某个主题,遍历订阅者,并执行订阅者如thisInstance1的update函数
    publisher1.publish("晨报1");
    publisher1.publish("晨报2");
    publisher2.publish("晨报3");
(5)观察者模式与发布订阅模式的区别
  A、观察者模式,为紧耦合,只有两个角色,目标对象维护观察者对象,并通知观察者
  B、发布订阅模式,为松耦合,有三个角色,多了一个第三方平台,目标对象和观察者对象,通过第三方平台进行通信
(6)Vue的基本原理,运用发布-订阅模式,具体可查看手写vue2.x原理:https://gitee.com/younghxp/vue2-data-response
 
二十四、与parseFloat相关
原文地址:https://yq.aliyun.com/ziliao/92498?spm=5176.8246799.blogcont.20.cUDmIE
1、Number()
(1)如果是Boolean值,true和false值将分别被转换为1和0。
(2)如果是数字值,只是简单的传入和返回。
(3)如果是null值,返回0。
(4)如果是undefined,返回NaN。
(5)如果是字符串:
  A、如果字符串中只包含数字时,将其转换为十进制数值,忽略前导0
  B、如果字符串中包含有效浮点格式,如“1.1”,将其转换为对应的浮点数字,忽略前导0
  C、如果字符串中包含有效的十六进制格式,如“0xf”,将其转换为相同大小的十进制数值
  D、如果字符串为空,将其转换为0
  E、如果字符串中包含除上述格式之外的字符,则将其转换为NaN
(6)如果是对象,则调用对象的valueOf()方法,然后依照前面的规则转换返回的值。如果转换的结果是NaN,则调用对象的toString()方法,然后再依照前面的规则转换返回的字符串值。
  var num1 = Number("Hello world");//NaN
  var num2 = Number("");//0
  var num3 = Number("0000011"); //11
2、parseInt()
(1)处理整数的时候parseInt()更常用。parseInt()函数在转换字符串时,会忽略字符串前面的空格,知道找到第一个非空格字符。
(2)如果第一个字符不是数字或者负号,parseInt() 就会返回NaN,同样的,用parseInt() 转换空字符串也会返回NaN。
(3)如果第一个字符是数字字符,parseInt() 会继续解析第二个字符,直到解析完所有后续字符串或者遇到了一个非数字字符。
(4)parseInt()方法还有基模式,可以把二进制、八进制、十六进制或其他任何进制的字符串转换成整数。
(5)基是由parseInt()方法的第二个参数指定的,所以要解析十六进制的值,当然,对二进制、八进制,甚至十进制(默认模式),都可以这样调用parseInt()方法。
  var num1 = parseInt("AF",16); //175
  var num2 = parseInt("AF");//NaN
  var num3 = parseInt("10",2); //2(按照二进制解析)
  var num4 = parseInt("sdasdad");//NaN
3、parseFloat()
(1)与parseInt() 函数类似,parseFloat() 也是从第一个字符(位置0)开始解析每一个字符。也是一直解析到字符串末尾,或者解析到遇见一个无效的浮点数字字符为止。
(2)也就是说,字符串中第一个小数点是有效的,而第二个小数点就是无效的了,它后面的字符串将被忽略。
(3)parseFloat() 只解析十进制,因此它没有第二个参数指定基数的用法
(4)如果字符串中包含的是一个可解析为正数的数(没有小数点,或者小数点后都是零),parseFloat() 会返回整数。
  var num1 = parseFloat("123AF"); //123
  var num2 = parseFloat("0xA");//0
  var num3 = parseFloat("22.5");  //22.5
  var num4 = parseFloat("22.3.56");//22.3
  var num5 = parseFloat("0908.5"); //908.5
  parseInt() 和parseFloat() 的区别在于:
  parseFloat() 所解析的字符串中第一个小数点是有效的,而parseInt() 遇到小数点会停止解析,因为小数点并不是有效的数字字符。
  parseFloat() 始终会忽略前导的零,十六进制格式的字符串始终会被转换成0,而parseInt() 第二个参数可以设置基数,按照这个基数的进制来转换。
4、customParseInt:字符串类型的数字转换成数字类型的数字、数字类型的数字和字符串类型的非数字保持原样
  function customParseInt(num) {
    var tempNum = parseInt(num);
    if (typeof tempNum === 'number' && !isNaN(tempNum)) {
      return tempNum;
    }
    return num;
  };
  console.log(customParseInt(1))
  console.log(customParseInt("1"))
  console.log(customParseInt("闪烁"))
4、整型范围
(1)有负号整型范围
  Int8:[-128:127]
  Int16:[-32768:32767]
  Int32:[-2147483648:2147483647]
  Int64:[-9223372036854775808:9223372036854775807]
(2)无负号整型范围
  UInt8:[0:255]
  UInt16:[0:65535]
  UInt32:[0:4294967295]
  UInt64:[0:18446744073709551615]
 
二十五、业务逻辑
注、软件一般分为三个层次:表现层、业务逻辑层、数据层
1、表现层:负责界面展示
2、业务逻辑层:接收表现层的请求,向数据层请求数据,将数据返给表现层
(1)业务,要实现的核心需求
(2)业务逻辑,实现核心需求的代码
3、数据层:负责数据存储、读取
 
二十六、造轮子:在编程中,业界已经有公认的软件或者库了,你明知道自己不可能比它做得更好,却还坚持要做
  
  
  
  

  

posted @ 2019-10-25 16:37  WEB前端工程师_钱成  阅读(3585)  评论(0编辑  收藏  举报