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、数据层:负责数据存储、读取