7、svg、D3、ES6|Proxy|await、Common与ES6解构变量、TS泛型接口|类继承实例、jQuery、Error错误、日期时间|倒计时|时区区时、数据类型|类数组|数字转化|对象相关with性能、函数之预解释|闭包|内存泄漏、节流去抖|柯里化|点透、循环for、设计模式、业务逻辑接口(3950行)

一、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、解构赋值
  (1)解构对象
    var person = {name: 'zhangsan', address: {province: '江苏', city: '南京'}};
    var {name, address:{province, city}} = person;//有key,无value
    console.log(name, province, city);
  (2)解构数组
    var arr = [1, 2, 3];
    var [a, b, c] = arr;
    console.log(a, b, c);
5、var、let、const的区别(变量提升、声明1次、块级作用域) 
  (1)变量提升,var有变量提声,let和const没有变量提声
  (2)同一变量的声明次数,var多次,let和const1次
  (3)块级作用域,
    A、var没有块级作用域,{}内外是同一作用域
    B、let和const有块级作用域,{}内外是子父作用域
    C、示例
      if(true){
        let x = 5;
      }else{
        var x = 10;
      }
      console.log(x);
  (4)重新赋值,var和let能重新赋值,const不能重新赋值
    A、const对象变量的组成可以重新赋值
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中模块示例
  注、as后面为本文件使用名称
  (1)默认导出和导入
    A、导出,mathUtils.js,只能导出1次
      export default {
        add,
        subtract,
        multiply
      };//下面是错误用法
      export default add;
      export default subtract;
      export default multiply;
    B、导入,导入时可以自定义名称
      import mathUtils from './mathUtils.js';
  (2)*导出和导入
    A、导出,namedUtils.js
      export {
        add,
        subtract,
        multiply
      };//下面是正确用法
      export add;
      export subtract;
      export multiply;
    B、导入,使用通配符导入整个模块,并将其解构为abc1,ABC1
      import * as namedUtils from "./abc";
      //以下进一步使用,两种任选一种
      //import {add,subtract,multiply} = namedUtils;
      //namedUtils.add,namedUtils.subtract,namedUtils.multiply
  (3)普通导出和导入
    A、导出,mathUtils.js,可以导出多次
      const add = (a, b) => a + b;
      const subtract = (a, b) => a - b;
      const multiply = (a, b) => a * b;
      export {
        add,
        subtract,
        multiply
      };//此效果等同于下面效果
      export add;
      export subtract;
      export multiply;
    B、导入,导入时使用相同的名称,必须使用--------花括号
      import { add, subtract, multiply } from './mathUtils.js';
  (4)默认和普通同时导入
    A、import mathUtils, { add1, subtract1 } from './mathUtils.js';
  (5)*和普通同时导入
    A、import a from './d.js'; // 导入默认导出
    B、import * as rest from './d.js'; // 导入除了默认导出之外的所有变量
  (6)改名导出和导入
    A、导出
      export {abc as abc1, ABC as ABC1}
    B、导入
      import {abc1,ABC1} from "./abc";
      import {abc1 as abc2, ABC1 as ABC2} from "./abc";
3、ESModule和CommonJS的区别
  (1)语法不同
    A、ESModule用export(没有s)和import导出和导入,import在编译时加载,必须放在开头
    B、CommonJS用module.exports和require导出和导入,require在运行时加载,可以放在任何位置
    C、import和‌require可以代替对方使用
  (2)注意
    A、都可以在现代浏览器中使用
    B、早期的node要用vue-loader把.vue文件转为ES6模块
    C、2019年发布的node13.2.0,开始支持ES6模块,此后package.json中无需添加vue-loader依赖
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,用function定义
        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 = () => {
          let result = [];
          for (let i = 0; i < 3; i++) {
            result[i] = 'x';
          }
          return result;
        }
        console.log( createArray() ); 
      //写法3,用=>定义,有参数,普通
        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') ); 
      //写法4,用=>定义,有参数,解构
        export default ({ command, mode }: ConfigEnv): UserConfig => {
          ({ command, mode }: ConfigEnv),这部分是函数的参数。接收对象,对象的类型为ConfigEnv,解构出两个属性command和mode
          UserConfig => {},这部分是函数的返回值。类型是UserConfig
          return {};
        };
      */
    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日期时间
1、new Date()、GMT时间、UTC时间
  (1)在浏览器上返回当地时间,如“Tue Dec 17 2024 14:24:26 GMT+0800 (中國標準時間)”
    A、GMT+0800 (中國標準時間),是说明文字,意思是,左侧时间早GMT时间8小时
    B、GMT时间
      a、Greenwich Mean Time,格林尼治标准时间,以英国伦敦格林尼治天文台为基准,受地球自转和季节变化的影响
      b、全球统一的时间标准之一,本地时间可以通过该时间和时区转换
  (2)在服务器上返回UTC时间,如“2024-12-17T06:17:48.399Z”
    A、T,日期和时间的分隔符
    B、.399,秒的小数部分
    C、Z,是说明文字,意思是,左侧时间是UTC时间
    D、UTC时间
      a、Coordinated Universal Time,协调世界时,以原子钟为基准,不受地球自转和季节变化的影响
      b、全球统一的时间标准之一,本地时间可以通过该时间和时区转换
  (3)UTC和GMT的关系,
    A、UTC是在GMT基础上发展而来,比GMT更精确
    B、不涉及高精度时间要求的情况下,UTC和GMT可以视为相同
  (4)获取当前毫秒数的5种方法:
    var str1 = Date.now();
    var str2 = +new Date();
    var str3 = new Date() * 1;
    var str4 = new Date().getTime();
    var str5 = new Date().valueOf();
    var str = [str1, str2, str3, str4, str5];
    for (var i = 0; i < str.length; i++) {
      console.log(str[i]);
    }
2、Date的参数
  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",
  ];
  for (var i = 0; i < array.length; i++) {
    var date = new Date(array[i]);
    str = "参数为:" + array[i] +  ",你获取的年、月、日分别是:" + date.getFullYear()+ "、" + date.getMonth()+  "、" + date.getDate();
    console.log(str);
  }
  console.log( "以上,‘系统’把参数按照‘实际’的年月日处理,返回‘系统’的年月日(月份比‘实际’小1)" );
  {
    var date = new Date();
    var str = "参数为:" + "空" +  ",你获取的年、月、日分别是:" + date.getFullYear()+ "、" + date.getMonth()+  "、" + date.getDate();
    console.log(str);
  }
  console.log( "以上,返回‘系统’的年月日(月份比‘实际’小1)" );
  {
    var milliseconds = new Date().getTime();
    var date = new Date(milliseconds);
    var str = "参数为:" + milliseconds +  ",你获取的年、月、日分别是:" + date.getFullYear()+ "、" + date.getMonth()+  "、" + date.getDate();
    console.log(str);
  }
  console.log( "以上,返回‘系统’的年月日(月份比‘实际’小1)" );
  {
    var ary = [2023, 10, 22, 10];
    var date = new Date(...ary);
    var str = "参数为:" + ary +  ",你获取的年、月、日分别是:" + date.getFullYear()+ "、" + date.getMonth()+  "、" + date.getDate();
    console.log(str);
  }
  console.log( "以上,‘系统’把参数按照‘系统’的年月日处理,返回‘系统’的年月日(参数的月份与获取的月份一致)" );
3、获取当前浏览器的时区
  function getTimezoneOffset(){
    var date = new Date();
    var offset = date.getTimezoneOffset();//世界标准时间-本地区时=分钟差
    var local = '';
    var timezone = '';
    var isFlag = false;
    var obj = {
      '东': [-60, -120, -180, -240, -300, -360, -420, -480, -540, -600, -660, -720],
      '西': [60, 120, 180, 240, 300, 360, 420, 480, 540, 600, 660, 720],
    }
    for(var key in obj){
      if(obj[key].indexOf(offset) > -1){
        isFlag = true;
        local = key;
        break;
      }
    }
    if(isFlag){
      timezone = '你目前处于' + local + Math.abs(offset / 60) + '区';
    }else{
      timezone = '你目前处于0时区';
    }
    return timezone;
  };    
  console.log( getTimezoneOffset() ); 
3、在任意地方获取任意经度的区时(不是地方时)
  function getLocalTime( longitude ){
    // 验证,https://time.org.cn/,点击“切换”,选择“东经/西经”、输入经度、点击“确定”
    if(typeof longitude != 'number'){
      return '错误提示:经度必须是一个数字'
    }
    if(longitude < -180 || longitude > 180){
      return '错误提示:经度必须在 -180 到 180 之间'
    }
    function addZero(num){
      return num < 10 ? '0'+num : num;
    }
    function addZero2(num){
      var length = num.toString().length;
      return ['', '00'+num, '0'+num, num][length] ;
    }
    // 以下规定,longitude的值,为负表示西经,为正表示东经
    var sign = longitude > 0 ? 1 : -1;
    var textFront = longitude == 0 ? '' : longitude < 0 ?  '西经' : '东经';
    var textBack = longitude == 0 ? '经线' :'';
    var textFull = '在'+textFront+addZero2(Math.abs(longitude))+'度'+textBack+',现在的区时是:';
    // 以下获取,目标时区
    if(longitude < 0) longitude = -longitude;
    var timezone = Math.floor(longitude / 15) ;
    var oneHour = longitude % 15 > 7.5 ? 1 : 0;
    var targetZone = timezone+oneHour;
    // 以下获取,当地日期对象
    var date = new Date();
    // 以下获取,当地到UTC的毫秒数
    var localToUtcMill  = date.getTimezoneOffset() * 60 * 1000; //把分钟转换为毫秒
    // 以下获取,UTC到目标的毫秒数
    var utcToTargetMill = targetZone * sign * 60 * 60 * 1000; //把小时转换为毫秒
    // 以下获取,当地到目标的毫秒数
    var localToTargetMill = localToUtcMill+utcToTargetMill;
    // 以下获取,目标的毫秒数
    var targetMill = date.getTime()+localToTargetMill;
    // 以下获取,目标的年、月、日、时、分、秒
    var targetDate = new Date(targetMill);
    var targetYear = targetDate.getFullYear();
    var targetMonth = addZero(targetDate.getMonth()+1) ;
    var targetDay = addZero(targetDate.getDate()) ;
    var targetHour = addZero(targetDate.getHours()) ;
    var targetMinute = addZero(targetDate.getMinutes()) ;
    var targetSecond = addZero(targetDate.getSeconds()) ;
    var targetDateTime = targetYear+'-'+targetMonth+'-'+targetDay+' '+targetHour+':'+targetMinute+':'+targetSecond;
    // 以下返回,最终结果
    return textFull+targetDateTime
  };
  for( var i = -180; i <= 220; i+=15 ) {
    console.log( getLocalTime(i) );
  }
4、获取日期和时长
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>获取日期和时长</title>
      <style>
        .display{
          padding: 50px;
          line-height: 50px;
        }
      </style>
    </head>
    <body>
      <div class="display">
        <div id="durationId1"></div>
        <div id="datetimeId1"></div>
      </div>
      <div class="display">
        <div id="datetimeId2"></div>
        <div id="durationId2"></div>
      </div>
    </body>
    <script>
      //以下,定义函数-获取日期
      function newDatetime(outMilliseconds) {
        function addZero(number) {
          return number.toString()[1] ? number : "0" + number;
        }
        var now = new Date();
        var nowMilliseconds = now.getTime();
        var distance = nowMilliseconds + outMilliseconds;
        var dateNew = new Date(distance);
        var year  = dateNew.getFullYear();
        var month  = addZero(dateNew.getMonth() + 1);
        var date = addZero(dateNew.getDate());
        var hour = addZero(dateNew.getHours());
        var minute = addZero(dateNew.getMinutes());
        var second = addZero(dateNew.getSeconds());
        var obj = {};
        obj.client = year + "年" + month + "月" + date + "日" + hour + "时" + minute + "分" + second + "秒";
        obj.server = year + "-" + month + "-" + date + " " + hour + ":" + minute + ":" + second;
        return obj
      }
      //以下,定义函数-获取时长
      function durationTime(datetime) {
        function addZero(number) {
          return number.toString()[1] ? number : "0" + number;
        }
        var now = new Date();
        var nowMilliseconds = now.getTime();
        var outMilliseconds = new Date(datetime).getTime();
        var distance = nowMilliseconds - outMilliseconds;
        if(distance < 0) distance = -distance;
        var seconds = Math.floor(distance/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 client = "";
        var server = "";
        var obj = {};
        if (day > 0) {
          client += day + "天";
          server += day + " ";
        }
        if (day > 0 || hour > 0) {
          client += addZero(hour) + "小时";
          server += addZero(hour) + ":";
        }
        if (day > 0 || hour > 0 || minute > 0) {
          client += addZero(minute) + "分";
          server += addZero(minute) + ":";
        }
        if (day > 0 || hour > 0 || minute > 0 || second > 0) {
          client += addZero(second) + "秒";
          server += addZero(second);
        }
        obj.client = client;
        obj.server = server;
        return obj
      }
      //以下,定义函数-服务端写法改为客户端写法
      function serverToClient(dateTime) {
        dateTime = dateTime.replace('-', "年").replace('-', "月").replace(' ', "日")
        dateTime = dateTime.replace(':', "时").replace(':', "分") + "秒"
        return dateTime
      }
      // 以下,获取日期
      var durationStr = '1000 * 60 * 60 * 24'; //为正获取未来时间,为负获取过去时间,为0获取当前时间
      var durationNum = eval(durationStr);
      var durationEle1 = document.getElementById('durationId1');
      var datetimeEle1 = document.getElementById('datetimeId1');
      // 以下,获取时长
      var datetimeStr = '2022-02-24 11:00:00'; //早于现在为持续时长,晚于现在为倒计时;
      var datetimeDesc = '<br>开始的<b>俄乌战争</b>,到现在';
      var datetimeEle2 = document.getElementById('datetimeId2');
      var durationEle2 = document.getElementById('durationId2');
      function interval() {
        var now = newDatetime(0).client;//durationEle1.textContent
        durationEle1.innerHTML = '距现在<br>'+ now + '<br>' + durationNum + '(' + durationStr + ')毫秒,是';
        datetimeEle1.innerHTML = newDatetime(durationNum).client;
        datetimeEle2.innerHTML = '北京时间<br>'+ serverToClient(datetimeStr) + datetimeDesc + '<br>' + now + '<br>已经持续了';
        durationEle2.innerHTML = durationTime(datetimeStr).client;
      }
      interval()
      // 以下,开启定时器
      setInterval(function(){
        interval()
      },1000);
      // 以下,
    </script>
  </html>
5、年龄计算,
  function getAge(birthYear){
    var nowYear = new Date().getFullYear();
    var age = nowYear - birthYear;
    var text = '此人出生于' + birthYear + '年,今年' + age + '岁';
    return text;
  }
  console.log( getAge(1955) );
5、是4的子级,可以删除
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>俄乌战争持续时长</title>
      <style>
        .display{
          padding: 50px;
          line-height: 50px;
        }
      </style>
    </head>
    <body>
      <div class="display">
        <div id="datetimeId2"></div>
        <div id="durationId2"></div>
      </div>
    </body>
    <script>
      function nowDatetime() {
        function addZero(number) {
          return number.toString()[1] ? number : "0" + number;
        }
        var now = new Date();
        var year  = now.getFullYear();
        var month  = addZero(now.getMonth() + 1);
        var date = addZero(now.getDate());
        var hour = addZero(now.getHours());
        var minute = addZero(now.getMinutes());
        var second = addZero(now.getSeconds());
        var client = year + "年" + month + "月" + date + "日" + hour + "时" + minute + "分" + second + "秒";
        return client
      }
      function durationTime(datetime) {
        function addZero(number) {
          return number.toString()[1] ? number : "0" + number;
        }
        var now = new Date();
        var nowMilliseconds = now.getTime();
        var outMilliseconds = new Date(datetime).getTime();
        var distance = nowMilliseconds - outMilliseconds;
        if(distance < 0) distance = -distance;
        var seconds = Math.floor(distance/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 client = "";
        if (day > 0) client += day + "天";
        if (day > 0 || hour > 0) client += addZero(hour) + "小时";
        if (day > 0 || hour > 0 || minute > 0) client += addZero(minute) + "分";
        if (day > 0 || hour > 0 || minute > 0 || second > 0) client += addZero(second) + "秒";
        return client
      }
      function serverToClient(dateTime) {
        dateTime = dateTime.replace('-', "年").replace('-', "月").replace(' ', "日")
        dateTime = dateTime.replace(':', "时").replace(':', "分") + "秒"
        return dateTime
      }
      var datetimeStr = '2022-02-24 11:00:00'; 
      var datetimeDesc = '<br>开始的<b>俄乌战争</b>,到现在';
      var datetimeEle2 = document.getElementById('datetimeId2');
      var durationEle2 = document.getElementById('durationId2');
      function interval() {
        var now = nowDatetime();
        datetimeEle2.innerHTML = '北京时间<br>'+ serverToClient(datetimeStr) + datetimeDesc + '<br>' + now
        durationEle2.innerHTML = '已经持续了<br>' + durationTime(datetimeStr);
      }
      interval()
      setInterval(function(){
        interval()
      },1000);
    </script>
  </html>

十二、js数据类型与四种检测方法
 来源:https://www.cnblogs.com/dushao/p/5999563.html
1、数据类型
  (1)5种基本数据类型:number、string、boolean、undefined(变量声明未赋值)、null(变量声明赋此值,typeof为'object')
  (2)2种引用数据类型:函数、对象(Array、Arguments、Object、Date、Error、RegExp)
  (3)使用从未声明的变量a,报错为"a is not defined"
  (4)x=x+5;
    在数学领域,x是未知数,下一步移项
    在计算机领域,x是变量,下一步右边的计算结果、赋值给左边
  (5)null与对象
    var value;
    console.log( value == null ) // 为true,则value为null
    console.log( typeof value == "object" && value ) // 为true,则value为普通对象
  (6)操作符(运算符)
    A、常见操作符,如,算术操作符、比较操作符、逻辑操作符、赋值操作符、位操作符、
    B、其他操作符,如,instanceof、typeof、void、三元操作符
    C、B中的前3个,都有两种效果相同的写法,以void为例,‘void 0’和‘void(0)’
    D、void(0),计算但不返回值,等同于undefined,
    E、html示例,<a href="javascript:void(0)">单击此处什么也不会发生</a>,
    F、js示例,console.log(void(0) === undefined) //true
2、arguments,实参的副本
  (1)数据类型是类数组,对象的一种,Arguments与、Array、Object、Date、Error、RegExp、Function、类似
    A、模拟如下
      var argument = {
        0: 'param0',
        1: 'param1',
        2: 'param2',
        length: 3,
      }
    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; // 如A,当this指向arguments时,this.length即end为3
        for(var i = start; i < end; i++){ 
          result.push(this[i]);// 如A,分别是,this[0]、this[1]、this[2],没有this['length']
        }
        return result;
      }
  (2)arguments的数据类型检测
    function fn(){
      console.log( arguments );//[Arguments] { '0': 8, '1': 8, '2': 8 }
      console.log( '以下用对象的方法操作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
      console.log( '5:',arguments.hasOwnProperty('length') );//true
      console.log( '以下用数组的方法操作arguments:');
      console.log( '6:',arguments.length );//3
      console.log( '7:',Array.prototype.slice.call(arguments)  );//[ 8, 8, 8 ]
      console.log( '以下检测arguments的数据类型:');
      console.log( '8:',typeof arguments === 'object' );//object
      console.log( '9:',arguments instanceof Object) // true
      console.log( '10:',arguments.constructor === Object) // true
      console.log( '11:',Object.prototype.toString.call(arguments));//[object Arguments]
      console.log( '以下检测-其它引用数据-的数据类型:');
      console.log( '12:',Object.prototype.toString.call([ 8, 8, 8 ]));//[object Array]
      console.log( '13:',Object.prototype.toString.call({ '0': 8, '1': 8, '2': 8 }));//[object Object]
      console.log( '14:',Object.prototype.toString.call(new Object()));//[object Object]
      console.log( '15:',Object.prototype.toString.call(new Date()));//[object Date]
      console.log( '16:',Object.prototype.toString.call(new Error()));//[object Error]
      console.log( '17:',Object.prototype.toString.call(/test/));//[object RegExp]
      console.log( '18:',Object.prototype.toString.call(function(){}));//[object Function]
    }
    fn(8,8,8)
3、四种检测方式    
  附、被检测的数据
    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();
    console.log("(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
    }
    console.log("(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 
    console.log("(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 
    console.log("(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; 

十三、数字转化
原文地址: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、三个基本特征
  (1)封装,可以隐藏实现细节,使得代码模块化,实现代码重用
  (2)继承,可以扩展已存在的代码模块(类),实现代码重用
  (3)多态,实现接口重用
2、对象基本概念(面对对象编程)
  (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
3、特殊原型链示例
  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;//相对这种方式来说,上边的写法更安全
4、with语句与性能检测
  (1)缺点,可能会导致数据泄漏到全局作用域     
  (2)with语句,将代码的作用域设置到一个特定的对象中
    console.time('这是性能检测,参数必须一样');
      var foo = {
        bar: {
          data: {
            x: 0,
            y: 0,
            z: 0,
          }
        }
      }
      //1、以下不用with分别打点,赋值与获取
        foo.bar.data.x = 1111;
        foo.bar.data.y = 2222;
        foo.bar.data.z = 3333;
        console.log( foo.bar.data.x, foo.bar.data.y, foo.bar.data.z );
      //2、以下用1个with打点,赋值与获取
        with(foo.bar.data){
          x = 111;
          y = 222;
          z = 333;
        }
        with(foo.bar.data){
          console.log( x, y, z );
        }
      //3、以下用3个with连用,赋值与获取
        with(foo)with(bar)with(data){
          x = 11;
          y = 22;
          z = 33;
        }
        with(foo)with(bar)with(data){
          console.log( x, y, z );
        }
    console.timeEnd('这是性能检测,参数必须一样');
5、多态
   附、同一操作作用于不同的对象上面,可以产生不同的执行结果。比如你说“叫”,鸭子听了会发出“嘎嘎声”,鸡听了会发出“咯咯声”。
  (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());
    另外,组件化开发的组件就相当于一个“对象”,一个操作“封装”成一个函数,通过调用内置方法“继承”内部的功能,“多态”没有体现
6、创建对象
  (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);
7、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' )
8、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();
      },
    });

十五、函数相关
1、相关定义
  (1)函数:一段可以重复使用的代码块
  (2)高阶函数:操作其他函数的函数,比如map、forEach
  (3)重载:Java一个函数可以有多个定义,只要这每个定义的签名(参数的类型和数量)不同即可。JS没有签名,所以不能重载
  (4)预解释:在代码执行之前,JavaScript引擎会先对代码进行一次扫描,将变量声明和函数声明提升到当前作用域的顶部
  (5)闭包:能够读取其他函数内部变量的函数,即函数里的函数;好处,避免污染全局变量;坏处,有可能造成内存泄漏;
2、内存泄漏的情形
  (1)意外的全局变量
  (2)未解除的事件监听器
  (3)未移除的定时器或回调函数
  (4)闭包,一个函数作为函数的执行结果被一个变量引用
3、函数执行(函数调用)方式 
  (1)直接调用;
    function aaa (){
      return '返回值' //return row.text = "发布",先给变量赋值,再返回变量 
    }
    aaa ()
  (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步执行 

十六、节流、去抖、柯里化、点透
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();
      });

十七、循环
附、JS的逗号,用来将多个表达式连接为一个表达式,新表达式的值为最后一个表达式的值
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 (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)
        }
      }
  (5)多条件同时成立
    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
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)
    }
   
十八、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

十九、业务逻辑
附、接口:url、method、参数、返回值
注、软件一般分为三个层次:表现层、业务逻辑层、数据层
1、表现层:负责界面展示
2、业务逻辑层:接收表现层的请求,向数据层请求数据,将数据返给表现层
  (1)业务,应用程序中的具体任务。前端程序员不仅要关注展示,还应关注业务,弄清不同业务之间的关系
  (2)业务逻辑,应用程序中,实现具体任务的代码
3、数据层:负责数据存储、读取
  
  

  

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