7、svg、D3、ES6|Proxy|await、Common与ES6模块、TS泛型接口|类继承实例、jQuery相关、Error、Date日期时间倒计时、数据类型void、预解释闭包内存泄漏函数执行|逗号|高阶|重载、节流去抖|柯里化|点透、for|with|console、对象原型链多态|创建create|defineProperty、设计模式、parseFloat、业务逻辑(4000行)

一、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>
      <div class="flex">
        <div>
          <div class="fontWeight">五、mask遮罩</div>
          <pre>
            1、示例
            <svg width="75" height="75" viewBox="0 0 75 75" fill="none" xmlns="http://www.w3.org/2000/svg" >
              <mask id="mask0" x="0" y="0" mask-type="alpha">
                <circle cx="37.5" cy="37.5" r="37.5" fill="#0042DA" />
              </mask>
              <g mask="url(#mask0)"><!-- 属性mask用来引用一个遮罩元素 -->
                <rect x="-30" y="-43" width="131" height="154" fill="#0042DA" />
                <rect x="2.50413" y="120.333" width="81.5597" height="86.4577" rx="2.5"
                  transform="rotate(-52.6423 2.50413 120.333)" stroke="#FED23D" stroke-width="5" />
                <circle cx="76.5" cy="-1.5" r="29" stroke="#FF8E20" stroke-width="5" />
                <path d="M-49.8224 22L-15.5 -40.7879L18.8224 22H-49.8224Z" stroke="#F7F8FF" stroke-width="5" />
              </g>
            </svg>
          </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++){//报错,Assignment to constant variable. 给常量赋值
        a[i]=function(){
          console.log(i);
        };
      }
      a[6]();
  (2)提声
    附、var不存在暂时性死区、有变量提声、声明前可用
    附、let和const,存在暂时性死区、没有变量提声、声明前不可用
    附、变量提升,var将变量声明提升到它所在作用域的最开始的部分
    附、暂时性死区,let或const声明变量之前,该变量不可用
    示例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、箭头函数和普通函数的区别(new、arguments、this、call、apply、prototype、Generator、yield),
  (1)arguments,用rest参数…解决,
    A、报错
      let foo = () => {
        console.log(arguments)
      }
      foo(1, 2, 3)
    B、不报错
      let foo = (...args) => {
        console.log(args)
      }
      foo(1, 2, 3)
      //[1, 2, 3]
  (2)参数个数
    A、0或多个参数,需要用() //var fn=()=>5
    B、1个参数,不需要() //var fn=v=>v
  (3)分句个数
    附:var 变量=(参数)=>函数体
    A、多个分句,用{}和return;
    B、1个分句,不用{}和return;
    C、1个分句,返回值为对象,用({})
  (4)this,宿主对象的this
    A、示例一
      var value = 6;
      var obj = {
        value: 5,
        fn: function fnIn() {
          setTimeout(() => {
            console.log(this.value); //5,宿主对象为fn,其this为obj
          });
        },
      };
      obj.fn();
    B、示例二
      var value = 6;
      var obj = {
        value: 5,
        fn: () => {
          console.log(this.value); //6,宿主对象为obj,其this为window
        },
      };
      obj.fn();
  (5)call和apply,对this没有影响
  (6)不能使用new,不能作为构造函数,
  (7)没有原型属性,prototype,
  (8)不能做Generator函数,不能使用yield关键字,
7、访问器属性
   来源,https://blog.csdn.net/weixin_44410783/article/details/110223586
  (1)ES6对象
    通过get和set定义一个属性,代替内部属性被外部访问
    var obj = {
      _num : 0,
      set num(value){//有且仅有一个参数
        this._num = value;
      },
      get num(){//不能有参数,必须返回内容
        return this._num;
      }
    }
    console.log( obj.num );
    obj.num = 1;
    console.log( obj.num );
  (2)ES6类
    class Emp{
      constructor(name,age){
        this.name=name;
        this.age=age;
        Object.defineProperty(this,"_eage",{
          value:this.age,
          writable:true,
          enmerable:false,
          configurable:false
        })
      }
      get age(){
        return this._eage;
      }
      set age(v){
        if(v>18){
          this._eage=v
        }else{
          throw Error("禁");
        }
      }
    }
    var e=new Emp('tom',28)
    console.log(e)
    e.age=49
    console.log(e.age)
8、模板字符串
  (1)在模板字符串html中,用`<div>${ /* 注释内容 */'' }</div>`加注释
  (2)在模板字符串js中,用`${变量}`加变量

四、ES6进阶
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实现双向绑定
3、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上获取默认行为。
4、Reflect方法
  (1)Reflect.get(target, name, receiver)
    查找并返回target对象的name属性,如果没有该属性,则返回undefined。
    如果name属性部署了读取函数,则读取函数的this绑定receiver。
  (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
5、Iterator(遍历器)
  (1)以下遍历器(迭代器)模拟代码
    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 }"
  (2)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属性。
6、Generator 函数(生成器)
  (1)Generator函数有两个特征
    A、function关键字与函数名之间有一个星号
    B、函数体内部使用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));
7、async和await
  (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 {
        
        }
      });
  
五、CommonJS与ES6模块(含AMD、CMD、UMD)
1、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 允许将代码按功能或逻辑分类成独立的模块,每个模块只负责特定的功能,使代码更加可维护和可复用。
    B、缓存代码:CommonJS 规范提供了模块的加载和缓存机制,可以确保模块只会被加载一次,避免重复加载和执行,提高性能。
    C、隔离命名空间:每个模块都有自己独立的作用域,不会与其他模块中的变量和函数冲突,有效避免命名冲突。
    D、跨平台使用:CommonJS 规范不限于在浏览器中使用,也可以在其他 JavaScript 运行环境中使用,如 Node.js 等。
  (4)module.exports和exports的区别
    A、初始化
      a、module.exports = {}
      b、exports = module.exports
    B、赋值操作
      a、exports重新赋值后,不再指向module.exports
      b、外部引入的是module.exports所指的对象
    C、正确使用
      a、module.exports.fn = function() {}
      b、exports.fn = function() {}
2、ES6中模块规范(在前端模块化开发中使用)
  (1)默认导出和引入,export default,1个文件只能有1个默认导出
    A、先定义,再默认导出
      function ABC(){};
      export default ABC;
    B、直接默认导出
      export default function /*ABC*/(){};
    C、上面对应的import引入为
      import DEF from "./abc"
  (2)普通导出和引入,export
    A、先定义,再普通导出
      var abc="abc";
      function ABC(){};
      export {acb,ABC}
    B、直接普通导出
      export var abc="abc";
      export function ABC(){};
    C、上面对应的import引入为
      import {abc,ABC} from "./abc" 
    D、通配符导入
      import * as echarts from 'echarts';相当于 
      import { a, b, c, d } from 'echarts'
  (3)混合导出和引入
    A、默认和普通,分别导出 
    B、默认和普通,同时引入
      import DEF,{abc,ABC} from "./def" 指的是,def.js模块默认导出了DEF,普通导出了abc,ABC 
  (4)改名导出和引入
    A、定义
      var abc="abc";
      function ABC(){};
      export {abc as abc1, ABC as ABC1}
    B、引入
      import {abc1,ABC1} from "./abc";
      import {abc1 as abc2, ABC1 as ABC2} from "./abc";
3、CommonJS和esmodule的区别
   注、都可以在现代浏览器中使用
   注、2019年发布的node13.2.0开始支持ES6模块,.vue文件被vue-loader转化为ES6模块
  (1)语法不同
    A、CommonJS使用require和module.exports进行模块导入和导出
    B、ESModule使用import和export关键字进行模块导入和导出
  (2)导入过程与结果不同
    A、CommonJS只能,对导入值,静态、同步、全部拷贝,运行时建立依赖关系
    B、ESModule支持,对导入值,动态、异步、部分引用,编译时建立依赖关系
  (3)循环依赖处理不同
    来源,https://blog.csdn.net/Pentoncos/article/details/127557676
    A、CommonJS借助模块缓存,已经加载的模块标注为已缓存
    B、ESModule借助模块地图,已经加载的模块标注为获取中
4、AMD、CMD、UMD模块规范
  (1)AMD和require.js,AMD依赖模块加载跳过所有异步,运行后面的回调函数,
    运行中所有依赖模块的语句,都定义在另一个回调函数中,等到加载完成之后,这个回调函数才会运行
    A、定义模块
      define("abc", ["a", "b", "c", 'd'], function (a, b, c, d) {
        var a = require('a');
      });
    B、引用模块
      require(['abc','def'],function(abc,def){});
  (2)CMD和sea.js,在回调函数里,需要某个依赖,才会加载这个依赖
    A、定义模块
      define("abc", ["a", "b", "c", 'd'], function (require, exports, module) {
        var a = require('a');
      });
    B、引用模块
      require("abc")
  (3)UMD用于判断代码运行环境
    A、UMD,Universal Module Definition,通用模块定义
    B、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() {})
 
六、TS知识(typeScript)
 附、ts在线运行
 (1)https://typescript.jsrun.net/
 (2)https://www.json.cn/run/typescript/
 (3)https://www.bejson.com/runcode/typescript
 附、泛型,https://www.tslang.cn/docs/handbook/generics.html
1、符号(不是修饰符)
  (1)交叉类型&
    A、&用于创建交叉类型,合并多个类型的属性
    B、如果一个值的类型是交叉类型A&B,那么该值必须同时满足类型A和类型B的要求
      type A = { propA: number };
      type B = { propB: string };
      type C = A & B;
      // 结果类型 C
      // { propA: number, propB: string }
  (2)联合类型|
    A、|用于创建联合类型,表示一个值的类型可以是多个类型之一
    B、如果一个值的类型是联合类型A|B,那么该值可以是类型A或者类型B中的任意一种
      type A = { propA: number };
      type B = { propB: string };
      type C = A | B;
      // 结果类型 C
      // { propA: number } | { propB: string }
  (3)??
    A组、
      console.log(null || 1) //1
      console.log(null ?? 1) //1
    B组、
      console.log(undefined || 1) //1
      console.log(undefined ?? 1) //1
    C组、
      console.log(0 || 1) //1
      console.log(0 ?? 1) //0 
  (4)?.
    var ss = null;
    console.log(ss?.qqq)// undefined
    var ss = {qqq:1};
    console.log(ss?.qqq)// 1
  (5)?:
    email?: string; // 可选属性
2、泛型,延迟指定的数据类型,“<XXX>”放在变量的后面(在定义时、在使用时)
  (1)不用泛型定义
    A、写法1
      function identity(arg: number): number {
        return arg;
      }
      console.log(identity(3));
    B、写法二
      function identity(arg: any): any {
        return arg;
      }
      console.log(identity(3));
  (2)泛型的定义与执行
    function identity<T>(arg: T): T { //可以没有“: T”。泛型-参数-返回值类型(可以缺失)-函数体
      return arg;
    }
    let output = identity<string>("myString"); 
    let output = identity("myString"); 
  (3)泛型函数,延迟指定(参数和返回值)的数据类型
    A、泛型函数的普通类型<T>
      function createArray<T>(length: number, value: T): Array<T> {
        let result: T[] = [];
        for (let i = 0; i < length; i++) {
          result[i] = value;
        }
        return result;
      }
      console.log( createArray<string>(3, 'x') ); 
      /* 
      //ts函数,不用泛型,如下
      //写法1
      var createArray = function(length: number, value: string){
        let result = [];
        for (let i = 0; i < length; i++) {
          result[i] = value;
        }
        return result;
      }
      console.log( createArray(3, 'x') ); 
      //写法2
      var createArray = (length: number, value: string) => {
        let result = [];
        for (let i = 0; i < length; i++) {
          result[i] = value;
        }
        return result;
      }
      console.log( createArray(3, 'x') ); 
      //写法3
      var createArray = () => {
        let result = [];
        for (let i = 0; i < 3; i++) {
          result[i] = 'x';
        }
        return result;
      }
      console.log( createArray() ); 
      */ 
    B、泛型函数的默认类型<T = string>
      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;
      }
      console.log( createArray<string>(3, 'x') ); 
    C、泛型函数的执行 
      type FilePreview = {
        data: FilePreviewData;
        mime: string;
        name: string;
        preview: string;
        type: string;
      };
      const [previews, setPreviews] = createSignal<FilePreview[]>([],{});
      //FilePreview[],数组的每项必须包含FilePreview 
      //createSignal,它的泛型可能-没限制参数,只限制返回值 
  (4)泛型类
    A、TypeScript的核心原则之一是对值进行类型检查
    B、在TypeScript里,接口的作用就是为这些类型命名和为代码定义契约
    C、declare关键字通常用于声明不需要编译的实体,例如全局变量、函数、对象等
    D、泛型类普通写法-无参
      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; };
      console.log( myGenericNumber.zeroValue );
      console.log( myGenericNumber.add(2, 3) );
    E、泛型类普通写法-有参
      class Persons<T> {
        private value: T;
        aaa: T;
        constructor(value: T) {
          this.value = value;
        }
        getValue(): T {
          return this.value;
        }
      } 
      const one = new Persons<string>("小美");
      one.aaa = '字符串';
      console.log(one.aaa); 
      console.log(one.getValue()); 
      console.log('---------------------'); 
      const two = new Persons<number[]>([1, 2, 3]);
      two.aaa = [3,3,3];
      console.log(two.aaa); 
      console.log(two.getValue()); 
    F、泛型约束
      interface Lengthwise {
        length: number;
      }
      function loggingIdentity<T extends Lengthwise>(arg: T): T {
        console.log(arg.length);
        return arg;
      }
    G、对泛型进行约束,只允许这个函数传入那些包含length属性的变量
    H、我们定义一个接口来描述约束条件。创建一个包含.length属性的接口,使用这个接口和extends关键字来实现约束 
3、接口,数据结构和数据类型的规范,“:XXX”放在变量的后面(在定义时)
  (1)不用接口规范
    function printLabel(labelledObj: { label: string }) {
      console.log(labelledObj.label);
    }
    let myObj = { size: 10, label: "Size 10 Object" };
    printLabel(myObj);
  (2)接口的定义与使用
    A、定义1
      type SquareConfig = {
        color?: string;
        width?: number;
      }
      type ShortTextInputProps = {//案例来源,chat-embed项目下ShortTextInput.tsx文件
        ref: HTMLInputElement | HTMLTextAreaElement | undefined;
        onInput: (value: string) => void;
        fontSize?: number;
        disabled?: boolean;
      } & Omit<JSX.TextareaHTMLAttributes<HTMLTextAreaElement>, 'onInput'>;
      //Omit<Type, Keys>创建新类型,从现有类型(Type)中排除指定属性(Keys)
    B、定义2
      interface SquareConfig {
        color?: string;
        width?: number;
      }
    C、使用
      function createSquare(config: SquareConfig): {color: string; area: number} {
        let newSquare = {color: "white", area: 100};
        if (config.color) {
          newSquare.color = config.color;
        }
        if (config.width) {
          newSquare.area = config.width * config.width;
        }
        return newSquare;
      }
      console.log( createSquare({}) );
      console.log( createSquare({color: "black"}) );
  (3)数组的接口
    A、var numbers: number[] = [1, 2, 3, 4, 5];
    B、var numbers: Array<number> = [1, 2, 3, 4, 5];
    C、var numbers = [1, 2, 3, 4, 5]; 
    D、ts声明自定义类型的数组示例 
      interface Teacher {
        name: string;
        age: number;
        speciality: string;
      }
      let teachers: Teacher[] = [
        { name: 'Alice', age: 30, speciality: 'Mathematics' },
        { name: 'Bob', age: 35, speciality: 'Physics' },
        { name: 'Charlie', age: 32, speciality: 'Chemistry' }
      ];
      teachers.forEach((teacher) => {
        console.log(`Name: ${teacher.name}, Age: ${teacher.age}, Speciality: ${teacher.speciality}`);
      });
  (4)对象的接口
    A、写法1
      interface User {
        name: string;
        age: number;
        email?: string; // 可选属性
      }
      let user: User = {
        name: 'Alice',
        age: 25
      };
      console.log( user );
    B、写法2,百度-TypeScript的类型声明 Record
      说明:
       a、Record类型是一个通用的类型,用于创建一个对象类型,其中所有的属性都有相同的值类型
       b、Record接收两个类型参数,第一个是键的类型,第二个是值的类型
      示例:
        type UserRecord = Record<string, string>;
        const user: UserRecord = {
          name: 'Alice',
          age: '30',
          job: 'Developer'
        };
        console.log( user );
    C、写法3,百度-overrideConfig?: Record<string, unknown>;
      示例1:为这个可选属性提供一个值
        let config: { overrideConfig?: Record<string, unknown> } = {
          overrideConfig: {
            key1: 'value1',
            key2: 42,
            key3: true
          }
        };
      示例2:不想提供任何值,只需要声明这个可选属性
        let config: { overrideConfig?: Record<string, unknown> } = {
          // overrideConfig is optional
        };
      示例3:在后面的代码中设置或修改这个属性
        let config: { overrideConfig?: Record<string, unknown> } = {};
        config.overrideConfig = {
          key1: 'value1',
          key2: 42,
          key3: true
        };
    D、写法4,真实案例(改编)
      示例1:可运行
        type IncomingInput = {
          conversation_id?: string;
          messages?: {}[]; //由对象组成的数组
          question?: string;
          //uploads?: FileUpload[];
          overrideConfig?: Record<string, unknown>;
          socketIOClientId?: string;
          chatId?: string;
          fileName?: string; 
          leadEmail?: string;
        };
        type MessageRequest = {
          chatflowid?: string;
          apiHost?: string;
          body?: IncomingInput;
        };  
        const body: IncomingInput = {
          conversation_id: 'conversation_id()', //chatId()  724b8f8a385411efb642e43d1a3ceb2
          messages: [
            {
              role : "user",
              content : 'value',
            }
          ]
        };
        const obj: MessageRequest = {
          chatflowid: 'string',
          apiHost: 'string',
          body: body
        }
        console.log( obj );
      示例2:不可运行,注意函数的参数默认值
        export const sendMessageQuery = ({ chatflowid, apiHost = 'http://localhost:3000', body }: MessageRequest) =>
          sendRequest<any>({
            method: 'POST',
            url: `${apiHost}/api/v1/prediction/${chatflowid}`,
            //url: 'http://172.20.35.35/v1/api/completion',
            body,
          });
  (5)函数的接口
    interface User {
      name: string;
      age: number;
      email?: string; // 可选属性
    }
    function createUser(userData: User): User{
      return {
        id: userData.id,
        name: userData.name,
        isActive: userData.isActive,
      };
    }
    const newUser = createUser({ id: 1, name: 'Alice', isActive: true });
    console.log(newUser); 
4、泛型接口
  (1)写法1
    interface CreateArrayFunc {
      <T>(length: number, value: T): Array<T>;
    }
    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;
    }
    console.log(createArray(3,'x'));
  (2)写法2
    interface CreateArrayFunc<T> { //有<T>
      (length: number, value: T): Array<T>;
    }
    let createArray: CreateArrayFunc<string>; //有<string>
    createArray = function<T>(length: number, value: T): Array<T> {
      let result: T[] = [];
      for (let i = 0; i < length; i++) {
        result[i] = value;
      }
      return result;
    }
    console.log(createArray(3,'x'));

七、ES6和TS的类、继承、实例
  附、两者的共同特征
    A、继承:super是父类的构造函数,必须首先调用
    B、static:(子)类都可以获取
1、ES6的类、继承、实例
  (1)static函数:类调用,this指向类;子类调用,this指向子类
  (2)普通函数:实例调用,this指向实例;子实例调用,this指向子实例
  (3)示例:
    class Parent {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
      static name = "animal";
      static age = "20";
      static getY() {
        console.log('通过父类访问静态,this就是父类,this === Parent',);
        console.log('通过子类访问静态,this就是子类,this === Son',);
      }
      getX() {
        console.log('通过父类实例访问普通,this就是父类实例,this === parent',);
        console.log('通过子类实例访问普通,this就是子类实例,this === son',);
      }
    }
    class Son extends Parent {
      constructor(...props) {
        super(...props);
        this.z = 4;
      }
    }
    Son.getY();
    console.log( "-----------以上类访问,以下实例访问-----------" );
    new Son(2,3).getX();
2、TS的类、继承、实例
  知识来源,https://www.tslang.cn/docs/handbook/classes.html
  代码运行,https://www.json.cn/run/typescript/
  附、没有重叠,have no overlap
  附、修饰符(限制数据的使用范围) //普通函数,无修饰符,默认为public
    A、公共public:(子)类内可访问、(子)实例可访问
    B、受保protected:(子)类内可访问
    C、私有private:类内可访问
  (1)static函数:类调用,this指向类;子类调用,this不指向任何
  (2)普通函数:实例调用,this指向实例;子实例调用,this不指向任何
  (3)示例:
    class Parent {
      public publicA: string = 'aaa';
      protected protectedA: string = '';
      private privateA: string;
      constructor(publicA: string, protectedA: string) {
        this.publicA = publicA;
        this.protectedA = protectedA;
        this.privateA = 'privateA';
        console.log( '私有属性只能在自身的类内访问,如'+this.privateA );
      };
      static sayHello() {
        console.log('通过父类访问静态,this就是父类,this === Parent',);
        console.log('通过子类访问静态,this不是子类,this != Son',);
      }
      sayWorld() {
        console.log('通过父类访问静态,this就是父类,this === parent',);
        console.log('通过子类访问静态,this不是子类,this != son',);
      }
    }
    class Son extends Parent {
      public publicB: string;
      protected protectedB: string;
      private privateB: string;
      constructor(publicC: string, protectedC: string, privateC: string) {
        super(publicC,protectedC);
        console.log("子类可-以访问:"+this.publicA+",public")
        console.log("子类可-以访问:"+this.protectedA+",protected")
        console.log("子类不-可访问:私有,private")
        this.publicB = publicC;
        this.protectedB = protectedC;
        this.privateB = privateC;
        console.log("类内可-以访问:"+this.publicB+",public")
        console.log("类内可-以访问:"+this.protectedB+",protected")
        console.log("类内可-以访问:"+this.privateB+",private")
      }
    }
    Son.sayHello();
    var son = new Son("公共", "保护", "私有");
    son.sayWorld()
    console.log('通过实例,只能访问('+son.publicA+'-'+son.publicB+')属性');

八、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、时区
  (1)GMT,世界时,格林尼治标准时间
  (2)UTC,世界标准时间,接近世界时的时间计量系统
  (3)获取本地区时
    var getTimezoneOffset = function(){
      var date = new Date();
      var offset = date.getTimezoneOffset();//世界标准时间-本地地方时的分钟差值
      var timezone = -(offset / 60); //把分钟转换为小时
      return timezone
    };
2、日期时间的格式化-框架内置
  (1)过滤
    $filter('date')(
      new Date(new Date().getTime() - 2 * 60 * 1000),
      'yyyy-MM-dd HH:mm:ss'
    );
  (2)获取中国当前标准时间
    console.log(new Date())   
3、获取当前毫秒数的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]);
  }
4、参数问题
  (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);
    }
5、日期时间的格式化-手写
  (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,
      };
    } 
6、日期时间中的汉字和分隔符互相转换
  (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));
7、现在时间和倒计时:
  <!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检测数据类型的几种方法
 来源:https://www.cnblogs.com/dushao/p/5999563.html
1、数据类型
  (1)五种基本数据类型:number、string、boolean、undefined(变量声明未赋值)、null(变量声明赋此值,typeof为'object')
  (2)两种引用数据类型:函数、对象(实例获取的途径有,数组字面量、对象字面量、new类)
  (3)使用从未声明的变量a,报错为"a is not defined"
  (4)x=x+5;
    在数学领域,下一步移项
    在计算机领域,下一步计算、赋值
2、null与对象
  var value;
  console.log( value == null ) // 为true,则value为null
  console.log( typeof value == "object" && value ) // 为true,则value为普通对象
3、void,操作符,计算但不返回值,等同于undefined,
  用于html:<a href="javascript:void(0)">单击此处什么也不会发生</a>
  用于JS:var abc; if(abc !== void 0)//if(abc !== undefined)
  console.log(void 0 === undefined && void(0) === undefined) //true
4、arguments,实参的副本
  (1)是类数组,
    A、模拟如下
      var argument = {
        0: 'param0',
        1: 'param1',
        2: 'param2',
      }
    B、使用前,应通过Array.prototype.slice.call(arguments)转换为数组,
    C、数组方法slice的伪定义
      Array.prototype.slice = function(start,end){
        var result = new Array();
        start = start || 0;
        end = end || this.length; // this指向arguments
        for(var i = start; i < end; i++){
          result.push(this[i]);//当this为数组时,i为index;当this为对象时,i为key;非常重要!!!
        }
        return result;
      }
  (2)示例
    function fn(){
      console.log( arguments );//[Arguments] { '0': 8, '1': 8, '2': 8 }
      //以下用对象的方法操作arguments
      console.log( '1:',Object.keys(arguments) );//[ '0', '1', '2' ],
      console.log( '2:',Object.values(arguments) );//[ 8, 8, 8 ]
      console.log( '3:',arguments.hasOwnProperty(0) );//true
      console.log( '4:',arguments.hasOwnProperty('0') );//true
      //以下用数组的方法操作arguments
      console.log( '5:',arguments.length );//3
      console.log( '6:',Array.prototype.slice.call(arguments)  );//[ 8, 8, 8 ]
      //以下检测arguments的数据类型
      console.log( '7:',typeof arguments === 'object' );//object
      console.log( '8:',arguments instanceof Object) // true
      console.log( '9:',arguments.constructor === Object) // true
      console.log( '10:',Object.prototype.toString.call(arguments));//[object Arguments]
      console.log( '11:',Object.prototype.toString.call([ 8, 8, 8 ]));//[object Array]
      console.log( '12:',Object.prototype.toString.call({ '0': 8, '1': 8, '2': 8 }));//[object Object]
    }
    fn(8,8,8)
5、四种检测方式    
  附、被检测的数据
    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; 

十三、预解释、闭包、内存泄漏、函数调用、高阶函数、重载
1、预解释,在代码执行之前,JavaScript引擎会先对代码进行一次扫描,将变量声明和函数声明提升到当前作用域的顶部
2、闭包
  (1)定义,能够读取其他函数内部变量的函数,即函数里的函数
  (2)闭包优缺点
    A、好处:避免污染全局变量;
    B、坏处:有可能造成内存泄漏;
3、内存泄漏的情形
  (1)意外的全局变量
  (2)未解除的事件监听器
  (3)未移除的定时器或回调函数
  (4)闭包,一个函数作为函数的执行结果被一个变量引用
  (5)自执行函数,一个对象作为函数的执行结果,被另一个函数的变量引用,以下来自aplus.js
    ! function(e) {
      function t(a) {
        if (o[a]) return o[a].exports;
        var n = o[a] = {
          exports: {},
          id: a,
          loaded: !1
        };
        return e[a].call(n.exports, n, n.exports, t), n.loaded = !0, n.exports
      }
      var o = {};
      return t.m = e, t.c = o, t.p = "", t(0)
    }([function(e, t, o) {
      "use strict";
      ! function() {
        var e = window.goldlog || (window.goldlog = {});
        e._aplus_plugin_cctv || (
          e._aplus_plugin_cctv = {
            status: "complete"
          }, o(1).run()
        )
      }()
    }, function(e, t, o) {
      "use strict";
      function a() {
        var e = l.getCookie("userSeqId");
        if (e) {
          var t = document.getElementById("tb-beacon-aplus") || document.getElementById("beacon-aplus");
          if (t) {
            var o = t.getAttribute("exparams"),
              a = "uidaplus=" + e;
            o = o ? o.replace(/&aplus&/, "&" + a + "&aplus&") : a + "&aplus&sidx=aplusSidex", t.setAttribute("exparams", o)
          }
        }
        return e
      }
      function n() {
        var e = {};
        try {
          var t = goldlog.getMetaInfo("aplus-rhost-g-map");
          "string" == typeof t ? e = JSON.parse(t) : "object" == typeof t && (e = t)
        } catch (t) {
          e = {}
        }
        return e
      }
      function r(e, t) {
        var o = n();
        return o && o[t] ? "//" + o[t] + t : e
      }
      var s = o(2),
        l = o(3);
      t.run = function() {}
    }]);
4、函数的执行方式 
  (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);
      }()];
    E、示例,减少全局变量
      示例1、
        for(var i=0; i<aBtn.length; i++){
          (function(index){
            aBtn[index].onclick=function click(){
              alert(index);
            }
          })(i);
        }
      示例2、
        for(var i=0; i<6; i++){
          (function(index){
            setTimeout(function(){
              console.log(i);
              console.log(index);
            })
          })(i);
        }
      示例3、
        (function (allFn) { //连续3次自执行 
          allFn()
        })(function () {
          return (function outerFn(m) {
            function recursion(n) {
              console.log( n );
              return n
            }
            recursion(m)
            return recursion
          })(1)(16)
        });
   附、变量滞后,预解释与执行顺序
    function n() { //第3步执行
      console.log(o);
    }
    var o = {}; //第1步执行
    n() //第2步执行 
5、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)同时成立
    for(var i=0,j=0;i<10,j<6;i++,j++){//i<10,j<6;同时成立
      var k=j+i;
    }
    console.log(i,j,k);//6,6,10
  (3)函数
    function m() {
      return 1,2,3
    }
    console.log(m());//3
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、柯里化函数:函数里面返回函数,函数执行过后再执行,第一次执行公共部分
  (1)示例一
    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));
  (2)示例2
    function fn(a, b) {
      return function inner(type) {
        let res
        switch (type) {
          case '+': res = a + b; break;
          case '-': res = a - b; break;
          case '*': res = a * b; break;
          case '/': res = a / b; break;
        }
        return res
      }
    }
    var f = fn(100, 200)// f 接受到的就是 inner 函数
    console.log(f('+'))
    console.log(f('-'))
    console.log(f('*'))
    console.log(f('/'))
4、点透
  (1)为什么移动端浏览器的click事件延迟300毫秒执行?
    A、原因:要观察用户是否进行第2次点击,进而判断用户是想单击页面,还是想双击缩放页面
  (2)点透发生的情形:
    A、click移动端浏览器的元素,立即触发touch事件,
    B、如果touch事件的执行结果是该元素从文档树上消失,a或input元素出现
    C、那么300ms后click事件会发生在a或input元素上
    D、这个现象给人的感觉就是“点透”了
  (3)点透的解决方案:
    A、在touch事件里,延迟执行touch事件的代码或阻止默认的click事件
    B、定时器延迟
      ele.addEventListener("touchend",function (event) {
          setTimeout(function () {
              ele.style.display="none";
          },400);
      });
      //以下zepto的tap事件点透解决
      ele.on("touchend", function () {
          setTimeout(function () {
            $("#alertBox").hide();
          }, 350);
      });
    C、阻止默认事件
      ele.addEventListener("touchend",function (event) {
          ele.style.display="none";
          event.preventDefault();
      });
      //以下zepto的tap事件点透解决
      ele.on("touchend", function (e) {
          $("#alertBox").hide();
          e.preventDefault();
      });

十五、循环和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时就会终止循环。
    }
  (4)for循环特殊案例
    A、外部可读
      for(var i=0,j=0;i<10,j<6;i++,j++){//i<10,j<6;同时成立
        var k=j+i;
      }
      console.log(i,j,k);//6,6,10
    B、外部不可读
      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
    C、只终止内层
      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)
        }
      }
2、while循环
  (1)执行代码放在while前的do里
    var i = 0;
    do {
      i++;
      console.log(i)
    }
    while (i < 4);
  (2)执行代码放在while后
    var i=4;
    while (i--) {//条件
      console.log(i)
    }
3、if成立,执行代码 
  (1)if(isTrue)done()
  (2)isTrue && done();
4、with语句,用法示例,含代码执行效率
  //初始数据
  var foo = {
    bar: {
      data: {
        x: 0,
        y: 0,
        z: 0,
      }
    }
  }
  var index = 3
  if(index === 1){
    //赋值方式1
    console.time('赋值方式1耗时');
    foo.bar.data.x = 1;
    foo.bar.data.y = 2;
    foo.bar.data.z = 3;
    console.log( foo.bar.data.x, foo.bar.data.y, foo.bar.data.z);
    console.timeEnd('赋值方式1耗时');
  }else if(index === 2){
    //赋值方式2
    console.time('赋值方式2耗时');
    with (foo.bar.data){
      x = 11;
      y = 22;
      z = 33;
    }
    with (foo.bar.data){
      console.log( x, y, z );
    }
    console.timeEnd('赋值方式2耗时');
  }else if(index === 3){
    //赋值方式3
    console.time('赋值方式3耗时');
    with(foo)with(bar)with(data){
      x = 111;
      y = 222;
      z = 333;
    }
    with(foo)with(bar)with(data){
      console.log( x, y, z );
    }
    console.timeEnd('赋值方式3耗时');
  }
  //console.error('111', '222', '333'); 
   
十六、对象、原型链(__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 prototype = { proto: 'proto' };
    var props = {
      attr: {
        enumerable: true,
        value: 'attr'
      }
    };
    var obj = Object.create( prototype, props )
    console.log( obj )
    console.log( obj.attr )
    console.log( obj.proto )
    console.log( obj.__proto__ === prototype, 'obj.__proto__ === prototype' )
7、Object.defineProperty(myObject, prop, descriptor)
  来源,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
  (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: 'myValue',
        writable: true,
      });
      myObj.key = 'youValue';
      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);
    C、访问器-描述符 
      var eric={
        ename:"艾力克",
        eage:25
      };
      Object.defineProperties(eric,{
        _eage:{
          value:eric.eage,
          writable:true
        },
        eage:{
          get:function(){
            return this._eage;
          },
          set:function(value){
            if(value>=18 && value<=65){
              this._eage=value;
            }else{
              throw Error("年龄必须介于18~65岁之间");
            }
          },
          enumerable:true,
          configurable:false
        }
      });
      console.log(eric.eage);//25
      eric.eage=18;
      console.log(eric.eage);//18
      eric.eage=16;
      console.log(eric.eage);//超出范围,报错 
  (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)空字符串,返回NaN
  (2)忽略字符串前面的空格,直到找到第一个非空格字符,第一个小数点后面的字符被忽略
    A、如果第一个字符不是数字或者负号,返回NaN
    B、如果第一个字符是数字字符,继续解析第二个字符,直到解析完所有后续字符串或者遇到了一个非数字字符
  (3)进制,默认10
  (4)示例
    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)空字符串,返回NaN
  (2)忽略字符串前面的空格,直到找到第一个非空格字符,第二个小数点后面的字符被忽略
    A、如果第一个字符不是数字或者负号,返回NaN
    B、如果第一个字符是数字字符,继续解析第二个字符,直到解析完所有后续字符串或者遇到了一个非数字字符
  (3)进制,只解析十进制,因此它没有第二个参数指定基数的用法
  (4)如果字符串中包含的是一个可解析为正数的数(没有小数点,或者小数点后都是零),返回整数
  (5)示例
    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前端工程师_钱成  阅读(3598)  评论(0编辑  收藏  举报