7、svg、D3、ES6相关、TS相关、jQuery相关、Error与try|catch、Date日期时间、判断数据类型void、预解释|闭包|内存泄漏|函数执行|高阶|重载、节流|去抖|柯里化、点透、for|while循环|if条件|逗号、对象|原型链|多态|创建|create|defineProperty、JS设计模式、parseFloat、业务逻辑、造轮子(3400行)
一、svg <html> <head> <meta charset="utf-8"> <title>svg子标签</title> <style> .color{ color: rgb(185, 125, 125); } .fontWeight{ font-weight: 900; } .flex{ display: flex; } .flex > pre{ margin: 0; } .svgUp{ /* svg标签,默认值,width:300px; height:150px */ width: 500px; height: 130px; } .svgMiddle{ /* svg标签,默认值,width:300px; height:150px */ width: 500px; height: 100px; } .svgDown{ /* svg标签,默认值,width:300px; height:150px */ width: 500px; height: 80px; } </style> </head> <body style="zoom:1"> <div> <div class="fontWeight">SVG</div> <div>教程,https://www.cainiaojc.com/svg/svg-tutorial.html</div> <div>使用,"<"img src="svg-icon.svg"">"</div> <div>坐标,左上角的坐标是0,0--这点特别重要</div> </div> <div> <div class="fontWeight">一、svg标签下-基本标签-形状</div> <div class="flex"> <pre> 1、直线 "<"line x1="0" y1="20" x2="200" y2="70" stroke="gray" stroke-width="2" /">" <svg class="svgUp"> <line x1="0" y1="20" x2="200" y2="70" stroke="gray" stroke-width="2"/> </svg> </pre> <pre> 2、折线 "<"polyline points="20,10 30,100 170,50 70,50" style="fill:#ccc; stroke:#000; stroke-width:2" /">" <svg class="svgUp"> <polyline points="20,10 30,100 170,50 70,50" style="fill:#ccc;stroke: #000;stroke-width:2"/> </svg> </pre> <pre> 3、多边形 "<"polygon points="20,10 30,100 170,50 70,50" style="fill:#ccc; stroke:#000; stroke-width:2" /">" <svg class="svgUp"> <polygon points="20,10 30,100 170,50 70,50" style="fill:#ccc; stroke:#000;stroke-width:2"/> </svg> </pre> </div> <div class="flex"> <pre> 4、矩形 fill-opacity,属性定义填充颜色透明度(合法的范围是:0到1) stroke-opacity,属性定义笔触颜色的透明度(合法的范围是:0到1) "<"rect x="20" y="20" width="250" height="40" fill="gray" /">" <svg class="svgMiddle"> <rect x="20" y="20" width="250" height="40" fill="gray"/> </svg> </pre> <pre> 5、圆形 cx和cy,圆点的x和y坐标,默认为(0,0);r,圆的半径 "<"circle cx="50" cy="50" r="40" style="fill:gray;" /">" <svg class="svgMiddle"> <circle cx="50" cy="50" r="40" style="fill:gray;"/> </svg> </pre> <pre> 6、椭圆 cx和cy,圆点的x和y坐标,默认为(0,0);rx和ry,水平和垂直半径 "<"ellipse cx="110" cy="50" rx="100" ry="40" style="fill:grey;" /">" <svg class="svgMiddle"> <ellipse cx="110" cy="50" rx="100" ry="40" style="fill:grey;"/> </svg> </pre> </div> </div> <div class="flex"> <div> <div class="fontWeight">二、svg标签下-组合标签-形状</div> <pre> 1、示例 <svg class="svgDown"> <symbol id="shapeA"> <circle cx="0" cy="0" r="60" ></circle> </symbol> <symbol id="shapeB"> <path d="M0 50 L50 0 L50 50 Z" /> </symbol> <defs> <g id="shapeC"> <rect x="0" y="0" width="50" height="50" ></rect> </g> <g id="shapeD"> <circle cx="24" cy="24" r="24" ></circle> <path d="M0 50 L120 0 L120 50 Z" /> </g> </defs> <use xlink:href="#shapeA" x="0" y="20" ></use> <use xlink:href="#shapeB" x="100" y="30" ></use> <use xlink:href="#shapeC" x="200" y="30" ></use> <use xlink:href="#shapeD" x="300" y="30" ></use> </svg> 附、示例 <!-- <template> <svg :class="svgClass"> <use :xlink:href="iconName" :fill="color" /> </svg> </template> --> </pre> <pre class="color"> 2、标签 "<"use xlink:href="#shapeA" x="0" y="30"">""<"/use">" (1)标签 A、symbol,定义一个形状,被use引用后显示 B、defs,定义多个形状,被use引用后显示 g,定义多个形状里的一个形状 C、use,使用形状 (2)属性 A、xlink:href,是svg的一种属性, 用于定义svg文档中的元素与外部资源之间的链接,包括图片、音频、视频等 </pre> </div> <div> <div class="fontWeight">三、svg标签下-属性</div> <pre> 1、示例 <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="svgID" class="icon" t="1701073968299" version="1.1" width="见下面script" height="见下面script" > <path d=" M1010.036364 465.454545L539.927273 9.309091c-13.963636-13.963636-41.890909-13.963636-55.854546 0L13.963636 465.454545c-18.618182 13.963636-18.618182 41.890909 0 55.854546 13.963636 13.963636 41.890909 13.963636 55.854546 0l414.254545-395.636364c13.963636-13.963636 41.890909-13.963636 55.854546 0l414.254545 395.636364c13.963636 13.963636 41.890909 13.963636 55.854546 0 18.618182-13.963636 18.618182-41.890909 0-55.854546z M372.363636 698.181818v186.181818c0 27.927273-18.618182 46.545455-46.545454 46.545455H232.727273c-27.927273 0-46.545455-18.618182-46.545455-46.545455v-279.272727c0-27.927273-18.618182-46.545455-46.545454-46.545454s-46.545455 18.618182-46.545455 46.545454v372.363636c0 27.927273 18.618182 46.545455 46.545455 46.545455h279.272727c27.927273 0 46.545455-18.618182 46.545454-46.545455v-186.181818c0-27.927273 18.618182-46.545455 46.545455-46.545454h93.090909c27.927273 0 46.545455-18.618182 46.545455-46.545455s-18.618182-46.545455-46.545455-46.545454H418.909091c-27.927273 0-46.545455 18.618182-46.545455 46.545454z M837.818182 605.090909v279.272727c0 27.927273-18.618182 46.545455-46.545455 46.545455h-93.090909c-27.927273 0-46.545455 18.618182-46.545454 46.545454s18.618182 46.545455 46.545454 46.545455h186.181818c27.927273 0 46.545455-18.618182 46.545455-46.545455v-372.363636c0-27.927273-18.618182-46.545455-46.545455-46.545454s-46.545455 18.618182-46.545454 46.545454z" p-id="4219" fill="#000" ></path> </svg> </pre> <pre class="color"> 2、属性 "<" 标签内容,免 /">" (1)viewBox,svg元素的视图框坐标是x y width height,如"0 0 1024 1024" (2)xmlns,xml命名空间,xml namespace,防止来自不同技术的元素发生冲突 (3)xmlns:xlink,xlink在XML文档中创建超级链接的语言 (4)id,见下面script (5)class,标签的样式 (6)t,本代码生成时的时间 (7)version,图片的版本 (8)width,svg宽,见下面script (9)height,svg高,见下面script 3、附xml标签的属性 (1)standalone="no",文档会引用一个外部文件,!DOCTYPE后的内容,即为引用的内容 </pre> </div> <div> <div class="fontWeight">四、path标签下-属性</div> <pre> 1、示例 <svg class="svgDown"> <path d="M120 30 L10 80 L250 80 Z" fill="#000" stroke="red" stroke-width="2"/> </svg> </pre> <pre class="color"> 2、属性 "<"path d="M120 30 L10 80 L250 80 Z" fill="#000" stroke="red" stroke-width="2" /">" (1)属性 A、d,包含所有的绘图命令,见(2)(3) B、fill,轮廓内的填充颜色 C、stroke,轮廓内的描边颜色 D、stroke-width,轮廓内的描边宽度 附、关于样式的属性,可以写到class或style里 (2)常用命令 A、M:x,y moveto 将笔移动到指定点x,y而不绘图 B、L:x,y Lineto 绘制一条从当前笔位置到指定点x,y的直线 C、Z:封闭路径 通过从当前点到第一个点画一条线来封闭路径 (3)不常用命令 A、H:X 水平线 画一条水平线到定义的点,(指定的x,笔当前的y) C、V:y 垂直线 在由(定义的当前x,指定y)定义的点上画一条垂直线 </pre> </div> </div> </body> </html> <script> document.getElementById("svgID").setAttribute("width", "50"); document.getElementById("svgID").setAttribute("height", "50"); </script> 二、D3之基础用法、综合用法、数据平放、数据立放、饼图 1、基础用法(绑定文字) <html> <head> <meta charset="utf-8"> <title>Hello World</title> <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> </head> <body> <p></p> <p></p> <p></p> </body> </html> <script> var dataset = ["sun","moon","you"]; var body = d3.select("body"); var p = body.selectAll("p"); p.data(dataset).text(function(d, i){ return "I love " + d; }); console.log(d3.range(5)); </script> 2、综合用法(柱形图,含选择集、数据绑定、比例尺、坐标轴) <html> <head> <meta charset="utf-8"> <title>Hello World</title> <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> <style> .axis path, .axis line{ fill: none; stroke: black; shape-rendering: crispEdges; } .axis text { font-family: sans-serif; font-size: 11px; } </style> </head> <body> </body> </html> <script> //画布大小 var width = 400; var height = 400; //在 body 里添加一个 SVG 画布 var svg = d3.select("body") .append("svg") .attr("width", width) .attr("height", height); //画布周边的空白 var padding = {left:30, right:30, top:20, bottom:20}; //定义一个数组 var dataset = [10, 20, 30, 40, 33, 24, 12, 5]; //x轴的比例尺 var xScale = d3.scale.ordinal() .domain(d3.range(dataset.length)) .rangeRoundBands([0, width - padding.left - padding.right]); //y轴的比例尺 var yScale = d3.scale.linear() .domain([0,d3.max(dataset)]) .range([height - padding.top - padding.bottom, 0]); //定义x轴 var xAxis = d3.svg.axis() .scale(xScale) .orient("bottom"); //定义y轴 var yAxis = d3.svg.axis() .scale(yScale) .orient("left"); //矩形之间的空白 var rectPadding = 4; //添加矩形元素 var rects = svg.selectAll(".MyRect") .data(dataset) .enter() .append("rect") .attr("class","MyRect") .attr("transform","translate(" + padding.left + "," + padding.top + ")") .attr("x", function(d,i){ return xScale(i) + rectPadding/2; }) .attr("y",function(d){ return yScale(d); }) .attr("width", xScale.rangeBand() - rectPadding ) .attr("height", function(d){ return height - padding.top - padding.bottom - yScale(d); }) .attr("fill","steelblue"); //添加文字元素 var texts = svg.selectAll(".MyText") .data(dataset) .enter() .append("text") .attr("class","MyText") .attr("transform","translate(" + padding.left + "," + padding.top + ")") .attr("x", function(d,i){ return xScale(i) + rectPadding/2; } ) .attr("y",function(d){ return yScale(d); }) .attr("dx",function(){ return (xScale.rangeBand() - rectPadding)/2; }) .attr("dy",function(d){ return 20; }) .text(function(d){ return d; }) .style({ "fill":"#FFF", "text-anchor":"middle" }); //添加x轴 svg.append("g") .attr("class","axis") .attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")") .call(xAxis); //添加y轴 svg.append("g") .attr("class","axis") .attr("transform","translate(" + padding.left + "," + padding.top + ")") .call(yAxis); </script> 3、数据平放 (1)数据平放(无比例尺) <html> <head> <meta charset="utf-8"> <title>Hello World</title> <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> </head> <body> </body> </html> <script> var dataset = [ 250 , 210 , 170 , 130 , 90 ];//数据(表示矩形的宽度) var width = 300;//画布的宽度 var height = 300;//画布的高度 var svg = d3.select("body")//选择文档中的body元素 .append("svg")//添加一个svg元素 .attr("width", width)//设定宽度 .attr("height", height);//设定高度 var rectHeight = 25;//每个矩形所占的像素高度(包括空白) svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x",20) .attr("y",function(d,i){ return i * rectHeight; }) .attr("width",function(d){ return d; }) .attr("height",rectHeight-10) .attr("fill","steelblue"); </script> (2)数据平放(有比例尺) <html> <head> <meta charset="utf-8"> <title>Hello World</title> <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> </head> <body> </body> </html> <script> var dataset = [ 250 , 210 , 170 , 130 , 90 ];//数据(表示矩形的宽度) var width = 300;//画布的宽度 var height = 300;//画布的高度 var svg = d3.select("body")//选择文档中的body元素 .append("svg")//添加一个svg元素 .attr("width", width)//设定宽度 .attr("height", height);//设定高度 var rectHeight = 25;//每个矩形所占的像素高度(包括空白) var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ]; var linear = d3.scale.linear() .domain([0, d3.max(dataset)]) .range([0, 250]); var rectHeight = 25;//每个矩形所占的像素高度(包括空白) svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x",20) .attr("y",function(d,i){ return i * rectHeight; }) .attr("width",function(d){ return linear(d);//在这里用比例尺 }) .attr("height",rectHeight-10) .attr("fill","steelblue"); </script> 4、数据立放 (1)数据立放(无比例尺) <html> <head> <meta charset="utf-8"> <title>Hello World</title> <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> </head> <body> </body> </html> <script> var dataset = [ 250 , 210 , 170 , 130 , 90 ];//数据(表示矩形的宽度) var width = 300;//画布的宽度 var height = 300;//画布的高度 var svg = d3.select("body")//选择文档中的body元素 .append("svg")//添加一个svg元素 .attr("width", width)//设定宽度 .attr("height", height);//设定高度 var rectHeight = 25;//每个矩形所占的像素高度(包括空白) svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x",function(d,i){ return i * rectHeight; }) .attr("y",function(d,i){ return height - d; }) .attr("width",rectHeight-10) .attr("height",function(d){ return d; }) .attr("fill","steelblue"); </script> (2)数据立放(有比例尺) <html> <head> <meta charset="utf-8"> <title>Hello World</title> <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> </head> <body> </body> </html> <script> var dataset = [ 250 , 210 , 170 , 130 , 90 ];//数据(表示矩形的宽度) var width = 300;//画布的宽度 var height = 300;//画布的高度 var svg = d3.select("body")//选择文档中的body元素 .append("svg")//添加一个svg元素 .attr("width", width)//设定宽度 .attr("height", height);//设定高度 var rectHeight = 25;//每个矩形所占的像素高度(包括空白) var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ]; var linear = d3.scale.linear() .domain([0, d3.max(dataset)]) .range([0, 250]); var rectHeight = 25;//每个矩形所占的像素高度(包括空白) svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x",function(d,i){ return i * rectHeight; }) .attr("y",function(d,i){ return 250 - linear(d) ; }) .attr("width",rectHeight-10) .attr("height",function(d){ return linear(d);//在这里用比例尺 }) .attr("fill","steelblue"); </script> 5、饼图 <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.7.2/jquery.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/d3/3.2.8/d3.js"></script> <script type="text/javascript"> //准备数据 var chartData = [ { label: '漯河', value: 2 }, { label: '舞阳', value: 3 }, { label: '郭庄', value: 2 }, { label: '北京', value: 3 }, { label: '漯河3', value: 2 }, { label: '北京3', value: 3 }, { label: '漯河4', value: 2 }, { label: '北京4', value: 3 }, { label: '郭庄2', value: 4 } ]; var colors = [ '#2484c1', '#65a620', '#7b6888', '#a05d56', '#961a1a', '#d8d23a', '#e98125', '#d0743c', '#635222' ]; var title = [ { text: 'www.guowenke.tk', color: '#333333', fontSize: 18, font: 'arial' } ]; //定义画布大小 var canvasWidth = 600; var canvasHeight = 400; $(function () { var svg = d3 .select('#pie') .append('svg') .attr('width', canvasWidth) .attr('height', canvasHeight); //标题 svg .selectAll('.title') .data(title) .enter() .append('text') .text(function (d) { return d.text; }) .attr('class', 'title') .attr('fill', function (d) { return d.color; }) .style('font-family', function (d) { return d.font; }) .attr('x', canvasWidth / 2) .attr('y', 30); //绘图 //确定饼图中心 var pieChartElement = svg .append('g') .attr( 'transform', 'translate(' + canvasWidth / 2 + ',' + canvasHeight / 2 + ')' ); //创建弧生成器 var arc = d3.svg .arc() .innerRadius(canvasHeight * 0) .outerRadius((canvasHeight * 0.8) / 3) .startAngle(0) .endAngle(function (d) { return (d.value / getSumData(chartData)) * 2 * Math.PI; }); var arcs = pieChartElement .selectAll('g') .data(chartData) .enter() .append('g') .attr('transform', function (d, i) { var angle = 0; if (i > 0) { angle = getSegmentAngle(i - 1, chartData, getSumData(chartData)); } return 'rotate(' + angle + ')'; }); arcs .append('path') .attr('fill', function (d, i) { return colors[i];//设定弧的颜色 }) .attr('d', function (d) { return arc(d);//使用弧生成器 }); //添加标签组 var outerLabelGroupData = []; var lineCoordGroups = []; var labels = svg.append('g').attr('class', 'labels'); var labelGroup = labels .selectAll('.labelGroup') .data(chartData) .enter() .append('g') .attr('class', 'labelGroup'); labelGroup.append('text').text(function (d, i) { return d.label + ':' + d.value; }); d3.selectAll('.labelGroup') .each(function (d, i) { var labelGroupDims = $('.labelGroup').get(i).getBBox(); var angle = getSegmentAngle(i, chartData, getSumData(chartData), { midpoint: true }); var originalX = canvasWidth / 2; var originalY = canvasHeight / 2 - ((canvasHeight * 0.8) / 3 + 3); var newCords = rotate( originalX, originalY, canvasWidth / 2, canvasHeight / 2, angle ); if (angle > 180) { newCords.x -= labelGroupDims.width + 28; } else { newCords.x += 28; } outerLabelGroupData[i] = { x: newCords.x, y: newCords.y, w: labelGroupDims.width, h: labelGroupDims.height }; }) .attr('transform', function (d, i) { return ( 'translate(' + outerLabelGroupData[i].x + ',' + outerLabelGroupData[i].y + ')' ); }); d3.selectAll('.labelGroup').each(function (d, i) { var angle = getSegmentAngle(i, chartData, getSumData(chartData), { midpoint: true }); var originalX = canvasWidth / 2; var originalY = canvasHeight / 2 - ((canvasHeight * 0.8) / 3 + 3); var newCords = rotate( originalX, originalY, canvasWidth / 2, canvasHeight / 2, angle ); var labelXMargin = 6; var originCoords = newCords; var heightOffset = outerLabelGroupData[i].h / 5; var quarter = Math.floor(angle / 90); var midPoint = 4; var x2, y2, x3, y3; if (quarter === 2 && angle === 180) { quarter = 1; } switch (quarter) { case 0: x2 = outerLabelGroupData[i].x - labelXMargin - (outerLabelGroupData[i].x - labelXMargin - originCoords.x) / 2; y2 = outerLabelGroupData[i].y + (originCoords.y - outerLabelGroupData[i].y) / midPoint; x3 = outerLabelGroupData[i].x - labelXMargin; y3 = outerLabelGroupData[i].y - heightOffset; break; case 1: x2 = originCoords.x + (outerLabelGroupData[i].x - originCoords.x) / midPoint; y2 = originCoords.y + (outerLabelGroupData[i].y - originCoords.y) / midPoint; x3 = outerLabelGroupData[i].x - labelXMargin; y3 = outerLabelGroupData[i].y - heightOffset; break; case 2: var startOfLabelX = outerLabelGroupData[i].x + outerLabelGroupData[i].w + labelXMargin; x2 = originCoords.x - (originCoords.x - startOfLabelX) / midPoint; y2 = originCoords.y + (outerLabelGroupData[i].y - originCoords.y) / midPoint; x3 = outerLabelGroupData[i].x + outerLabelGroupData[i].w + labelXMargin; y3 = outerLabelGroupData[i].y - heightOffset; break; case 3: var startOfLabel = outerLabelGroupData[i].x + outerLabelGroupData[i].w + labelXMargin; x2 = startOfLabel + (originCoords.x - startOfLabel) / midPoint; y2 = outerLabelGroupData[i].y + (originCoords.y - outerLabelGroupData[i].y) / midPoint; x3 = outerLabelGroupData[i].x + outerLabelGroupData[i].w + labelXMargin; y3 = outerLabelGroupData[i].y - heightOffset; break; } lineCoordGroups[i] = [ { x: originCoords.x, y: originCoords.y }, { x: x2, y: y2 }, { x: x3, y: y3 } ]; }); var lineGroups = svg .append('g') .attr('class', 'lineGroups') .style('opacity', 1); var lineGroup = lineGroups .selectAll('.lineGroup') .data(lineCoordGroups) .enter() .append('g') .attr('class', 'lineGroup'); var lineFunction = d3.svg .line() .interpolate('basis') .x(function (d) { return d.x; }) .y(function (d) { return d.y; }); lineGroup .append('path') .attr('d', lineFunction) .attr('stroke', function (d, i) { return colors[i]; }) .attr('stroke-width', 1) .attr('fill', 'none'); function rotate(x, y, xm, ym, a) { a = (a * Math.PI) / 180;// 转为弧度 var cos = Math.cos, sin = Math.sin, xr = (x - xm) * cos(a) - (y - ym) * sin(a) + xm, yr = (x - xm) * sin(a) + (y - ym) * cos(a) + ym; return { x: xr, y: yr }; } function getSumData(data) { var sumData = 0; var countData = data.length; for (var i = 0; i < countData; i++) { sumData += data[i].value; } return sumData; } function getSegmentAngle(index, data, sumValue, opts) { var options = $.extend( { compounded: true, midpoint: false }, opts ); var currValue = data[index].value; var fullValue; fullValue = 0; for (var i = 0; i <= index; i++) { fullValue += data[i].value; } //值转为角度 var angle = (fullValue / sumValue) * 360; if (options.midpoint) { var currAngle = (currValue / sumValue) * 360; angle -= currAngle / 2; } return angle; } }); </script> </head> <body> <div id="pie"></div> </body> </html> 6、拓扑图 <!DOCTYPE> <html> <meta charset="utf-8"> <title>Radial Dendrogram</title> <link rel="stylesheet" type="text/css" href="https://unpkg.com/@observablehq/notebook-inspector@1/dist/notebook-inspector-style.css"> <body> <div id="console" style="width: 100%;"></div> </body> </html> <script type="module"> import { Inspector, Runtime } from "https://unpkg.com/@observablehq/notebook-runtime@1?module"; //URL: https://observablehq.com/@13716164418/radial-dendrogram const m0 = { id: "myId", variables: [ { inputs: ["md"], value: (function (md) { return md`# Radial Dendrogram` }) }, { name: "chart", inputs: ["tree", "d3", "data", "DOM", "width"], value: (function (tree, d3, data, DOM, width) { const root = tree(d3.hierarchy(data) .sort((a, b) => (a.height - b.height) || a.data.name.localeCompare(b.data.name))); const svg = d3.select(DOM.svg(width, width)) .style("width", "100%") .style("height", "auto") .style("padding", "10px") .style("box-sizing", "border-box") .style("font", "10px sans-serif"); const g = svg.append("g"); const link = g.append("g") .attr("fill", "none") .attr("stroke", "#555") .attr("stroke-opacity", 0.4) .attr("stroke-width", 1.5) .selectAll("path") .data(root.links()) .enter().append("path") .attr("d", d3.linkRadial().angle(d => d.x).radius(d => d.y)); const node = g.append("g") .attr("stroke-linejoin", "round") .attr("stroke-width", 3) .selectAll("g") .data(root.descendants().reverse()) .enter().append("g") .attr("transform", d => `rotate(${d.x * 180 / Math.PI - 90}) translate(${d.y},0)`); node.append("circle") .attr("fill", d => d.children ? "#555" : "#999") .attr("r", 2.5); node.append("text") .attr("dy", "0.31em") .attr("x", d => d.x < Math.PI === !d.children ? 6 : -6) .attr("text-anchor", d => d.x < Math.PI === !d.children ? "start" : "end") .attr("transform", d => d.x >= Math.PI ? "rotate(180)" : null) .text(d => d.data.name) .filter(d => d.children) .clone(true).lower() .attr("stroke", "white"); document.body.appendChild(svg.node()); const box = g.node().getBBox(); svg.remove() .attr("width", box.width) .attr("height", box.height) .attr("viewBox", `${box.x} ${box.y} ${box.width} ${box.height}`); return svg.node(); }) }, { name: "data", inputs: ["require"], value: (function (require) { return require("@observablehq/flare") }) }, { name: "width", value: (function () { return 932 }) }, { name: "radius", inputs: ["width"], value: (function (width) { return width / 2 }) }, { name: "tree", inputs: ["d3", "radius"], value: (function (d3, radius) { return d3.cluster().size([2 * Math.PI, radius - 100]) }) }, { name: "d3", inputs: ["require"], value: (function (require) { return require("d3@5") }) } ] }; const notebook = { id: "myId", modules: [m0] }; export default notebook; Runtime.load(notebook, Inspector.into(document.getElementById("console"))); </script> 三、ES6之基础 来源,http://caibaojian.com/es6 1、Set,唯一值数组,ES6提供的新的数据结构,类似于数组,但是成员的值都是唯一的,没有重复的 (1)去重 Array.from(new Set([1,2,3,3,1,4])); //[1, 2, 3, 4] (2)并集 var a = new Set([1, 2, 3]); var b = new Set([4, 3, 2]); var union = new Set([...a, ...b]); //{1, 2, 3, 4} (3)交集 var a = new Set([1, 2, 3]); var b = new Set([4, 3, 2]); var intersect = new Set([...a].filter(x => b.has(x))); //{2, 3} (4)删除 var list = new Set([1,20,30,40]) list.delete(30) //删除值为30的元素,这里的30并非下标 (5)清除所有元素 var list = new Set([1,2,3,4]) list.clear() (6)添加元素add var list=new Set(); list.add(1) list.add(2).add(3).add(3) //2只被添加了一次 2、Map,任意键对象,ES6提供的新的数据结构,类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键 (1)示例1, var obj = new Map() obj.set(0, "attack_type") //键是数值 obj.set("age", 20) //键是字符串 obj.set(undefined, "definition") //键是undefined (2)示例2, var obj = new Map([['name', '张三'], ['age', 18], ['sex', '男']]) obj.size //3 3、Symbol,ES6提供的新的数据类型 (1)原生函数,String()、Number()、Boolean()、Array()、Object()、Function()、RegExp()、Date()、Error()、Symbol() (2)原始数据类型,未定义(undefined)、空值(null)、布尔值(boolean)、字符串(string)、数值(number)、对象(object)、符号(symbol) (3)ES5的对象属性名都是字符串,这容易造成属性名冲突的问题 (4)symbol表示独一无二的值,"真实值"无法获取,Symbol类型没有字面量,感觉类似于对象的实例 (5)示例1,A页面和B页面的变量名有可能相同,造成覆盖,因此Symbol的作用不大 var aaa = Symbol('foo'); //A页面 var bbb = Symbol('foo'); //B页面 console.log(aaa === bbb); //false (5)示例2, let ccc = Symbol.for('foo'); //创建新符号 let ddd = Symbol.for('foo'); //重用已有符号 console.log(ccc === ddd); //true (6)示例3, var s1 = Symbol('foo'); var s2 = Symbol('bar'); var s3 = Symbol('baz'); var s4 = Symbol('qux'); var sss = { //[属性],会对属性进行读取,并且转换成字符串。[s1]是读取了Symbol的字符串键'foo' [s1]: 'foo val' }; //或 sss[s1] = 'foo val'; console.log(sss); // { [Symbol(foo)]: 'foo val' } 4、解构赋值 var person = {name: 'zhangsan', address: {province: '江苏', city: '南京'}}; var {name, address:{province, city}}=person; console.log(name, province, city); 5、var、let、const的区别 (1)块级,var不是块级作用域,let和const是块级{}作用域 示例1, var b=[]; for(var j=0;j<10;j++){ b[j]=function(){ console.log(j); }; } b[6](); 示例2, var a=[]; for(let i=0;i<10;i++){ a[i]=function(){ console.log(i); }; } a[6](); 示例3, var a=[]; for(const i=0;i<10;i++){ a[i]=function(){ console.log(i); }; } a[6](); (2)提声,var不存在暂时性死区、有变量提声、声明前可用,let和const,存在暂时性死区、没有变量提声、声明前不可用 附、变量提升,将变量声明提升到它所在作用域的最开始的部分,通过var定义(声明)的变量,在定义语句之前就可以访问到 示例1, console.log(a); //undefined var a = 1; 示例2, console.log(a); //a is not defined 示例3, console.log(a) let a = 1; 示例4, console.log(a) const a = 1; (3)重声,var允许重复声明变量,let和const不允许重复声明变量 示例1, var a = 1; var a = 1; console.log(a) 示例2, let a = 1; let a = 1; console.log(a) 示例3, const a = 1; const a = 1; console.log(a) (4)初始值,var和let可以不设置初始值、可以重新赋值,const必须设置初始值、不能重新赋值 示例1, var a; a = 1; a = 2; console.log(a) 示例2, let a; a = 1; a = 2; console.log(a) 示例3, const a; a = 1; a = 2; console.log(a) 6、箭头函数的this 注意:宿主对象的this,即箭头函数的this 示例一 var value = 6; var obj = { value: 5, fn: function fnIn() { setTimeout(() => { console.log(this.value); //5,宿主对象为fnIn,其this为obj }); }, }; obj.fn(); 示例二 var value = 6; var obj = { value: 5, fn: () => { console.log(this.value); //6,宿主对象为obj,其this为window }, }; obj.fn(); 7、箭头函数和普通函数的区别(new、arguments、this、call、apply、prototype、Generator、yield), (1)箭头函数作为匿名函数,是不能作为构造函数的,不能使用new, (2)箭头函数不绑定arguments,取而代之用rest参数…解决, (3)箭头函数会捕获其所在上下文的 this 值,作为自己的 this 值,即箭头函数所在对象的 this, (4)使用call()和apply()调用只是传入了参数而已,对 this 并没有什么影响, (5)箭头函数没有原型属性prototype, (6)箭头函数不能当做Generator函数,不能使用yield关键字, (7)箭头函数参数:0或多个参数,需要用();1个参数,不需要(), (8)箭头函数函数体:多个分句,用{}和return;1个分句,不用{}和return;1个分句,返回值为对象,用({})。 8、普通函数与箭头函数对照 (1)普通函数:function fn(){return 5} 箭头函数:var fn=()=>{return 5} (2)普通函数:function fn(v){return 5} 箭头函数:var fn=(v)=>v (3)普通函数:map(function(){return 5}) 箭头函数:map(()=>5) (4)普通函数:map(function(v){return 5}) 箭头函数:map(v=>5) 附:var 变量=(参数)=>函数体 9、模板字符串 (1)在模板字符串html中,用`<div>${ /* 注释内容 */'' }</div>`加注释 (2)在模板字符串js中,用`${变量}`加变量 四、ES6之Proxy(内部拦截) 1、Proxy实例(Proxy必须针对Proxy实例进行操作,否则Proxy不起作用) (1)实例一 var obj = {}; var thisObj = new Proxy(obj, { get: function (target, key, receiver) { console.log(obj === target); //true if (key === 4) { return 5; } return 6; }, set: function (target, key, value, receiver) { return Reflect.set(target, key, value, receiver); }, has: function (target, key) { if (key[0] === "c") { return false; } return key in target; }, }); thisObj.count = 1; console.log(thisObj.count); console.log("count" in thisObj); (2)实例二 var fn=function(){ }; var thisFn = new Proxy(fn,{ construct:function(target,args){ console.log(target===fn)//true return { value: args[0]*10 }//construct方法返回的必须是一个对象,否则会报错。 } }); console.log(new thisFn(1)); console.log(Reflect.construct(thisFn,[1])); 2、Proxy配置 (1)get(target, propKey, receiver) 拦截对象属性的读取,比如proxy.starFunction 和 proxy['starFunction']。 如果propKey属性部署了读取函数,则读取函数的this绑定receiver(一个对象)。 (2)set(target, propKey, value, receiver) 拦截对象属性的设置,比如proxy.starFunction = v或proxy['starFunction'] = v,返回一个布尔值。 (3)has(target, propKey) 拦截propKey in proxy的操作,以及对象的hasOwnProperty方法,返回一个布尔值。 (4)deleteProperty(target, propKey) 拦截delete proxy[propKey]的操作,返回一个布尔值。 (5)ownKeys(target) 拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy),返回一个数组。 该方法返回对象所有自身的属性,而Object.keys()仅返回对象可遍历的属性。 (6)getOwnPropertyDescriptor(target, propKey) 拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。 (7)defineProperty(target, propKey, propDesc) 拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。 (8)preventExtensions(target) 拦截Object.preventExtensions(proxy),返回一个布尔值。 (9)getPrototypeOf(target) 拦截Object.getPrototypeOf(proxy),返回一个对象。 (10)isExtensible(target) 拦截Object.isExtensible(proxy),返回一个布尔值。 (11)setPrototypeOf(target, proto) 拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。 如果目标对象是函数,那么还有两种额外操作可以拦截。 (12)apply(target, object, args) 拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...) 。 (13)construct(target, args) 拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。 另外,vue2用Object.defineProperty实现双向绑定,vue3用Proxy实现双向绑定 五、ES6之Reflect(外部操纵) 1、Reflect概述 Reflect对象与Proxy对象一样,也是ES6为了操作对象而提供的新API。Reflect对象的设计目的有这样几个。 (1)将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。 (2)修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。 (3)让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。 (4)Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。 2、Reflect方法 (1)Reflect.get(target, name, receiver) 查找并返回target对象的name属性,如果没有该属性,则返回undefined。 如果name属性部署了读取函数,则读取函数的this绑定receiver。 var antzone = { webName: "蚂蚁部落", url:"www.softwhy.com", get age() { return this.myAge; } } console.log(Reflect.get(antzone, "age", { myAge: 4 })); (2)Reflect.set(target, name, value, receiver) 设置target对象的name属性等于value。如果name属性设置了赋值函数,则赋值函数的this绑定receiver。 (3)Reflect.has(obj, name) 等同于name in obj。 (4)Reflect.deleteProperty(obj, name) 等同于delete obj[name]。 (5)Reflect.construct(target, args) 等同于new target(...args),这提供了一种不使用new,来调用构造函数的方法。 function func1(a, b, c) { this.sum = a + b + c; } const args = [1, 2, 3]; const object1 = new func1(...args); const object2 = Reflect.construct(func1, args); console.log(object2.sum);//expected output: 6 console.log(object1.sum);//expected output: 6 (6)Reflect.getPrototypeOf(obj) 读取对象的__proto__属性,对应Object.getPrototypeOf(obj)。 (7)Reflect.setPrototypeOf(obj, newProto) 设置对象的__proto__属性,对应Object.setPrototypeOf(obj, newProto)。 (8)Reflect.apply(fun,thisArg,args) 等同于Function.prototype.apply.call(fun,thisArg,args)。一般来说,如果要绑定一个函数的this对象,可以这样写fn.apply(obj, args),但是如果函数定义了自己的apply方法,就只能写成Function.prototype.apply.call(fn, obj, args),采用Reflect对象可以简化这种操作。 console.log(Reflect.apply(Math.floor, undefined, [1.75]));//expected output: 1 六、ES6之Iterator(遍历器) //以下遍历器(迭代器)模拟代码 function createIterator(items) { var i = 0; return { next: function() { var done = (i >= items.length); var value = done ? undefined : items[i++] ; return { done: done, value: value }; } }; } var iterator = createIterator([1, 2, 3]); console.log(iterator.next()); //"{ value: 1, done: false }" console.log(iterator.next()); //"{ value: 2, done: false }" console.log(iterator.next()); //"{ value: 3, done: false }" console.log(iterator.next()); //"{ value: undefined, done: true }" JavaScript原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6又添加了Map和Set。这样四种数据结构只要部署了Iterator接口,就是”可遍历的“(iterable)。ES6规定,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。 const obj = { [Symbol.iterator] : function () { return { next: function () { return { value: 1, done: true }; } }; } }; 上面代码中,对象obj是可遍历的(iterable),因为具有Symbol.iterator属性。 七、ES6之Generator 函数(生成器) 1、Generator函数有两个特征 (1)function关键字与函数名之间有一个星号; (2)函数体内部使用yield(即“产出”)语句,定义不同的内部状态。 2、调用Generator函数后,该函数并不执行,返回的是一个指向内部状态的指针对象,也就是遍历器对象(Iterator Object)。 3、调用遍历器对象的next方法,使得指针移向下一个状态。 4、也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield语句(或return语句)为止。 5、换言之,Generator函数是分段执行的,yield语句是暂停执行的标记,而next方法可以恢复执行。 6、示例1 function* starFunction(one) { console.log(one); var two = 2 * (yield one + 1); //6,7 console.log("1111111111111111111111111111111"); console.log(two); var three = yield 2 * two; //4,8 console.log("2222222222222222222222222222222"); console.log(three); var four = yield 2 * three; //2,4 console.log("3333333333333333333333333333333"); console.log(four); console.log(one, two, three, four); console.log("4444444444444444444444444444444"); return one + two + three + four; } var useFunction = starFunction(6); console.log(useFunction.next()); console.log(useFunction.next(2)); console.log(useFunction.next(2)); console.log(useFunction.next(2)); next第1次执行,返回“第1个yield语句的计算结果”; next第2次执行,返回“第2个yield语句的计算结果”,传入的参数取代“第1个yield语句的计算结果”; next第3次执行,返回“第3个yield语句的计算结果,传入的参数取代“第2个yield语句的计算结果””; next第n次执行时,如果没有第n个yield语句,那就一直执行到return,如果没有return,就返回undefined。 7、示例2 来源:http://www.ruanyifeng.com/blog/2015/04/generator.html Fetch模块返回的是一个Promise对象,因此要用then方法调用下一个next方法。 var fetch = require('node-fetch'); function* generatorFn(){ var urlA = 'https://api.github.com/users/githubA'; var urlB = 'https://api.github.com/users/githubB'; yield fetch(urlA); yield fetch(urlB); } var generatorOne = generatorFn(); var result = generatorOne.next(); result.value.then(function(data){ return data.json(); }).then(function(data){ generatorOne.next(data); }); 8、如果想要第一次调用next方法时,就能够输入值,可以在Generator函数外面再包一层。如下 function wrapper(generatorFunction) { return function (num) { let generatorObject = generatorFunction(num); generatorObject.next(); return generatorObject; }; } const wrapped = wrapper(function* starFunction(a) { console.log(a); var b = 2 * (yield a + 1); //6,7 console.log("1111111111111111111111111111111"); console.log(b); var c = yield 2 * b; //4,8 console.log("2222222222222222222222222222222"); console.log(c); var d = yield 2 * c; //2,4 console.log("3333333333333333333333333333333"); console.log(d); console.log(a, b, c, d); console.log("4444444444444444444444444444444"); return a + b + c + d; }); var b = wrapped(6); console.log(b.next(2)); console.log(b.next(2)); console.log(b.next(2)); 八、ES6之async(then)函数及其内部的await(promise)指令 (1)嵌套回调、Promise、Generators不够优雅, (2)从外向里,使用async-await-Promise-resolve,resolve的参数是await的返回值,async的返回值是async的then参数的参数 (3)await语句只能在async函数内部使用,该语句的异步执行结束后,才能执行下面的语句 示例一、 function thisPromise(x) { return new Promise(function (resolve) { //此处把x(加工后)作为参数向后台发送请求 setTimeout(function () { //此处获得后台返回结果并加工为x+50,通过resolve暴露出去 resolve(x + 50); }, 500); }); } async function thisAdd(x) { //var thisAdd = async function(x) { var a = await thisPromise(20); var b = await thisPromise(30); //此处可以加工a,b return x + a + b; //这个结果作为参数传给后面的success } thisAdd(10).then(function (total) { console.log(total); //1秒后,出打印结果160 }); 示例二、 router.beforeEach(async(to, from, next) => { if (getToken()) { if (to.path === '/login') { next({ path: '/' }); } else { if (userInfo) { next(); } else { try { await userStore.getInfo(); } catch (error) { } } } } else { } }); 九、ES6中模块规范(在前端模块化开发中使用) 1、默认导出和引入,export default,1个文件只能有1个默认导出 (1)先定义,再默认导出 function ABC(){}; export default ABC; (2)直接默认导出 export default function /*ABC*/(){}; (3)上面对应的import引入为 import DEF from "./abc" 2、普通导出和引入,export (1)先定义,再普通导出 var abc="abc"; function ABC(){}; export {acb,ABC} (2)直接普通导出 export var abc="abc"; export function ABC(){}; (3)上面对应的import引入为 import {abc,ABC} from "./abc" (4)通配符导入 import * as echarts from 'echarts';相当于 import { a, b, c, d } from 'echarts' 3、混合导出和引入 (1)默认和普通,分别导出 (2)默认和普通,同时引入 import DEF,{abc,ABC} from "./def" 指的是,def.js模块默认导出了abc,普通导出了ABC 4、改名导出和引入 (1)定义 var abc="abc"; function ABC(){}; export {abc as abc1, ABC as ABC1} (2)引入 import {abc1,ABC1} from "./abc"; import {abc1 as abc2, ABC1 as ABC2} from "./abc"; 十、AMD、CMD、UMD、CommonJS模块规范 1、AMD和require.js,AMD依赖模块加载跳过所有异步,运行后面的回调函数,运行中所有依赖模块的语句,都定义在另一个回调函数中,等到加载完成之后,这个回调函数才会运行。 (1)定义模块 define("abc", ["a", "b", "c", 'd'], function (a, b, c, d) { var a = require('a'); }); (2)引用模块 require(['abc','def'],function(abc,def){}); 2、CMD和sea.js,在回调函数里,需要某个依赖,才会加载这个依赖。 (1)定义模块 define("abc", ["a", "b", "c", 'd'], function (require, exports, module) { var a = require('a'); }); (2)引用模块 require("abc") 3、UMD用于判断代码运行环境 (1)UMD:Universal Module Definition,通用模块定义 (2)UMD实例 (function fn(root, factory) { if(typeof exports === 'object' && typeof module === 'object'){//非原生commonjs环境下,如nodejs环境 module.exports = factory(); }else if(typeof define === 'function' && define.amd){//AMD环境下,如requirejs加载器 define([], factory); }else if(typeof define === "function" && define.cmd){//CMD环境下,如seajs加载器,一个模块就是一个文件 define([], factory); }else if(typeof exports === 'object'){//原生commonjs环境下 exports["aaa"] = factory(); }else if(typeof self === 'object'){//使用了web worker技术的服务器环境,不会影响页面的性能。 self["aaa"] = factory(); }else if(typeof global === 'object'){//如果所有的全局环境变量都统一为global self["aaa"] = factory(); }else{//其它环境下,如window root["aaa"] = factory(); } })(this, function() {}) 4、CommonJS中模块规范(在后台nodejs开发中使用,在前端框架处理兼容时使用) (1)导出模块 module.exports = obj; (2)导入模块 var example = require('./example.js'); var area = example.area;//使用模块 (3)commonJS 规范主要特点 来源,https://blog.csdn.net/jieyucx/article/details/131548528 A、模块化组织代码:CommonJS 允许将代码按功能或逻辑分类成独立的模块,每个模块只负责特定的功能,使代码更加可维护和可复用。 C、隔离命名空间:每个模块都有自己独立的作用域,不会与其他模块中的变量和函数冲突,有效避免命名冲突。 C、代码加载管理:CommonJS 规范提供了模块的加载和缓存机制,可以确保模块只会被加载一次,避免重复加载和执行,提高性能。 D、跨平台使用:CommonJS 规范不限于在浏览器中使用,也可以在其他 JavaScript 运行环境中使用,如 Node.js 等。 5、commonjs和esmodule的区别 注、2019年发布的node13.2.0开始支持ES6模块,.vue文件被vue-loader转化为ES6模块 (1)语法不同 A、CommonJS使用require和module.exports进行模块导入和导出;exports有s B、ESModule使用import和export关键字进行模块导入和导出;export没有s (2)动态导入不同 A、CommonJS不支持动态导入 B、ESModule支持动态导入,可以在代码运行时根据需要导入模块 (3)作用域不同 A、CommonJS的模块作用域是动态的,模块中定义的变量和函数会被添加到全局作用域 B、ESModule的模块作用域是静态的,模块中定义的变量和函数不会污染全局作用域 (4)异步加载不同 A、CommonJS只能同步加载模块 B、ESModule可以异步加载模块,以提高性能和减少启动时间 (5)循环依赖处理不同 A、CommonJS可能会因为循环依赖导致程序崩溃 B、ESModule可以处理循环依赖 (6)浏览器兼容性不同 A、CommonJS可以在所有现代浏览器和Node.js中使用 B、ESModule在现代浏览器中得到广泛支持,但在旧版浏览器中可能无法使用 (7)导出方式不同 A、CommonJS导出的是一个值的拷贝,会对加载结果进行缓存,内部再修改这个值不会同步到外部; B、ESModule导出的是一个引用,内部修改可以同步到外部 (8)加载方式不同 A、CommonJS加载的是整个模块,将所有的接口全部加载进来 B、ESModule可以单独加载其中的某个接口 十一、TS泛型(Generics)是指在定义函数、接口或类的时候,不预先指定参数和返回值的具体类型,而在使用的时候再指定类型的一种特性。 1、泛型函数,泛型函数与非泛型函数没什么不同,只是有一个类型参数在最前面,像函数声明一样 (1)泛型函数的普通类型 function createArray<T>(length: number, value: T): Array<T> { let result: T[] = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; } createArray<string>(3, 'x'); //['x', 'x', 'x'] (2)泛型函数的默认类型 function createArray<T = string>(length: number, value: T): Array<T> { let result: T[] = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; } 2、泛型接口 (1)泛型接口写法一 interface CreateArrayFunc { <T>(length: number, value: T): Array<T>; } (2)泛型接口写法二 interface CreateArrayFunc<T> { (length: number, value: T): Array<T>; } (3)使用接口 let createArray: CreateArrayFunc; createArray = function<T>(length: number, value: T): Array<T> { let result: T[] = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; } createArray(3, 'x'); //['x', 'x', 'x'] 3、泛型类 (1)TypeScript的核心原则之一是对值进行类型检查 (2)在TypeScript里,接口的作用就是为这些类型命名和为代码定义契约 (3)https://www.tslang.cn/docs/handbook/interfaces.html (4)泛型类普通写法 class GenericNumber<T> { zeroValue: T; add: (x: T, y: T) => T; } let myGenericNumber = new GenericNumber<number>(); myGenericNumber.zeroValue = 0; myGenericNumber.add = function(x, y) { return x + y; }; (5)泛型约束 interface Lengthwise { length: number; } function loggingIdentity<T extends Lengthwise>(arg: T): T { console.log(arg.length); return arg; } (6)对泛型进行约束,只允许这个函数传入那些包含 length 属性的变量 (7)我们定义一个接口来描述约束条件。 创建一个包含 .length属性的接口,使用这个接口和extends关键字来实现约束 十二、ES6和TS的类和实例 1、ES6的属性、方法、继承 (1)在类中定义的属性,是实例的属性。相当于ES5中实例的构造函数。 (2)在类中定义的属性前,加上static,那么该属性就是静态属性即类本身的属性,通过类名.aaa来获取。 (3)在类中定义的方法,是实例的方法。相当于ES5中实例的原型。 (4)在类中定义的方法前,加上static,那么该方法就是静态方法即类本身的方法,通过类名.aaa来调用。方法可以被子类继承,通过子类名.aaa来调用,里面的this指的是类而不是实例。 (5)ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面。 (6)ES6的继承,实质是先创造父类的实例对象this,然后再用子类的构造函数修改this。子类内部的super是父类的构造函数,必须首先调用。 (7)static的内容,在ES5中,只能写在类AAA的外部,通过AAA.bbb定义、获取、调用。 //以下实例必须在浏览器控制台里运行,不能在node环境里运行 class Parent { constructor(x, y) { this.x = x; this.y = y; } getX() { console.log("this为Parent实例,实例的getX方法被调用,实例的x为:" + this.x); } static name = "animal"; static age = "20"; static getY() { console.log("this为Parent类,类的getY方法被调用,类的name为:" + this.name); } } class Son extends Parent { constructor(...props) { super(...props); this.z = 4; } } var parent = new Parent(2, 3); var son = new Son(2, 3); parent.getX(); Parent.getY(); son.getX(); Son.getY(); console.log(parent.x, parent.y); console.log(Parent.name, Parent.age); console.log(son.x, son.y, son.z); 2、TS的类和实例 知识来源,https://www.tslang.cn/docs/handbook/classes.html 代码运行,https://www.json.cn/runcode/run_typescript/ (1)公共public:类内可访问、子类内可访问、实例可访问、子实例可访问 (2)受保protected:类内可访问、子类内可访问 (3)私有private:类内可访问 class Person { public pubName: string; protected proName: string; /* protected */ constructor(pubName: string,proName: string) { this.pubName = pubName; this.proName = proName; }; } class Employee extends Person { public departmentA: string; protected departmentB: string; private departmentC: string; constructor(pubName: string,proName: string, department: string) { super(pubName,proName); this.departmentA = department; this.departmentB = department; this.departmentC = department; console.log(this.departmentC+"A:类内可访问,private") console.log(this.departmentB+"B:类内可访问,protected") console.log(this.proName+":子类可访问,protected") console.log(this.departmentA+"C:类内可访问,public") console.log(this.pubName+":子类可访问,public") } } console.log(new Employee("姓名人A","姓名人B", "售货员").departmentA+"D:实例可访问,public") console.log(new Employee("姓名人A","姓名人B", "售货员").pubName+"D:实例可访问,public") 十三、jQuery知识 1、四种宽 (1)width():其宽度范围是所匹配元素的宽度width; (2)innerWidth():其宽度范围是所匹配元素的宽度width+padding; (3)outerWidth():其宽度范围是所匹配元素的宽度width+padding+border; (4)outerWidth(true)其宽度范围是所匹配元素的宽度width+padding+border+margin; 2、attr和prop 附、总说明 A、2011年,jQuery.1.6.1发布,在本版本及以后的版本中 B、设置和获取-自定义属性用attr C、设置和获取-固有属性用prop D、其它的不要深究 (1)低版本 <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>jquery.1.5.1及以前</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.5.1/jquery.js"></script> </head> <body> <pre> <h3><input type="checkbox" id="check" checked="" aaa="aaa"/>jquery.1.5.1及以前</h3> input type="checkbox" id="check" aaa="aaa" checked="" 1、checked="",初始化时,不管如何赋值,只要出现,就是选中 2、attr可以设置、获取固有属性、自定义属性 (1)设置固有属性, $('#check').attr('checked',false)//不选中 $('#check').attr('checked','')//不选中 $('#check').attr('checked','111')//选中 $('#check').attr('checked',true)//选中 (2)获取固有属性, console.log($('#check').attr('checked'));//true console.log($('#check').attr('checked',false).attr('checked'));//false console.log($('#check').attr('checked','').attr('checked'));//false console.log($('#check').attr('checked','111').attr('checked'));//true console.log($('#check').attr('checked',true).attr('checked'));//true (3)获取自定义属性,console.log($('#check').attr('aaa'));//aaa 3、prop不存在 (1)console.log($('#check').prop);//undefined </pre> </body> </html> <script type="text/javascript"> console.log($('#check').attr('checked'));//true console.log($('#check').attr('checked',false).attr('checked'));//false console.log($('#check').attr('checked','').attr('checked'));//false console.log($('#check').attr('checked','111').attr('checked'));//true console.log($('#check').attr('checked',true).attr('checked'));//true </script> (2)高版本 <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>jquery.1.6.1及以后</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.6.1/jquery.js"></script> </head> <body> <pre> <h3><input type="checkbox" id="check" checked="" aaa="aaa"/>jquery.1.6.1及以后</h3> input type="checkbox" id="check" aaa="aaa" checked="" 1、checked="",不管如何赋值,只要出现,就是选中 2、attr可以设置、获取自定义属性、固有属性; (1)设置固有属性, $('#check').attr('checked',false)//不选中 $('#check').attr('checked','')//选中 $('#check').attr('checked','111')//选中 $('#check').attr('checked',true)//选中 (2)获取固有属性, console.log($('#check').attr('checked'));//checked console.log($('#check').attr('checked',false).attr('checked'));//undefined console.log($('#check').attr('checked','').attr('checked'));//undefined console.log($('#check').attr('checked','111').attr('checked'));//undefined console.log($('#check').attr('checked',true).attr('checked'));//checked (3)获取自定义属性, console.log($('#check').attr('aaa'));//aaa 3、prop可以设置、获取固有属性; (1)设置固有属性, $('#check').prop('checked',false)//不选中 $('#check').prop('checked','')//不选中 $('#check').prop('checked','111')//选中 $('#check').prop('checked',true)//选中 (2)获取固有属性, console.log($('#check').prop('checked'));//true console.log($('#check').prop('checked',false).prop('checked'));//false console.log($('#check').prop('checked','').prop('checked'));//false console.log($('#check').prop('checked','111').prop('checked'));//true console.log($('#check').prop('checked',true).prop('checked'));//true (3)获取自定义属性, console.log($('#check').prop('aaa'));//undefined </pre> </body> </html> <script type="text/javascript"> </script> 3、实例无new构建 (function(window) { var jQuery = function(selector, context) { return new jQuery.prototype.init(selector, context, rootjQuery); }, jQuery.prototype = { init: function( elem, options, prop, end, easing, unit ) { this.elem = elem; this.prop = prop; this.easing = easing || jQuery.easing._default; this.options = options; this.start = this.now = this.cur(); this.end = end; this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); }, } jQuery.prototype.init.prototype = jQuery.prototype; })(window); 十四、jQuery示例 1、jQuery实现鼠标跟随 说明, (1)所谓鼠标跟随,一般就是指鼠标移到哪张图片上,那该张图片的放大图片就会出现,并且放大图片会随着鼠标在该张图片上移动而移动。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style> * { margin: 0; padding: 0; } img { border: none; } .box { width: 660px; position: relative; } .box .mark { position: absolute; width: 400px; height: 300px; display: none; } .box .mark img { width: 100%; } .box img { width: 150px; float: left; margin: 5px; } </style> </head> <body> <div class="box" id="box"> <img src="http://www.qdfuns.com/misc.php?mod=attach&genre=editor&aid=e95708d565639d99576ae7cb00729334" realImg="http://www.qdfuns.com/misc.php?mod=attach&genre=editor&aid=5328802dc943fc046e109f70359add0a" alt=""/> <img src="http://www.qdfuns.com/misc.php?mod=attach&genre=editor&aid=9e5459a7c0098c27adf4bdd73889caa9" realImg="http://www.qdfuns.com/misc.php?mod=attach&genre=editor&aid=846f4d1987765dc4cfd5a06fcdd2dcc1" alt=""/> <img src="http://www.qdfuns.com/misc.php?mod=attach&genre=editor&aid=3cd1c8e301007f0c94850139ac79cb5a" realImg="http://www.qdfuns.com/misc.php?mod=attach&genre=editor&aid=747bf3f7092ebd2b0bf9fcd27e28bbe5" alt=""/> <img src="http://www.qdfuns.com/misc.php?mod=attach&genre=editor&aid=f391169b2cf678aa6fd253cf40d9821d" realImg="http://www.qdfuns.com/misc.php?mod=attach&genre=editor&aid=fec8d2f20fad1f28d540337a831e89d0" alt=""/> <div id="mark" class="mark"><img src="" alt=""/></div> </div> <script src="http://s0.kuaizhan.com/res/skin/js/lib/jquery-2.0.3.min.js"></script> <script> //1.鼠标移入哪张图片的时候,让他对应的大图显示; //2.当鼠标在img中移动的时候,大图跟着走; var $box=$('.box'); var $aImg=$box.children('img'); var $mark=$('.mark'); var $offset=$box.offset(); $aImg.mouseover(function(){ //当鼠标移入每张图片的时候,让mark显示,并且,让mark里面的img标签,src属性值为当前这个图片的realImg属性上拿到的值; $mark.show().find('img').attr('src',$(this).attr('realImg')); }); $aImg.mousemove(function(e){ //拿鼠标的x坐标,减去$box距离body的left位置; var left= e.clientX-$offset.left+10; var top= e.clientY-$offset.top+10; $mark.css({left:left,top:top}) }); $aImg.mouseout(function(){ $mark.hide(); }) </script> </body> </html> 2、jQuery实现文档树效果 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style> * { margin: 0; padding: 0; list-style: none; } .box { width: 250px; height: auto; padding: 20px; background: lightgrey; margin: 0 auto; } .box li { line-height: 30px; /*注意:height没有被设置,可以根据实际需要自动调整*/ position: relative; } .box li em { position: absolute; left: 0; top: 7px; width: 16px; height: 16px; background-image: url(""); background-size: 100%; cursor: pointer; } .box li em.open { background-image: url(""); background-size: 100%; } .box li span { padding-left: 20px; /*因为span前面的em已经绝对定位,脱离文档流了,所以span的左边界直达 li*/ } .box ul { display: none; } .two { margin-left: 20px; } .three { margin-left: 40px; } .four { margin-left: 40px; } /*ul.box下的li显示,其中有折叠的li加em; ul.box下的ul隐藏,其内部的li是没法显示的*/ </style> </head> <body> <ul class="box"> <li><em></em><span>第一级第一个</span> <ul class="two"> <li><span>第二级第一个</span></li> <li><em></em><span>第二级第二个</span> <ul class="three"> <li><em></em><span>第三级第一个</span> <ul class="four"> <li><span>第四级第一个</span></li> <li><span>第四级第二个</span></li> </ul> </li> <li><span>第三级第二个</span></li> </ul> </li> <li><em></em><span>第二级第三个</span> <ul class="three"> <li><span>第三级第一个</span></li> <li><span>第三级第二个</span></li> </ul> </li> </ul> </li> <li><em></em><span>第一级第一个</span> <ul class="two"> <li><span>第二级第一个</span></li> <li><em></em><span>第二级第二个</span> <ul class="three"> <li><em></em><span>第三级第一个</span> <ul class="four"> <li><span>第四级第一个</span></li> <li><span>第四级第二个</span></li> </ul> </li> <li><span>第三级第二个</span></li> </ul> </li> </ul> </li> </ul> <script src="http://s0.kuaizhan.com/res/skin/js/lib/jquery-2.0.3.min.js"></script> <script> /*思路: * 1.让前面有em的span加上小手效果; * 2.点击span or em的时候,看他父容器下是否有ul,如果有,让其显示,否则,隐藏 * */ var $box=$('.box'); var $aSpan=$box.find('span'); //1.让前面有em的span加上小手效果; $aSpan.each(function(index,item){ //if($(item).prev().length){ $(item).css('cursor','pointer');};思路1: $(item).prev('em').next('span').css('cursor','pointer'); //思路2: }); //2.点击span or em的时候,看他父容器下是否有ul,如果有,让其显示,否则,隐藏 $box.click(function(e){ //当点击的事件源是em or span的时候,我们看其父级下是否有ul //如果有:展开让其闭合,闭合就让其展开; if(e.target.tagName.toLowerCase()=='em' || e.target.tagName.toLowerCase()=='span'){ var $parent=$(e.target).parent(); var $ul=$parent.children('ul'); if($ul){ if($ul.css('display')=='block'){//展开,让其闭合 //当闭合的时候,让当前容器下,所有的em都移除open,所有的ul都隐藏; $parent.find('ul').hide(); $parent.find('em').removeClass('open'); }else{ //闭合让其展开 $ul.show(); $parent.children('em').addClass('open'); } } } }) </script> </body> </html> 3、jQuery方法扩展(选项卡) 说明, (1)在jQuery上扩展方法,通过点击实现选项卡的切换。本实例在jQuery的类上扩展,即$.extend({chooseCard:函数}),通过$.chooseCard('#box')调用; (2)有别于$.fn.extend({chooseCard:函数})扩展,通过$().chooseCard('#box')调用。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> * { margin: 0; padding: 0; } .box { width: 312px; border: 2px red solid; margin: 0 auto; } ul { overflow: hidden; } li { list-style: none; background: red; float: left; width: 100px; height: 30px; line-height: 30px; text-align: center; border: 2px solid orange; } li.on { background: green; } .box div { background: green; display: none; width: 312px; height: 200px; font-size: 30px; border-top: none; } .box div.on { display: block; } </style> </head> <body> <div class="box" id="box"> <ul> <li class="">中国</li> <li>日本</li> <li>韩国</li> </ul> <div class="on">中国是老大</div> <div>日本是老二</div> <div>韩国是老三</div> </div> <script src="http://s0.kuaizhan.com/res/skin/js/lib/jquery-2.0.3.min.js"></script> <script> (function ($) { $.extend({ chooseCard: function (idStr) { var $box = $(idStr); var $li = $box.find("li"); console.log($li); var $aDiv = $box.find("div"); $li.click(function () { $(this) .css({ height: "32px", "border-bottom": "none" }) .siblings("li") .css({ height: "30px", "border-bottom": "2px solid orange" }); $(this).addClass("on").siblings("li").removeClass("on"); $aDiv .eq($(this).index()) .addClass("on") .siblings("div") .removeClass("on"); }); }, }); })(jQuery); </script> <script> $(function(){ $.chooseCard('#box'); }) </script> </body> </html> 4、jQuery复选框全选、反选(<input type='checkbox'/>) 说明, (1)获取复选框状态:$("#allSelect").prop("checked") (2)改变复选框状态:$("#allSelect").prop("checked",false) (3)翻转复选框状态:item.checked = !item.checked; (4)判断复选框是否被选中:if ($(this).is(':checked')) (5)找到所有被选中的复选框:myDiv.find("input:checked"); (6)获取所有复选框:$("#single input:checkbox")或$("#single input[type=checkbox]"); (7)获取所有被选中的复选框:$("#single input:checkbox:checked")或$("#single input[type=checkbox]:checked"); <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="https://cdn.bootcss.com/jquery/1.9.0/jquery.js"></script> </head> <body> <span>全选</span><input type="checkbox" id="allSelect"><br> <span>反选</span><input type="checkbox" id="reverseSelect"> <div id="single"> <input type="checkbox"> <input type="checkbox"> <input type="checkbox"> <input type="checkbox"> </div> </body> </html> <script> $("#allSelect").click(function () { $("#single") .children() .each(function (index, item) { item.checked = $("#allSelect").prop("checked"); }); $("#reverseSelect").prop("checked", false); }); $("#reverseSelect").click(function () { $("#single") .children() .each(function (index, item) { item.checked = !item.checked; }); $("#reverseSelect").prop("checked", true); singleInput(); }); $("#single") .children() .click(function (index, item) { $("#reverseSelect").prop("checked", false); singleInput(); }); function singleInput() { if ( $("#single input:checkbox:checked").length == $("#single input:checkbox").length ) { $("#allSelect").prop("checked", true); } else { $("#allSelect").prop("checked", false); } } </script> 十五、Error对象详解 来源:https:www.jb51.net/article/124210.htm 1、说明,解释器会为每个错误情形创建并抛出一个Error对象,并包含该错误的描述信息 2、常用词汇 (1)Invalid,无效的 (2)malformed,畸形的,格式不正确的 (3)Uncaught,未捕获的 (4)Unexpected,意外的 (5)escape sequence,转译序列 3、七种错误 (1)EvalError,eval错误,eval()函数没有正确执行 (2)RangeError,范围错误,参数超范围,例如 A、[].length = -5 a、Uncaught RangeError: Invalid array length b、未捕获的范围错误:无效的数组长度 (3)ReferenceError,引用错误,例如 A、console.log(b) a、Uncaught ReferenceError: b is not defined b、未捕获的引用错误:b没有被定义 (4)SyntaxError,语法错误,例如 A、var 1 a、Uncaught SyntaxError: Unexpected number b、未捕获的语法错误:意外的数字 c、变量名首字符不应该为数字 B、var 1a a、Uncaught SyntaxError: Invalid or unexpected token b、未捕获的语法错误:无效的或意外的标记 c、变量名以数字开始了 C、new Object() a、Uncaught SyntaxError: Invalid or unexpected token b、未捕获的语法错误:无效的或意外的标记 c、半角括号写成全角括号了 D、function = 5 a、Uncaught SyntaxError: Unexpected token = b、未捕获的语法错误:意外的标记= c、function后面不应该是= E、console.log(1+2) a、Uncaught SyntaxError: missing ) after argument list b、未捕获的语法错误:在参数列表后,缺少) c、+为中文加号 F、if(true) a、Uncaught SyntaxError: Unexpected end of input b、未捕获的语法错误:输入意外结束 c、后面应该有花括号 G、JSON.parse(function(){}) a、Uncaught SyntaxError: Unexpected token u in JSON at position 1 b、未捕获的语法错误:意外的标记u H、json.parse(jsonData) a、Uncaught (in promise) SyntaxError: "[object Object]" is not valid JSON b、未捕获(承诺中)语法错误:“[object Object]”不是有效的JSON c、jsonData多了不必要的回车、最后一个逗号、不必要的双引号 I、.js文件中的路径转义(\)错误 a、Uncaught SyntaxError: Invalid Unicode escape sequence b、未捕获的语法错误:无效的Unicode转义序列 J、eval("{\"name\":\"123\"}"),将json字符串转换为json对象 a、Uncaught SyntaxError: Unexpected token ':' b、未捕获的语法错误:意外的标记: c、正确的用法为, console.log( eval("("+"{\"name\":\"123\"}"+")") ); console.log( eval("("+"[{\"name\":\"123\"}]"+")") ); console.log( eval("[{\"name\":\"123\"}]") ); (5)TypeError,类型错误,例如(7) A、123() a、Uncaught TypeError: 123 is not a function b、未捕获的类型错误:123不是一个函数 B、new 456 a、Uncaught TypeError: 456 is not a constructor b、未捕获的类型错误:456不是一个构造函数 (6)URIError,URI错误,例如(7) A、decodeURI("%") a、Uncaught URIError: URI malformed(畸形的) at decodeURI b、未捕获的URI错误:decodeURI处URI格式不正确 (7)手动抛错,中断代码执行如 throw new Error("提示文字") 4、其它报错 (1)Parsing error: Unexpected token解析错误:意外符号通常是eslint解析错误 5、错误处理 try{ 如果此处代码运行不出错,则catch函数不会执行 如果此处代码运行出错,则自动生成错误信息对象error传给catch函数, }catch(error){ 此处通过error.message读出错误信息 }finally{ 无论 try/catch 结果如何都会执行此处的代码块 } 十六、Date日期时间 附、https://www.runoob.com/jsref/jsref-obj-date.html 1、获取当前毫秒数的5种方法: var str1 = Date.now(); var str2 = +new Date(); var str3 = new Date() * 1; var str4 = new Date().getTime(); var str5 = new Date().valueOf(); var str = [str1, str2, str3, str4, str5]; for (var i = 0; i < str.length; i++) { console.log(str[i]); } 2、参数问题 (1)参数为多数字(年月日时分秒毫秒):获取的月份与实际一致,如new Date(2023, 11, 22, 10).getMonth() var d = new Date(year, month, day, hours, minutes, seconds, milliseconds); (2)其它情形参数:获取的月份比实际少1,如空、字符串、单数字(毫秒) var d = new Date(); var d = new Date(dateString); var d = new Date(milliseconds); (3)示例 var array = [ "2017-6-2", "2017-6-2 12:00:00", "2017/6/2", "2017/6/2 12:00:00", "6 2,2017", "6 2,2017 12:00:00", "6,2,2017", "6,2,2017 12:00:00", "Jun 2,2017", "Jun 2,2017 12:00:00", "Jun,2,2017", "Jun,2,2017 12:00:00", "Jun/2/2017", "Jun/2/2017 12:00:00", ]; var length = array.length; var month, str, milliseconds; var text1 = "获取的月份比实际少1,比如你获取的是"; var text2 = "获取的月份与实际一致,比如你获取的是"; for (var i = 0; i <= length + 2; i++) { if (i < length) { month = new Date(array[i]).getMonth(); str = "参数为:字符串," + text1 + month + "月,而实际上却是" + (month + 1) + "月"; } if (i === length) { month = new Date(2023, 11, 22, 10).getMonth(); str = "参数为:多数字," + text2 + month + "月,实际上就是" + (month + 0) + "月"; } if (i === length + 1) { month = new Date().getMonth(); str = "参数为:空," + text1 + month + "月,而实际上却是" + (month + 1) + "月"; } if (i === length + 2) { milliseconds = new Date().getTime(); month = new Date(milliseconds).getMonth(); str = "参数为:单数字," + text1 + month + "月,而实际上却是" + (month + 1) + "月"; } console.log(str); } 3、日期时间的格式化-手写 (1)把-秒数-转化为-日期时间,年月日时分秒 function secondsToDate(seconds) { var dateTime = new Date(seconds*1000); var yearMonthDay = dateTime.getFullYear() + '年' + (dateTime.getMonth() + 1) + '月' + dateTime.getDate() + '日' ; var hourMinuteSecond = dateTime.getHours() + '时' + (dateTime.getMinutes() + 1) + '分' + dateTime.getSeconds() + '秒' ; return { yearMonthDay: yearMonthDay, hourMinuteSecond: hourMinuteSecond, yearMonthDayHourMinuteSecond: yearMonthDay + hourMinuteSecond } } (2)把-秒数-转化为-时长,天时分秒 function formatSeconds(seconds) { function addZero(number) { return number.toString()[1] ? number : "0" + number; } var client = ""; var day = Math.floor(seconds / 86400); var hour = Math.floor((seconds % 86400) / 3600); var minute = Math.floor(((seconds % 86400) % 3600) / 60); var second = Math.floor(((seconds % 86400) % 3600) % 60); if (day > 0) { client += day + "天"; } if (day > 0 || hour > 0) { client += addZero(hour) + "小时"; } if (day > 0 || hour > 0 || minute > 0) { client += addZero(minute) + "分"; } client += addZero(second) + "秒"; return client; } (3)把-秒差-转化为-时长,天时分秒,倒计时 function countDown(future) { function addZero(number) { return number.toString()[1] ? number : "0" + number; } var now = new Date(); var future = new Date(future); var nowMilliseconds = now.getTime(); var futureMilliseconds = future.getTime(); var seconds = Math.floor((futureMilliseconds - nowMilliseconds)/1000) var day = Math.floor(seconds / 86400); var hour = Math.floor((seconds % 86400) / 3600); var minute = Math.floor(((seconds % 86400) % 3600) / 60); var second = Math.floor(((seconds % 86400) % 3600) % 60); var dayHourMinuteSecond = ""; var dateTimeObj = {}; if (day > 0) { dateTimeObj.day = day; dayHourMinuteSecond += day + "天"; } if (day > 0 || hour > 0) { dateTimeObj.hour = hour; dayHourMinuteSecond += addZero(hour) + "小时"; } if (day > 0 || hour > 0 || minute > 0) { dateTimeObj.minute = minute; dayHourMinuteSecond += addZero(minute) + "分"; } dateTimeObj.second = second; dayHourMinuteSecond += addZero(second) + "秒"; dateTimeObj.dayHourMinuteSecond = dayHourMinuteSecond; return dateTimeObj } /* var num = 0; var futureStr1 = "2030-10-01 00:00:00"; var futureStr2 = 1000*1000*1000*1000*1.8; var dateObj = countDown(futureStr1); console.log( dateObj ); var timer = setInterval(function(){ dateObj = countDown(futureStr1); console.log( dateObj ); num++; if(num>2)clearInterval(timer) },1000); */ (4)距-某日期时间-某秒-的-日期时间 function distanceDateTime (fixDateTime, distanceSeconds, separatorFront, separatorMiddle, separatorBack) { function addZero(number) { return number.toString()[1] ? number : "0" + number; } separatorFront = separatorFront || '-';//前分隔符,用来分割年月日 separatorMiddle = separatorMiddle || ' ';//中分隔符,用来分割年月日与时分秒 separatorBack = separatorBack || ':';//后分隔符,用来分割时分秒 var oldMilliseconds = (fixDateTime && new Date(fixDateTime).getTime()) || Date.now();//由固定日期时间生成的旧毫秒数 var newMilliseconds = oldMilliseconds + (distanceSeconds || 0) * 1000;//由旧毫秒数和间隔秒数生成的新毫秒数 var newObj = new Date(newMilliseconds);//由新毫秒数生成的日期时间对象 var newFullYear = newObj.getFullYear(); var newMonth = addZero(newObj.getMonth() + 1); var newDate = addZero(newObj.getDate()); var newHours = addZero(newObj.getHours()); var newMinutes = addZero(newObj.getMinutes()); var newSeconds = addZero(newObj.getSeconds()); var clientDate = newFullYear + "年" + newMonth + "月" + newDate + "日";//客户端日期 var clientTime = newHours + "时" + newMinutes + "分" + newSeconds + "秒";//客户端时间 var serverDate = newFullYear + separatorFront + newMonth + separatorFront + newDate;//服务端日期 var serverTime = newHours + separatorBack + newMinutes + separatorBack + newSeconds;//服务端时间 return { clientDate: clientDate, clientTime: clientTime, clientDateTime: clientDate + clientTime, serverDate: serverDate, serverTime: serverTime, serverDateTime: serverDate + separatorMiddle + serverTime, }; } 4、日期时间的格式化-框架内置 (1)过滤 $filter('date')( new Date(new Date().getTime() - 2 * 60 * 1000), 'yyyy-MM-dd HH:mm:ss' ); (2)获取中国标准时间 console.log(new Date('2020-12-5'))//Sat Dec 05 2020 15:21:57 GMT+0800 (中国标准时间) 5、日期时间中的汉字和分隔符互相转换 (1)汉字转换为分隔符 function toServerDatetime(str) { var regA = /\d+/g; var regB = /[年月日时分秒]/g; var tempReplace = str.replace(regA, function (match) { return match.toString()[1] ? match : "0" + match; }); var resultReplace = tempReplace.replace(regB, function (match) { var str = ""; if (match === "年" || match === "月") { str = "-"; } else if (match === "日") { str = " "; } else if (match === "时" || match === "分") { str = ":"; } else if (match === "秒") { str = ""; } return str; }); return resultReplace; } var str = "2022年5月25日16时15分6秒"; console.log(toServerDatetime(str)); (2)分隔符转换为汉字 function toClientDatetime(str) { var regA = /\d+/g; var regB = /[- :]/g; var tempReplace = str.replace(regA, function (match) { var numStr = match.toString(); if (numStr.length === 2) { match = numStr[0] == "0" ? numStr[1] : match; } return match; }); var index = 0; var resultReplace = tempReplace.replace(regB, function (match) { var str = ""; if (index === 0) { str = "年"; } else if (index === 1) { str = "月"; } else if (index === 2) { str = "日"; } else if (index === 3) { str = "时"; } else if (index === 4) { str = "分"; } index++; return str; }); return resultReplace + "秒"; } var str = "2022-05-25 16:15:06"; console.log(toClientDatetime(str)); 6、现在时间和倒计时: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>现在时间和倒计时</title> </head> <body> <p style="text-align: center;">现在时间是</p> <p style="text-align: center;" id="nowTime"></p> <p style="text-align: center; width: 1000px; height: 50px; line-height: 50px; margin: 50px auto; background: gray;">因为月份有28、29、30、31之分,所以为了避免计算错误,倒计时不出现年月</p> <p style="text-align: center;">距离2049年10月1日还有</p> <p style="text-align: center;" id="futureTime"></p> </body> <script> function addZero(number) { return number.toString()[1] ? number : "0" + number; } function countDown(future) { //以下是现在时间 var now = new Date(); var nowYear = now.getFullYear(); var nowMonth = now.getMonth() + 1; var nowDate = now.getDate(); var nowHour = now.getHours(); var nowMinute = now.getMinutes(); var nowSecond = now.getSeconds(); var nowStr = addZero(nowYear) + "年" + addZero(nowMonth) + "月" + addZero(nowDate) + "日" + addZero(nowHour) + "时" + addZero(nowMinute) + "分" + addZero(nowSecond) + "秒"; //以下是倒计时 var future = new Date(future); var nowMilliseconds = now.getTime(); var futureMilliseconds = future.getTime(); var seconds = Math.floor((futureMilliseconds - nowMilliseconds)/1000) var str = ""; var day = Math.floor(seconds / 86400); var hour = Math.floor((seconds % 86400) / 3600); var minute = Math.floor(((seconds % 86400) % 3600) / 60); var second = Math.floor(((seconds % 86400) % 3600) % 60); if ( day > 0) { str += day + "天"; } if ( day > 0 || hour > 0) { str += addZero(hour) + "小时"; } if ( day > 0 || hour > 0 || minute > 0) { str += addZero(minute) + "分"; } str += addZero(second) + "秒"; //以下是返回值 return { now:nowStr, future:str } } var nowDiv = document.getElementById('nowTime'); var futureDiv = document.getElementById('futureTime'); var futureStr = "2049-10-01 00:00:00"; var dateObj=countDown(futureStr); nowDiv.textContent = dateObj.now; futureDiv.textContent = dateObj.future; setInterval(function(){ var dateObj=countDown(futureStr); nowDiv.textContent = dateObj.now; futureDiv.textContent = dateObj.future; },1000); </script> </html> 十七、js判断数据类型的几种方法 1、数据类型 (1)五种基本数据类型:number、string、boolean、undefined(变量声明未赋值)、null(变量声明赋此值,typeof为'object') void,操作符,计算但不返回值,等同于undefined,console.log(void 0 === undefined && void(0) === undefined ) //true 用于html:<a href="javascript:void(0)">单击此处什么也不会发生</a> 用于JS:if(abc !== void 0)//if(abc !== undefined) (2)两种引用数据类型:函数、对象(实例获取的途径有,数组字面量、对象字面量、new类) (3)使用从未声明的变量a,报错为"a is not defined" 2、检测方法 //以下来源:https://www.cnblogs.com/dushao/p/5999563.html 附1、null与对象 console.log( value == null ) // 为true,则value为null console.log( typeof value == "object" && value ) // 为true,则value为普通对象 附2、以下是被检测的数据 var myNull = null; var myUndefined = undefined; var myString = "string"; var myNumber = 222; var myBoolean = true; var myFunction = function(){}; var myArray= [1,2,3]; var myObject = {a:1}; var myRegexp = /test/; var myDate = new Date(); var myError = new Error(); (1)typeof,返回值是字符串 console.log(typeof myNull === 'object') // true console.log(typeof myUndefined === 'undefined') // true console.log( '----------------------------------------' ); console.log(typeof myString === 'string') // true console.log(typeof myNumber === 'number') // true console.log(typeof myBoolean === 'boolean') // true console.log(typeof myFunction === 'function') // true console.log(typeof myArray === 'object') // true console.log(typeof myObject === 'object') // true console.log(typeof myRegexp === 'object') // true console.log(typeof myDate === 'object') // true console.log(typeof myError === 'object') // true console.log( '----------------------------------------' ); var obj = { 1: 'one', } for(var attr in obj){ console.log( typeof attr );//为什么是string,不是number } (2)instanceof,返回值是布尔 console.log(new String() instanceof String) // true console.log(new Number() instanceof Number) // true console.log(new Boolean() instanceof Boolean) // true console.log( '----------------------------------------' ); console.log(myString instanceof String) // false console.log(myNumber instanceof Number) // false console.log(myBoolean instanceof Boolean) // false console.log(myFunction instanceof Function) // true console.log(myArray instanceof Array) // true console.log(myObject instanceof Object) // true console.log(myRegexp instanceof RegExp) // true console.log(myDate instanceof Date) // true console.log(myError instanceof Error) // true (3)constructor,返回值是类 console.log(myString.constructor === String) // true console.log(myNumber.constructor === Number) // true console.log(myBoolean.constructor === Boolean) // true console.log(myFunction.constructor === Function) // true console.log(myArray.constructor === Array) // true console.log(myObject.constructor === Object) // true console.log(myRegexp.constructor === RegExp) // true console.log(myDate.constructor === Date) // true console.log(myError.constructor === Error) // true (4)prototype,返回值是字符串 console.log(Object.prototype.toString.call(myNull) === "[object Null]") // true; console.log(Object.prototype.toString.call(myUndefined) === "[object Undefined]") // true; console.log( '----------------------------------------' ); console.log(Object.prototype.toString.call(myString) === "[object String]") // true; console.log(Object.prototype.toString.call(myNumber) === "[object Number]") // true; console.log(Object.prototype.toString.call(myBoolean) === "[object Boolean]") // true; console.log(Object.prototype.toString.call(myFunction) === "[object Function]") // true; console.log(Object.prototype.toString.call(myArray) === "[object Array]") // true; console.log(Object.prototype.toString.call(myObject) === "[object Object]") // true; console.log(Object.prototype.toString.call(myRegexp) === "[object RegExp]") // true; console.log(Object.prototype.toString.call(myDate) === "[object Date]") // true; console.log(Object.prototype.toString.call(myError) === "[object Error]") // true; 十八、预解释、闭包、内存泄漏、函数调用、高阶函数、重载 附、预解释,在代码执行之前,JavaScript引擎会先对代码进行一次扫描,将变量声明和函数声明提升到当前作用域的顶部 1、闭包简略定义 (1)函数内部不会受外部干扰,这种机制叫做闭包。 (2)有权访问另一个函数作用域中的变量的函数,叫做闭包。 2、闭包详细定义及来源 (1)内部函数可以访问外部函数作用域,外部函数不能直接访问内部函数的作用域,即函数内部不会受外部干扰。函数的这种机制叫做闭包。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures (2)有权(能夠)访问另一个函数作用域中的变量的函数。《JavaScript高级程序设计》第178页 3、闭包优缺点 好处:避免污染全局变量; 坏处:有可能造成内存泄漏; 4、内存泄漏的条件和使用情形: (1)条件:函数里面的函数作为返回值被函数外面的变量引用 (2)使用情形:两函数中间的变量需要自增 5、函数的执行方式 (1)直接调用; (2)对象调用; (3)new调用; (4)call、apply调用 (5)自执行 A、开始+、-、!、void !function () { console.log(444); }(); B、前面() (function () { console.log(222); })(); C、外部() (function () { console.log(111); }()); D、外部[] [function () { console.log(333); }()]; 附、减少全局变量 for(var i=0; i<aBtn.length; i++){ (function(index){ aBtn[index].onclick=function click(){ alert(index); } })(i); } 附、连续3次自执行 (function (allFn) { allFn() })(function () { return (function outerFn(m) { function recursion(n) { console.log( n ); return n } recursion(m) return recursion })(1)(16) }); 附、return 逗号 function m() { return 1,2,3 } console.log(m()); 附、变量滞后 function n() { console.log(o); } var o = {}; n() 附、JS的逗号,用来将多个表达式连接为一个表达式,新表达式的值为最后一个表达式的值 (1)语句 var a,b,c,d; a = (b = 'bb', c = 'cc', d = 'dd'); console.log( a,b,c,d );//dd bb cc dd (2)函数 function aaa(){ var a = 'a'; var b = 'b'; var c = 'c'; return a, b, c } console.log( aaa() );//c 6、高阶函数:操作其他函数的函数,比如map、forEach 7、重载:Java一个函数可以有多个定义,只要这每个定义的签名(参数的类型和数量)不同即可。JS没有签名,所以不能重载。 十九、节流、去抖和柯里化 1、函数节流(throttle):不重新开始计时。 var last = 0;//上次点击时间 function clickIt(li) { var curr = +new Date(); if (curr - last < 2000) {//2秒钟 return }else{ last = curr; } //执行逻辑 } 2、函数去抖(debounce):重新开始计时。 var last = 0;//上次点击时间 function clickIt(li) { var curr = +new Date(); if (curr - last < 2000) {//2秒钟 last = curr; return } //执行逻辑 } 3、柯里化函数:把接受多个参数的函数变换成接受(第)一个参数的函数,然后用返回值函数接受余下的参数并返回结果。 js 如何实现sum(2,3)===sum(2)(3) function sum() { var arguments0 = arguments[0]; if (arguments.length === 1) { return function (arguments1) { return arguments0 + arguments1; } } if (arguments.length === 2) { return arguments[0] + arguments[1] } } console.log(sum(2,3)==sum(2)(3)); 二十、点透 1、为什么移动端浏览器的click事件延迟300毫秒执行?原因:要观察用户是否进行第2次点击,进而判断用户是想单击页面,还是想双击缩放页面 2、点透发生的情形:click移动端浏览器,立即触发touch事件,如果touch执行的结果是该元素从文档树上消失,300ms后系统要触发click的时候,发现该元素的位置是a或input元素,那么把click事件作用在这个元素上面了,这个现象给人的感觉就是“点透”了 3、点透的解决方案:在touch事件里,通过延迟执行touch事件的代码或阻止默认的click事件,来解决因单击而产生的点透问题 (1)阻止默认事件 ele.addEventListener("touchend",function (event) { ele.style.display="none"; event.preventDefault(); }); //以下zepto的tap事件点透解决 ele.on("touchend", function (e) { $("#alertBox").hide(); e.preventDefault(); }); (2)定时器延迟 ele.addEventListener("touchend",function (event) { setTimeout(function () { ele.style.display="none"; },400); }); //以下zepto的tap事件点透解决 ele.on("touchend", function () { setTimeout(function () { $("#alertBox").hide(); }, 350); }); 二十一、循环和if条件(含逗号) 1、for循环三种写法 for (语句 1; 语句 2; 语句 3){ //被执行的代码块 //语句1:在循环前执行 //语句2:循环的条件 //语句3:在循环后执行,缺失不会报错 } (1)写法一 for(var i=0;i<10;i++){ console.log(i); //如果我们用for循环要输出0到9,我们可以这么写 } (2)写法二 for(var i=10;i-=2;){//循环条件(为真时)、循环方式(递减),合二为一 console.log(i); //在语句2中,如果返回true循环会继续执行。在js中0,null,undefined,false,'',””作为条件判断时,其结果为false,也就说当i--到0的时候就是false,循环就终止了。 } (3)写法三 var rules = [-2, -1, 0, 2, 1]; for (var i = 0, rule; rule = rules[i++];) { console.log(rule); //var i = 0, rule;这是声明变量,是语句1,在循环前执行; //rule = rules[i++];这是循环的条件,是语句2;当成为undefined时就会终止循环。 } 2、for循环特殊案例 (1)外部可读 for(var i=0,j=0;i<10,j<6;i++,j++){ var k=j+i; } console.log(i,j,k);//6,6,10 (2)外部不可读 for(let i=0,j=0;i<10,j<6;i++,j++){ let k=j+i; } console.log(i,j,k);//i is not defined (3)只终止内层 for (var i = 0; i < 10; i++) { for (var j = 0; j < 10; j++) { if (i > 3) { // j > 3; i === j break; } console.log(i, j) } } 3、while循环 (1)执行代码放在while前 var i = 0; do { i++; console.log(i) } while (i < 4); (2)执行代码放在while后 var i=4; while (i--) {//条件 console.log(i) } 4、如果if条件成立,就执行代码 (1)if(isTrue)done() (2)isTrue && done(); 二十二、对象、原型链(__proto__)、多态 1、对象基本概念(面对对象编程) (1)类,是以函数的形式来定义的,函数也是一个对象,函数对象都有一个子对象prototype function Abc(){}; Abc.prototype.aaa = function(){}; (2)实例,通过new创建一个类的实例对象, var abc = new Abc(); abc.__proto__.aaa (3)原型链,构造函数原型与实例对象之间的连接,类的属性prototype对象的成员都是实例对象的成员,可以通过实例的属性__proto__获取,访问对象的一个属性或方法的时候,如果这个对象没有这个方法或属性,那么Javascript引擎将会访问这个对象的__proto__属性所指向的对象,若仍不能找到,就会继续向上查找,直到Object对象,还没找到该属性,则返回undefined console.log(Abc.prototype === abc.__proto__)//true console.log(Abc.prototype.aaa === abc.__proto__.aaa)//true console.log(aaa.__proto__.__proto__ === Object.prototype)//true 2、特殊原型链示例 var obj = {} function fn(){} (1)函数类或对象类或普通函数的原型 Object.prototype={}; Function.prototype={}; fn.prototype = {}; (2)函数类或对象类或普通函数或普通对象_所在类的原型 Function.__proto__ === Function.prototype Object.__proto__ === Function.prototype fn.__proto__ === Function.prototype; obj.__proto__ === Object.prototype; (3)函数类或对象类或普通函数或普通对象_所在类的原型_之所在类的原型... Function.__proto__.__proto__ === Function.prototype.__proto__ === Object.prototype Object.__proto__.__proto__ === Function.prototype.__proto__ === Object.prototype Function.__proto__.__proto__.__proto__ === Function.prototype.__proto__.__proto__ === Object.prototype.__proto__ === null Object.__proto__.__proto__.__proto__ === Function.prototype.__proto__.__proto__ === Object.prototype.__proto__ === null (4)原型链继承 Drag.prototype.__proto__ = EventEmitter.prototype;//这是更安全的继承方法,一般在Node里都是采用这种方式实现继承。IE不支持 Drag.prototype = new EventEmitter;//相对这种方式来说,上边的写法更安全 3、三个基本特征 (1)封装,可以隐藏实现细节,使得代码模块化,实现代码重用 (2)继承,可以扩展已存在的代码模块(类),实现代码重用 (3)多态,实现接口重用 4、多态 同一操作作用于不同的对象上面,可以产生不同的执行结果。比如你说“叫”,鸭子听了会发出“嘎嘎声”,鸡听了会发出“咯咯声”。 (1)非多态代码示例 var makeSound = function(animal) { if(animal instanceof Duck) { console.log('嘎嘎嘎'); } else if (animal instanceof Chicken) { console.log('咯咯咯'); } } var Duck = function(){} var Chiken = function() {}; makeSound(new Chicken()); makeSound(new Duck()); (2)多态的代码示例 var makeSound = function(animal) { animal.sound(); } var Duck = function(){} Duck.prototype.sound = function() { console.log('嘎嘎嘎') } var Chiken = function() {}; Chiken.prototype.sound = function() { console.log('咯咯咯') } makeSound(new Chicken()); makeSound(new Duck()); 另外,组件化开发的组件就相当于一个“对象”,一个操作“封装”成一个函数,通过调用内置方法“继承”内部的功能,“多态”没有体现 5、创建对象 (1)工厂模式: function creatAnimal(name,age) { var animal = new Object(); animal.name = name; animal.age = age; animal.eat = function(){ this.name + "会吃饭" } return animal } var dog = creatAnimal('小狗','2') console.log(dog); (2)构造函数模式: function Animal(name, age) { this.name = name; this.age = age; } this.prototype.eat = function () { console.log(this.name + "会吃饭"); } var dog = creatAnimal('小狗','2') console.log(dog); 6、Object.create(proto[, propertiesObject]) (1)参数, A、proto:新创建对象的原型对象 B、propertiesObject:新创建对象的(可)枚举属性 (2)返回值,一个新对象,带着指定的原型对象和属性 (3)实例 var myProto={proto:1}; var youProto={ enumer:{ enumerable: true, value: "custom" } }; var thisProto=Object.create(myProto,youProto) console.log(thisProto) console.log(thisProto.enumer) console.log(thisProto.__proto__===myProto) 7、Object.defineProperty(myObject, prop, descriptor) (1)参数, A、myObject要定义或修改的属性所在的对象 B、prop要定义或修改的属性的名称 C、descriptor要定义或修改的属性的描述符,共4个, (2)返回值,myObject (3)作用,定义或者修改myObject的属性 (4)描述符--限制描述符 A、configurable,默认为false。为true时,prop可删,描述符可改 B、enumerable,默认为false。为true时,prop可枚举 (5)描述符--数据描述符,和存取描述符不能并存 A、value,属性值,可以是数值、对象、函数等,默认为undefined B、writable,默认为false。为true时,prop可重写 (6)描述符--存取描述符,和数据描述符不能并存 A、get,属性的getter函数,当访问prop时,会调用此函数,该函数的返回值被用作属性值,默认为undefined。 B、set,属性的setter函数,当属性值被修改时,会调用此函数,该函数接受1个参数,默认为undefined。 (7)数据描述符和存取描述符使用示例 A、数据描述符 var myObj = { thisValue: 5 }; Object.defineProperty(myObj, "key", { configurable: false, enumerable: false, value: function () { console.log(this); return this.key+1; }, writable: true, //value: "myValue", //writable: false, }); myObj.key = 5; console.log(myObj.key); B、存取描述符 var myObj = { thisValue: 5 }; Object.defineProperty(myObj, "key", { configurable: false, enumerable: false, get: function () { console.log(this); return key+2; }, set: function (value) { console.log(this); key = value + 1; }, }); myObj.key = 5; console.log(myObj.key); (8)vue响应式相关代码 function def(obj, key, val, enumerable) { Object.defineProperty(obj, key, { configurable: true, enumerable: !!enumerable, value: val, writable: true, }); } Object.defineProperty(obj, key, { configurable: true, enumerable: true, get: function reactiveGetter() { var value = getter ? getter.call(obj) : val; return value; }, set: function reactiveSetter(newVal) { var value = getter ? getter.call(obj) : val; dep.notify(); }, }); 二十三、JS设计模式 来源:https://www.cnblogs.com/younghxp/p/16397059.html 扩展:https://blog.csdn.net/qq_41311259/article/details/119254280 包含:(1)策略模式(2)单例模式(3)代理模式(4)模板模式(5)享元模式(6)装饰器模式(7)观察者模式(8)发布-订阅模式 联想:车模、想装、担待 1、策略模式,算法使用与算法本身分离开来,避免“多重平行条件”选择语句,代码复用性高 另外,“多重嵌套条件”选择语句,仍然需要if-else嵌套或递归函数,不能使用策略模式 (1)策略模式1 let strategies = { 'S': function (salary) { return salary * 4 }, 'A': function (salary) { return salary * 3 }, 'B': function (salary) { return salary * 2 }, } function calculateBouns (level, salary) { return strategies[level](salary); } console.log(calculateBouns('S', 20000)) console.log(strategies[level](salary)) (2)策略模式2 var message = [ [!roleName,'角色名称不能为空!'], [!selfName,'注册名称不能为空!'], [outIds.length === rolesNum,'当前状态不能为空!'], ] for(var index = 0; index < message.length; index++){ if(message[index][0]){ return layer.open({ title: '系统信息', content: message[index][1], skin: 'layui-ext-yourskin' }); } } (3)策略模式3 //下面情形,用策略模式,不管范围有多少种,都只需1+2个判断,不用策略模式,则需要n+2个判断 var num = 400; var msg = ''; var rang = [ [0, 100, '检测数据' + num + '处于[0-100)之间'], [100, 200, '检测数据' + num + '处于[100-200)之间'], [200, 300, '检测数据' + num + '处于[200-300)之间'], [300, 400, '检测数据' + num + '处于[300-400)之间'], [400, 500, '检测数据' + num + '处于[400-500)之间'], [500, 600, '检测数据' + num + '处于[500-600)之间'], [600, 700, '检测数据' + num + '处于[600-700)之间'], ]; var tip = { front: '检测数据' + num + '小于' + rang[0][0], back: '检测数据' + num + '大于' + rang[rang.length - 1][1] } for (var i = 0; i < rang.length; i++) { if (i == 0 && num < rang[i][0]) { msg = tip.front; break; } if (i == rang.length - 1 && num > rang[i][1]) { msg = tip.back; break; } if (num >= rang[i][0] && num < rang[i][1]) { msg = rang[i][2]; break; } } console.log(msg) 2、单例模式,一个类仅有一个实例,提供访问他的全局api,如VueX,React-redux (1)惰性创建单例 var createDiv = (function () { var instance; return function () { if (!instance) { var div = document.createElement('div'); div.innerHTML = '我是登录窗口'; div.style.display = 'none'; document.body.appendChild(div); instance = div; } return instance } })() button.addEventListener('click', function () { let div = createDiv(); div.style.display = 'block'; }) (2)Class创建单例 class Singleton { constructor (name) { this.name = name } static getInstance (name) {//静态方法 if (!this.instance) { this.instance = new Singleton(name) } return this.instance } } let a = Singleton.getInstance('a1'); let b = Singleton.getInstance('b2'); console.log(a == b)//true 3、代理模式,为一个对象提供一个占位符,以便控制对它的访问; (1)不使用代理加载图片 function myImage(id,src){ var imgNode = document.createElement("img"); imgNode.src = src; document.getElementById(id).appendChild(imgNode); }; myImage.setSrc("id","pic.png"); (2)使用代理加载图片 function myImage(id,src,load){ //src:图片较大,下载慢,后出现 //load:图片较小,下载快,先占位 var imgNode = document.createElement("img"); imgNode.src = load; document.getElementById(id).appendChild(imgNode); var img = new Image(); img.src = src; img.onload = function(){ imgNode.setSrc(img.src); }; }; myImage.setSrc("id","loading.gif","pic.png"); (3)图片加载出错处理 $timeout(function () { var thisImg = new Image(); thisImg.src = $scope.r_g_company.logoPage.logo.src; thisImg.onerror= function() { $scope.r_g_company.logoPage.logo.src = $scope.r_g_company.logoPage.logo.srcCY } },1000); $.get('/app_v3/oem/info?' +new Date().getTime()).then(function (res) { $scope.r_g_company.logoPage.logo.src = '/audit-html/static/img/cy_oem/logo.png'; }).catch(function(){ $scope.r_g_company.logoPage.logo.src = '/audit-html/static/img/cy_oem/logo.png'; $scope.r_g_company.logoPage.logo.srcCY = '/audit-html/static/img/cy_tech/logo.png'; }); 4、模板模式,实现方式为继承 function Sport() {} Sport.prototype = { constructor: Sport, init: function() { this.stretch(); this.deepBreath(); }, stretch: function() { console.log('拉伸'); }, deepBreath: function() { console.log('深呼吸'); }, }; function Basketball() {}; Basketball.prototype = new Sport(); Basketball.prototype.start = function() { console.log('先投上几个三分'); }; basketball.init(); 5、享元模式,减少对象的数量 function Health(sex) { this.name = name; this.sex = sex; this.age = age; this.height = height; this.weight = weight; } Health.prototype.judge = function(name) { HealthManager.updateHealthData(name, this); var ret = name + ': '; if (this.sex === 'male') { ret += this.judgeMale(); } else { ret += this.judgeFemale(); } }; Health.prototype.judgeMale = function() { var ratio = this.height / this.weight; return this.age > 20 ? (ratio > 3.5) : (ratio > 2.8); }; Health.prototype.judgeFemale = function() { var ratio = this.height / this.weight; return this.age > 20 ? (ratio > 4) : (ratio > 3); }; var HealthFactory = { objs: {}, create: function(sex) { if (!this.objs[sex]) { this.objs[sex] = new Health(sex); } return this.objs[sex]; } }; var HealthManager = { healthData: {}, add: function(name, sex, age, height, weight) { var health = HealthFactory.create(sex); // 存储变化的数据 this.healthData[name] = { age: age, height: height, weight: weight }; return health; }, // 从存储的数据中获取,更新至当前正在使用的对象 updateHealthData: function(name, obj) { var healthData = this.healthData[name]; for (var item in healthData) { if (healthData.hasOwnProperty(item)) { obj[item] = healthData[item]; } } } }; var one = HealthManager.add('A', 'male', 18, 160, 80); var two = HealthManager.add('B', 'male', 21, 180, 70); one.judge('A'); // A: false two.judge('B'); // B: false 6、装饰器模式,在原来方法的基础上,添加一些新功能,有点类似代理模式,实际用于ajax请求的拦截器等 (1)创建主类 class Circle { draw() { console.log('画一个圆形'); } } (2)创建装饰器 class Decorator { constructor(circle) { this.circle = circle; } draw() { this.circle.draw(); this.setRedBorder(circle); } setRedBorder(circle) { console.log('画一个红色边框'); } } (3)使用装饰器 let circle = new Circle(); let decorator = new Decorator(circle); decorator.draw(); 7、观察者模式 (1)观察者类 class Observer { //name需要观察的参数 //callback 观察的参数达到边界条件触发的事件 constructor(name, callback) { this.name = name; this.callback = callback; } } (2)被观察者类 class Subject { //未传值初始为空 constructor(state) { //初始状态 this.state = state; //观察者方法队列 this.observsers = []; } //设置自己的状态 setState(val) { //告诉观察者目前改变成了什么状态 var that = this; this.state = val; //同时需要把自己身上的观察者方法全部触发 this.observsers.map(function(item){ //item是每一个观察者,每一个观察者是一个对象 item.callback(that.state); }) } //添加观察者 addObserver(observer) { //把观察者传递进来 this.observsers.push(observer); } //删除观察者 removeObserver(observer) { //过滤出来非当前观察者的观察者 this.observsers = this.observsers.filter(function(obs){ return obs !== observer; }); } } (3)使用 let obj = { name: "abc" }; //创建观察者与被观察者 //创建观察者 const observer = new Observer('watchName', function(newState){ console.log(newState); }); //创建一个被观察者 const subject = new Subject(obj.name); //添加一个观察者 subject.addObserver(observer); //触发观察者方法 subject.setState('123'); 8、发布-订阅模式 (1)发布者 class Publisher { constructor(name, channel) { this.name = name; this.channel = channel; } //添加目录 addTopic(topicName) { this.channel.addTopic(topicName); } //取消添加 removeTopic(topicName) { this.channel.removeTopic(topicName); } //发布晨报 publish(topicName) { this.channel.publish(topicName); } } (2)订阅者 class Subscriber { constructor(name, channel) { this.name = name; this.channel = channel; } //订阅目录 subscribe(topicName) { this.channel.subscribeTopic(topicName, this); } //取消订阅 unSubscribe(topicName) { this.channel.unSubscribeTopic(topicName, this); } //接收发布后,更新 update(topic) { console.log(`${topic}已经送到${this.name}家了`); } } (3)第三方平台 class Channel { constructor() { this.topics = {}; } //发布者在平台添加目录 addTopic(topicName) { this.topics[topicName] = []; } //发布者取消添加 removeTopic(topicName) { delete this.topics[topicName]; } //订阅者订阅目录 subscribeTopic(topicName, sub) { if (this.topics[topicName]) { this.topics[topicName].push(sub); } } //订阅者取消订阅 unSubscribeTopic(topicName, sub) { this.topics[topicName].forEach(function(item, index){ if (item === sub) { this.topics[topicName].splice(index, 1); } }); } //平台通知某个目录下所有订阅者 publish(topicName) { this.topics[topicName].forEach(function(item){ item.update(topicName);//发布的内容-传给-订阅者的函数 }); } } (4)应用 A、定义1个调度中心channel var channel = new Channel(); B、定义2个发布者publisher1、publisher2 var publisher1 = new Publisher("发布者1", channel); var publisher2 = new Publisher("发布者2", channel); C、定义3个订阅者subscriber1、subscriber2、subscriber3 var subscriber1 = new Subscriber("订阅者1", channel); var subscriber2 = new Subscriber("订阅者2", channel); var subscriber3 = new Subscriber("订阅者3", channel); D、2家发布者在平台上添加了晨报1、晨报2和晨报3三种晨报,如{'晨报1':[]} publisher1.addTopic("晨报1"); publisher1.addTopic("晨报2"); publisher2.addTopic("晨报3"); publisher2.removeTopic("晨报3");//取消添加 E、3个订阅者在平台上订阅了晨报,将自己的实例注入到数组中,如{'晨报1':[thisInstance1]} subscriber1.subscribe("晨报1"); subscriber2.subscribe("晨报1"); subscriber2.subscribe("晨报3"); subscriber3.subscribe("晨报2"); subscriber3.subscribe("晨报3"); subscriber3.unSubscribe("晨报3");//取消订阅 F、发布者推送某个主题,遍历订阅者,并执行订阅者如thisInstance1的update函数 publisher1.publish("晨报1"); publisher1.publish("晨报2"); publisher2.publish("晨报3"); (5)观察者模式与发布订阅模式的区别 A、观察者模式,为紧耦合,只有两个角色,目标对象维护观察者对象,并通知观察者 B、发布订阅模式,为松耦合,有三个角色,多了一个第三方平台,目标对象和观察者对象,通过第三方平台进行通信 (6)Vue的基本原理,运用发布-订阅模式,具体可查看手写vue2.x原理:https://gitee.com/younghxp/vue2-data-response 二十四、与parseFloat相关 原文地址:https://yq.aliyun.com/ziliao/92498?spm=5176.8246799.blogcont.20.cUDmIE 1、Number() (1)如果是Boolean值,true和false值将分别被转换为1和0。 (2)如果是数字值,只是简单的传入和返回。 (3)如果是null值,返回0。 (4)如果是undefined,返回NaN。 (5)如果是字符串: A、如果字符串中只包含数字时,将其转换为十进制数值,忽略前导0 B、如果字符串中包含有效浮点格式,如“1.1”,将其转换为对应的浮点数字,忽略前导0 C、如果字符串中包含有效的十六进制格式,如“0xf”,将其转换为相同大小的十进制数值 D、如果字符串为空,将其转换为0 E、如果字符串中包含除上述格式之外的字符,则将其转换为NaN (6)如果是对象,则调用对象的valueOf()方法,然后依照前面的规则转换返回的值。如果转换的结果是NaN,则调用对象的toString()方法,然后再依照前面的规则转换返回的字符串值。 var num1 = Number("Hello world");//NaN var num2 = Number("");//0 var num3 = Number("0000011"); //11 2、parseInt() (1)处理整数的时候parseInt()更常用。parseInt()函数在转换字符串时,会忽略字符串前面的空格,知道找到第一个非空格字符。 (2)如果第一个字符不是数字或者负号,parseInt() 就会返回NaN,同样的,用parseInt() 转换空字符串也会返回NaN。 (3)如果第一个字符是数字字符,parseInt() 会继续解析第二个字符,直到解析完所有后续字符串或者遇到了一个非数字字符。 (4)parseInt()方法还有基模式,可以把二进制、八进制、十六进制或其他任何进制的字符串转换成整数。 (5)基是由parseInt()方法的第二个参数指定的,所以要解析十六进制的值,当然,对二进制、八进制,甚至十进制(默认模式),都可以这样调用parseInt()方法。 var num1 = parseInt("AF",16); //175 var num2 = parseInt("AF");//NaN var num3 = parseInt("10",2); //2(按照二进制解析) var num4 = parseInt("sdasdad");//NaN 3、parseFloat() (1)与parseInt() 函数类似,parseFloat() 也是从第一个字符(位置0)开始解析每一个字符。也是一直解析到字符串末尾,或者解析到遇见一个无效的浮点数字字符为止。 (2)也就是说,字符串中第一个小数点是有效的,而第二个小数点就是无效的了,它后面的字符串将被忽略。 (3)parseFloat() 只解析十进制,因此它没有第二个参数指定基数的用法 (4)如果字符串中包含的是一个可解析为正数的数(没有小数点,或者小数点后都是零),parseFloat() 会返回整数。 var num1 = parseFloat("123AF"); //123 var num2 = parseFloat("0xA");//0 var num3 = parseFloat("22.5"); //22.5 var num4 = parseFloat("22.3.56");//22.3 var num5 = parseFloat("0908.5"); //908.5 parseInt() 和parseFloat() 的区别在于: parseFloat() 所解析的字符串中第一个小数点是有效的,而parseInt() 遇到小数点会停止解析,因为小数点并不是有效的数字字符。 parseFloat() 始终会忽略前导的零,十六进制格式的字符串始终会被转换成0,而parseInt() 第二个参数可以设置基数,按照这个基数的进制来转换。 4、customParseInt:字符串类型的数字转换成数字类型的数字、数字类型的数字和字符串类型的非数字保持原样 function customParseInt(num) { var tempNum = parseInt(num); if (typeof tempNum === 'number' && !isNaN(tempNum)) { return tempNum; } return num; }; console.log(customParseInt(1)) console.log(customParseInt("1")) console.log(customParseInt("闪烁")) 4、整型范围 (1)有负号整型范围 Int8:[-128:127] Int16:[-32768:32767] Int32:[-2147483648:2147483647] Int64:[-9223372036854775808:9223372036854775807] (2)无负号整型范围 UInt8:[0:255] UInt16:[0:65535] UInt32:[0:4294967295] UInt64:[0:18446744073709551615] 二十五、业务逻辑 注、软件一般分为三个层次:表现层、业务逻辑层、数据层 1、表现层:负责界面展示 2、业务逻辑层:接收表现层的请求,向数据层请求数据,将数据返给表现层 (1)业务,要实现的核心需求 (2)业务逻辑,实现核心需求的代码 3、数据层:负责数据存储、读取 二十六、造轮子:在编程中,业界已经有公认的软件或者库了,你明知道自己不可能比它做得更好,却还坚持要做