说明:在编辑器里找到bcbf-web\src\assets\icons\svg\edit.svg时,该文件默认显示为图片。右键该文件,点击打开方式...,点击文本编辑器,该文件显示为代码
一、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 style="transform: scale(0.7) translate(-100px) rotate(0deg)">
1、直线
<div>
"<"line x1="0" y1="20" x2="200" y2="70" stroke="gray" stroke-width="2" /">"
</div>
<svg class="svgUp">
<line x1="0" y1="20" x2="200" y2="70" stroke="gray" stroke-width="2"/>
</svg>
</pre>
<pre style="transform: scale(0.7) translate(-220px) rotate(0deg)">
2、折线
<div>
"<"polyline points="20,10 30,100 170,50 70,50" style="fill:#ccc; stroke:#000; stroke-width:2" /">"
</div>
<svg class="svgUp">
<polyline points="20,10 30,100 170,50 70,50" style="fill:#ccc;stroke: #000;stroke-width:2"/>
</svg>
</pre>
<pre style="transform: scale(0.7) translate(-520px) rotate(0deg)">
3、多边形
<div>
"<"polygon points="20,10 30,100 170,50 70,50" style="fill:#ccc; stroke:#000; stroke-width:2" /">"
</div>
<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" style="transform: scale(0.7) translate(-220px,-54px) rotate(0deg)">
2、属性
"<"path d="M120 30 L10 80 L250 80 Z" fill="#000" stroke="red" stroke-width="2" /">"
(1)d,domain,包含所有的绘图命令
A、M:move,将笔移动到指定点x,y而不绘图
B、L:line,绘制一条从当前笔位置到指定点x,y的直线
C、Z:zone,通过从当前点到第一个点画一条线来封闭路径
D、H:horizontal,画一条水平线到指定的点x,笔当前的y
E、V:vertical,画一条水平线到指定的点y,笔当前的x
(2)fill,轮廓内的填充颜色
(3)stroke,轮廓内的描边颜色
(4)stroke-width,轮廓内的描边宽度
</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基础
附、问答
问:ECMAScript2015为什么叫ES6?
答:它是ES5之后的版本,大家习惯按序号叫它ES6,而官方按年份叫ECMAScript2015
请按照下列格式,告诉我,es6中Map的增删改查的方法及简单示例
1、Set,唯一值数组,ES6提供的新的数据结构(集合),类似于数组,成员都是唯一、无索引、无序的
(1)增,add
const s = new Set();
s.add(1).add("hello").add({ name: "张三" });
(2)删,delete、clear
const s = new Set([1, 2, 3, "hello"]);
s.delete(2)
s.clear(); //清空
(3)改,无直接方法
const s = new Set([1, 2, 3]);
if(s.has(2)) s.delete(2).add(20) //需求:将2改为20
(4)查:has、size(类似数组length)、遍历
const s = new Set(["a", "b", "c", 123]);
console.log(s.has("a")); //判断元素是否存在
console.log(s.size); //获取元素数量
for (const item of s) { //遍历查询(无索引,只能遍历取值)
if (item === "b") {
console.log("找到b");
}
}
s.forEach((item) => { //forEach遍历
console.log(item);
});
(5)常见需求
A、并集
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var c = new Set([...a, ...b]);
B、去重
var list = new Set([1, 1, 2, 3, 3, 4]);
C、交集
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var c = new Set([...a].filter(x => b.has(x)))
D、排序,转换为数组后排序
//数字排序
const set = new Set([3, 1, 4, 1, 5, 9, 2]);
const sortedArray = Array.from(set).sort((a, b) => a - b);
const sortedSet = new Set(sortedArray);
console.log(sortedArray); // [1, 2, 3, 4, 5, 9]
//字符串排序
const strSet = new Set(['banana', 'apple', 'cherry']);
const sortedStrSet = new Set(Array.from(strSet).sort());
console.log([...sortedStrSet]); // ['apple', 'banana', 'cherry']
2、Map,键值对集合,ES6 提供的新的数据结构(映射),类似于对象,键可以是任意类型、保留插入顺序
附、Map和WeakMap的区别
A、Map,键可以是任意类型(包括基本类型、对象、函数等)
B、WeakMap,键只能是对象(null除外)。在全局创建实例,在不同页面定义同名不同实例的对象,并作为键名添加到实例的键值对,不会冲突
(1)增,set
const m = new Map();
m.set(1,“数字键")
(2)删,delete、clear
m.delete("age");
m.clear();//清空
(3)改,set
const m= new Map (I"age",20]);
m.set("age",25);//需求:将age的值从20改为25
(4)查:get、has、size(获取键值对数量)、遍历
const m = new Map();
m.set(1, "数字键").set("name", "张三").set("age", 20).set({ id: 1 }, "对象键");
console.log(m.get("name")); // 获取指定键的值
console.log(m.has("age")); // 判断键是否存在
console.log(m.size); // 获取键值对数量
m.forEach((value, key) => { // forEach遍历
console.log(`${key} → ${value}`);
});
for (const [key, value] of m) { // 遍历键值对
if (key === "age") {
console.log(`找到age,值为${value}`);
}
}
for (const key of m.keys()) { // 仅遍历键
console.log("键:", key);
}
for (const value of m.values()) { // 仅遍历值
console.log("值:", value);
}
(5)常见需求
A、Map转普通对象
const m = new Map ([["name", "赵六"], ["age", 30], [1, "数字键"]]);
const obj = Object.fromEntries (m);
console.log( obj );
B、普通对象转Map
const obj = { name: "钱七", age: 35 };
const m = new Map (Object.entries (obj));
console.log( m );
C、筛选Map中的键值对
const m = new Map ([["name", "孙八"], ["age", 40], ["gender", "男"]]);
const filteredMap = new Map ([...m].filter (([key, value]) => value > 30)); // 筛选值大于 30 的键值对
console.log( filteredMap );
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 str = '123';
var [a, b, c] = str;
console.log(a, b, c);
(2)解构数组
var arr = [1, 2, 3];
var [a, b, c] = arr;
console.log(a, b, c);
(3)解构对象
var person = {name: 'zhangsan', address: {province: '江苏', city: '南京'}};
var {name, address:{province, city}} = person;//有key,无value
console.log(name, province, city);
(4)解构函数参数
var isTrue = true;
var object = { A: 1, B: 2, C: 3, D: 4 }
if( isTrue ) {
function solution({ A, B, C, D}) {
console.log( A, B, C, D, '甲');
}
} else {
function solution(...args) {
const { A, B, C, D } = args[0];
console.log(A, B, C, D, '乙');
}
}
solution(object)
5、var、let、const的区别
(1)var--,无块级作用域,有变量提升,能声明多次,能重新赋值
(2)let--,有块级作用域,无变量提升,能声明一次,能重新赋值
(3)const,有块级作用域,无变量提升,能声明一次,不能重新赋值
(4)变量提升
A、var声明的变量以及function声明的函数,会在代码执行前被JS引擎“提升”到当前作用域的顶部
B、在变量声明之前访问var变量,值是undefined,直到真正执行到赋值语句
C、在变量声明之前访问function函数,值是function函数
(5)i=i+1释疑
A、数学理解,移项,合并同类项0=1
B、编程理解,变量赋值,把右边的计算结果赋值给左边的变量
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,继承自定义时的外层作用域,而不是调用时的对象
const obj = {
name: 'Alice',
sayName: () => {
console.log(this.name);
}
};
obj.sayName(); // 输出 undefined
function Timer() {
this.seconds = 0;
setTimeout(() => {
this.seconds++;
console.log( this.seconds );
}, 1000);
}
new Timer()
(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进阶
附、Proxy对象与Reflect对象的方法的方法一一对应
1、Proxy,代理,内部拦截,Proxy必须针对Proxy实例进行操作,否则Proxy不起作用,
示例1、const proxy = new Proxy(target, handler);
示例2、
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);
(1)Proxy.apply(target, object, args)
拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...) 。
(2)Proxy.construct(target, args)
拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
(3)Proxy.defineProperty(target, propKey, propDesc)
拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
(4)Proxy.deleteProperty(target, propKey)
拦截delete proxy[propKey]的操作,返回一个布尔值。
(5)Proxy.get(target, propKey, receiver)
拦截对象属性的读取,比如proxy.starFunction 和 proxy['starFunction']。
如果propKey属性部署了读取函数,则读取函数的this绑定receiver(一个对象)。
(6)Proxy.getOwnPropertyDescriptor(target, propKey)
拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
(7)Proxy.getPrototypeOf(target)
拦截Object.getPrototypeOf(proxy),返回一个对象。
(8)Proxy.has(target, propKey)
拦截propKey in proxy的操作,以及对象的hasOwnProperty方法,返回一个布尔值。
(9)Proxy.isExtensible(target)
拦截Object.isExtensible(proxy),返回一个布尔值。
(10)Proxy.ownKeys(target)
拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy),返回一个数组。
该方法返回对象所有自身的属性,而Object.keys()仅返回对象可遍历的属性。
(11)Proxy.preventExtensions(target)
拦截Object.preventExtensions(proxy),返回一个布尔值。
(12)Proxy.set(target, propKey, value, receiver)
拦截对象属性的设置,比如proxy.starFunction = v或proxy['starFunction'] = v,返回一个布尔值。
(13)Proxy.setPrototypeOf(target, proto)
拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。
如果目标对象是函数,那么还有两种额外操作可以拦截。
另外,vue2用Object.defineProperty实现双向绑定,vue3用Proxy实现双向绑定
2、Reflect,反映,外部操纵,
示例1、Reflect.method(target, ...args);
示例2、
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
(1)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
(2)Reflect.construct(target, args)
等同于new target(...args),这提供了一种不使用new,来调用构造函数的方法。
(3)Reflect.deleteProperty(obj, name)
等同于delete obj[name]。
(4)Reflect.get(target, name, receiver)
查找并返回target对象的name属性,如果没有该属性,则返回undefined
如果name属性部署了读取函数,则读取函数的this绑定receiver
(5)Reflect.getPrototypeOf(obj)
读取对象的__proto__属性,对应Object.getPrototypeOf(obj)
(6)Reflect.has(obj, name)
等同于name in obj
(7)Reflect.ownKeys(target)查找并返回target对象的所有属性,包括不可枚举属性和象征属性
等同于Object.getOwnPropertyNames(target)
(8)Reflect.set(target, name, value, receiver)
设置target对象的name属性为value。如果name属性设置了赋值函数,则赋值函数的this绑定receiver
(9)Reflect.setPrototypeOf(obj, newProto)
设置对象的__proto__属性,对应Object.setPrototypeOf(obj, newProto)。
3、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属性。
4、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));
5、async和await
(1)说明
A、await语句只能在async函数内部使用
B、阻塞当前异步函数内部的后续代码
C、不阻塞当前异步函数外部的后续代码
(2)示例一
A、定义
// 定义异步函数
async function fetchData() {
console.log('开始获取数据...');
// 模拟网络请求
const response = await new Promise((resolve) => {
setTimeout(() => {
resolve({ data: '这是获取的数据' });
}, 1000);
});
console.log('数据获取成功!');
return response.data;
}
B、调用1
// 执行异步函数
async function main() {
try {
console.log('程序开始执行');
// 调用异步函数并等待结果
const data = await fetchData();
console.log('获取到的数据:', data);
console.log('程序执行完毕');
} catch (error) {
console.error('发生错误:', error);
}
}
// 启动主函数
main();
C、调用2
fetchUserData(123)
.then((user) => {
console.log('处理后的数据:', user);
})
(3)示例二、
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 {
}
});
五、各种模块说明
1、AMD
(1)依赖,require.js,采用异步方式加载模块
(2)特征
A、模块依赖通过异步加载,不会阻塞后续代码执行。当所有依赖加载完成后,才会执行回调函数。
B、模块内部逻辑需定义在回调函数中,确保依赖已加载完成。
(3)定义模块
define ("abc", ["a", "b", "c", "d"], function (a, b, c, d) {
// 直接使用参数 a, b, c, d,无需再次 require
return {
// 模块导出内容
};
});
(4)引用模块
require (['abc','def'],function (abc,def){});
2、CMD
(1)依赖,sea.js,采用异步方式加载模块。
(2)特征,在回调函数里,需要某个依赖,才会加载这个依赖
(3)定义模块
define ("abc", ["a", "b", "c", 'd'], function (require, exports, module) {
var a = require ('a');
});
(4)引用模块
require (["abc"], function (abc){});
3、ESModule中模块示例,以ES6为例
(1)默认导出和导入,只能导出1次
A、导出,mathUtils.js
export default {
add,
subtract,
multiply
};
// 下面是错误用法(同一模块不能有多个默认导出)
//export default add;
//export default subtract;
//export default multiply;
B、导入,导入时可以自定义名称
import mathUtils from './mathUtils.js';
(2)*导出和导入
A、导出,newName.js
export {
add,
subtract,
multiply
};
// 下面是正确用法(需带关键字 const/let/function 等)
export const add = (a,b) => a+b;
export const subtract = (a,b) => a-b;
export const multiply = (a,b) => a*b;
B、导入,使用通配符导入整个模块
import * as newName from "./abc";
// 以下进一步使用,两种任选一种
//const {add,subtract,multiply} = newName;
//newName.add();newName.subtract();newName.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 const add1 = (a,b) => a+b;
export const subtract1 = (a,b) => a-b;
export const multiply1 = (a,b) => a*b;
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 newNameRest from './d.js'; //导入包括默认导出在内的所有成员(默认导出可通过newNameRest.default访问)
(6)改名导出和导入
A、导出
export {abc as newAbc, ABC as newABC}
B、导入
import {newAbc, newABC} from "./abc";
import {newAbc as anotherAbc, newABC as anotherABC} from "./abc";
4、CommonJS中模块规范(在后台nodejs开发中使用,在前端框架处理兼容时使用)
(1)导出模块 module.exports = obj;
(2)导入模块 var example = require ('./example.js'); var area = example.area;// 使用模块
(3)CommonJS 规范主要特点
A、模块化代码:CommonJS允许将代码按功能或逻辑分类成独立的模块,每个模块只负责特定的功能,使代码更加可维护和可复用
B、缓存代码:CommonJS规范提供了模块的加载和缓存机制,可以确保模块只会被加载一次,避免重复加载和执行,提高性能
C、隔离命名空间:每个模块都有自己独立的作用域,不会与其他模块中的变量和函数冲突,有效避免命名冲突
D、跨平台使用:CommonJS规范主要用于Node.js等服务器端JavaScript运行环境,在浏览器中需通过工具转换后使用
(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 () {}
5、ESModule和CommonJS的区别
(1)语法不同
A、ESModule 用 export 和 import 导出和导入,import 在编译时加载,必须放在模块顶层
B、CommonJS 用 module.exports 和 require 导出和导入,require 在运行时加载,可以放在任何位置
C、import 和 require 不能直接代替对方使用(语法和加载机制不同)
(2)注意
A、ESModule 可在现代浏览器中直接使用(需加 type="module"),CommonJS 不能在浏览器中直接使用,需转换
B、早期的 node 要用 babel 等工具将 ES6 模块转为 CommonJS 模块(vue-loader 用于处理.vue 文件,与模块转换无关)
C、2019 年发布的 node13.2.0 开始支持 ES6 模块,需在 package.json 中添加 "type": "module",与 vue-loader 无关
6、UMD说明
(1)Universal Module Definition,通用模块定义,用于判断代码运行环境
(2)实例
(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(function(require, exports, module) {
module.exports = factory();
});
} else if (typeof exports === 'object') { //CommonJS-like 环境
exports["aaa"] = factory();
} else if (typeof self === 'object') { //web worker 等环境
self["aaa"] = factory();
} else if (typeof global === 'object') { // 全局环境如 node 的 global
global["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)获取当前毫秒数的6种方法
var str0 = Date.now();
var str1 = +new Date();
var str2 = new Date() - 0;
var str3 = new Date() * 1; //实际示例,new Date(addOrEditForm.value.startTime) < new Date()
var str4 = new Date().getTime();
var str5 = new Date().valueOf();
var str = [str0, str1, str2, str3, str4, str5];
for (var i = 0; i < str.length; i++) {
console.log(i, str[i]);
}
(5)年龄计算
function getAge(birthYear){
var nowYear = new Date().getFullYear();
var age = nowYear - birthYear;
var text = '此人出生于' + birthYear + '年,今年' + age + '岁';
return text;
}
console.log( getAge(1955) )
(6)闰年计算
function judgeLeapYear(year){
var isTrue1 = year%4 == 0 && year%100 != 0; //能被4整除但不能被100整除
var isTrue2 = year%400 == 0; //能被400整除
if(isTrue1 || isTrue2){
return year + '年是闰年';
}else{
return year + '年不是闰年';
}
}
console.log( judgeLeapYear(1916) );
2、Date的参数
if(true){
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]);
var zero = i === 0? "1、\n": "";
var str = zero+"参数为:"+array[i]+ ",你获取的年、月、日分别是:"+date.getFullYear()+ "、"+date.getMonth()+"、"+date.getDate();
console.log(str);
}
console.log( "-----以上,系统把“字符串”参数按照“实际”的年月日处理,返回系统的年月日,此观点论据如上面注释-----" );
console.log( "--------------------------------------------------------------------------------" );
console.log( "-----以下,系统把“数字”参数按照“系统”的数据处理,返回系统的年月日,此观点论据如下面注释-----" );
}
if(true){
var date1 = new Date();
var year1 = date1.getFullYear();
var month1 = date1.getMonth();
var day1 = date1.getDate();
var str1 = "1、\n参数为:"+"空,系统把无参默认为系统的现在,返回系统现在的年月日:"+year1+ "、"+month1+"、"+day1;
console.log(str1);
}
if(true){
var milliseconds = new Date().getTime();
var date2 = new Date(milliseconds);
var year2 = date2.getFullYear();
var month2 = date2.getMonth();
var day2 = date2.getDate();
var str2 = "2、\n参数为:1个数字,把从系统获取的现在的“毫秒数”作为参数传给系统,获取的仍然是系统现在的年月日:"+year2+ "、"+month2+"、"+day2;
console.log(str2);
}
if(true){
var date0 = new Date();
var year0 = date0.getFullYear();
var month0 = date0.getMonth();
var day0 = date0.getDate();
var hour0 = date0.getHours();
var minute0 = date0.getMinutes();
var second0 = date0.getSeconds();
var ary = [year0, month0, day0, hour0, minute0, second0];
var date3 = new Date(...ary);
var year3 = date3.getFullYear();
var month3 = date3.getMonth();
var day3 = date3.getDate();
var str3 = "3、\n参数为:n个数字,把从系统获取的现在的“年月日”作为参数传给系统,获取的仍然是系统现在的年月日:"+year3+ "、"+month3+"、"+day3;
console.log(str3);
console.log( "--------------------------------------------------------------------------------" );
console.log( "-----注意,系统月份比实际月份小1" );
}
if(true){
for( var date = -2; date < 3; date++ ){
var month = 5 ;
var newDate = new Date(2020, month, date);
var monthStr = '此时的参数是:' + month + '、'+ date;
var dateStr = '此时的月日是:' + newDate.getMonth() + '、'+ newDate.getDate();
console.log( monthStr, dateStr );
}
}
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 * 3600 * 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、Day.js使用示例
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Day.js使用示例</title>
<style></style>
<script src="https://cdn.jsdelivr.net/npm/dayjs/dayjs.min.js"></script>
</head>
<body>
<h1>多次刷新浏览器后,在控制台看Day.js的使用效果</h1>
</body>
<script>
// import { ElMessage, dayjs } from 'element-plus'
var date = new Date();
console.log('注意:', 'dayjs、add、format的参数');
console.log('“指定的日期时间”与“指定的日期时间”是否“相等”', dayjs(date).isSame(date));
console.log('“指定的日期时间”与“指定的日期时间”是否“相等”', dayjs('2024-01-08').isSame('2024-01-08'));
console.log('“指定的日期时间”与“指定的日期时间”是否“相等”', dayjs('2024-01-08 16:32:42').isSame('2024-01-08 16:32:41'));
console.log('前1年的日期和时间', dayjs().add(-1, 'year').format('YYYY-MM-DD HH:mm:ss'));
console.log('前1月的日期和时间', dayjs().add(-1, 'month').format('YYYY-MM-DD HH:mm:ss'));
console.log('前1天的日期和时间', dayjs().add(-1, 'day').format('YYYY-MM-DD HH:mm:ss'));
console.log('当天日期和时间-服务端', dayjs().format('YYYY-MM-DD HH:mm:ss'));
console.log('当天日期和时间-客户端', dayjs().format('YYYY年MM月DD日 HH时mm分ss秒'));
console.log('后1天的日期和时间', dayjs().add(1, 'day').format('YYYY-MM-DD HH:mm:ss'));
console.log('后1月的日期和时间', dayjs().add(1, 'month').format('YYYY-MM-DD HH:mm:ss'));
console.log('后1年的日期和时间', dayjs().add(1, 'year').format('YYYY-MM-DD HH:mm:ss'));
</script>
</html>
5、正计时-获取时长年月日
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>正计时-发电报/打字机-效果</title>
<style>
* {
background: #000;
padding: 0;
margin: 0;
}
p {
color: white;
font-size: 65px;
text-indent: 130px;
font-family:"kaiti";
}
</style>
</head>
<body>
<p id="ele"></p>
</body>
</html>
<script>
//以下,定义函数-获取时长-年月日
function getTimeLength(pastDate) {
const past = typeof pastDate === 'string' ? new Date(pastDate) : new Date(pastDate.getTime());// 确保pastDate是Date对象
const now = new Date();// 获取当前日期
if (isNaN(past.getTime())) {// 验证日期有效性
throw new Error('无效的日期格式,请使用YYYY-MM-DD格式或有效的Date对象');
}
if (past > now) {// 确保过去日期不晚于今天
throw new Error('过去日期不能晚于当前日期');
}
let years = now.getFullYear() - past.getFullYear();// 计算完整年份
let months = now.getMonth() - past.getMonth();// 计算月份
let days = now.getDate() - past.getDate();// 计算天数
if (days < 0) {// 处理负数的月份和天数
const lastDayOfLastMonth = new Date(now.getFullYear(), now.getMonth(), 0).getDate();// 获取上个月的天数
days += lastDayOfLastMonth; //本月缺损的天数由上个月补足,即最终的天数为上个月剩余的天数
months--;
}
if (months < 0) {
months += 12;
years--;
}
return `${years}年${months}个月${days}天`
}
function showText(str, id) {
var text = "";
var [year, month, day] = str.split("-");
text += "钱成,河南省固始县人。";
text += year+"年"+month+"月"+day+"日开始从事前端工作,至今已工作" + getTimeLength(str) + "。";
text += "现在是人类最优秀的前端工程师。联系电话:137\xa01616\xa04418。完!";
var n = 0;
var ele = document.getElementById(id);
var timer = setInterval(function () {
ele.innerHTML += text.charAt(n);
n++;
if (n >= text.length)clearInterval(timer);
}, 100);
};
showText( '2017-02-20', 'ele' )
</script>
6、正计时-获取日期和时长
<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="durA"></div>
<div id="dateA"></div>
</div>
<div class="display">
<div id="dateB"></div>
<div id="durB"></div>
</div>
</body>
<script>
//以下,定义函数-获取时长-年月日时分秒
function getTimeLength(pastDateTime) {// 解析输入日期
let past;
if (typeof pastDateTime === 'string') {// 处理字符串格式(支持多种分隔符)
const dateTimeParts = pastDateTime.split(/[-/ :T]/);
if (dateTimeParts.length < 3) {
throw new Error('无效的日期时间格式,请使用YYYY-MM-DD HH:MM:SS格式');
}
// 补全时间部分(默认为00:00:00)
const year = parseInt(dateTimeParts[0], 10);
const month = parseInt(dateTimeParts[1], 10) - 1; // 月份从0开始
const day = parseInt(dateTimeParts[2], 10);
const hour = dateTimeParts[3] ? parseInt(dateTimeParts[3], 10) : 0;
const minute = dateTimeParts[4] ? parseInt(dateTimeParts[4], 10) : 0;
const second = dateTimeParts[5] ? parseInt(dateTimeParts[5], 10) : 0;
past = new Date(year, month, day, hour, minute, second);
} else if (pastDateTime instanceof Date) {
console.log( 'pastDateTime instanceof Date' );
past = new Date(pastDateTime.getTime());
} else {
throw new Error('无效的日期时间格式,请使用Date对象或YYYY-MM-DD HH:MM:SS格式字符串');
}
if (isNaN(past.getTime())) {// 验证日期有效性
throw new Error('无效的日期时间');
}
const now = new Date();// 获取当前时间
if (past > now) {// 确保过去时间不晚于现在
throw new Error('过去时间不能晚于当前时间');
}
const diffMs = now - past;// 计算总毫秒差
// 计算秒、分、时、日
let seconds = Math.floor(diffMs / 1000);
let minutes = Math.floor(seconds / 60);
seconds = seconds % 60;
let hours = Math.floor(minutes / 60);
minutes = minutes % 60;
let days = Math.floor(hours / 24);
hours = hours % 24;
// 计算年和月(需要处理月份和闰年)
let years = now.getFullYear() - past.getFullYear();
let months = now.getMonth() - past.getMonth();
// 调整年和月
if (months < 0 || (months === 0 && now.getDate() < past.getDate())) {
years--;
months += 12;
}
// 计算剩余天数(考虑不同月份的天数)
let startDate = new Date(past.getFullYear(), past.getMonth(), past.getDate());
let endDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
// 如果开始日期的日 > 结束日期的日,需要借一个月
if (startDate.getDate() > endDate.getDate()) {
endDate = new Date(endDate.getFullYear(), endDate.getMonth() - 1, endDate.getDate());
const daysInLastMonth = new Date(endDate.getFullYear(), endDate.getMonth() + 1, 0).getDate();
days = endDate.getDate() + (daysInLastMonth - startDate.getDate());
} else {
days = endDate.getDate() - startDate.getDate();
}
return `${years}年${months}个月${days}天${hours}小时${minutes}分钟${seconds}秒`;
}
//以下,定义函数-获取时长-日时分秒
function getLengthTime(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 getDatetime(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 serverToClient(dateTime) {
var isTrue = true;
if(isTrue){
var [year, month, day, hour, minute, second] = dateTime.split(/[- :]/);
dateTime = year+"年"+month+"月"+day+"日"+hour+"时"+minute+"分"+second+"秒";
}else{
dateTime = dateTime.replace('-', "年").replace('-', "月").replace(' ', "日")
dateTime = dateTime.replace(':', "时").replace(':', "分") + "秒"
}
return dateTime
}
// 以下,获取日期
var durationStr = '1000 * 3600 * 24'; //为正获取未来时间,为负获取过去时间,为0获取当前时间
var durationNum = eval(durationStr);
var durEleA = document.getElementById('durA');
var dateEleA = document.getElementById('dateA');
// 以下,获取时长
var datetimeStr = '2022-02-24 11:00:00'; //早于现在为持续时长,晚于现在为倒计时;
var datetimeDesc = '<br>开始的<b>俄乌战争</b>,到现在';
var dateEleB = document.getElementById('dateB');
var durEleB = document.getElementById('durB');
var isFlag = false;
var strOut = '';
if(isFlag){
strOut = getLengthTime(datetimeStr).client + ',即<br>'
}
function interval() {
var now = getDatetime(0).client;//durEleA.textContent
durEleA.innerHTML = '距现在<br>'+ now + '<br>' + durationNum + '(' + durationStr + ')毫秒,是';
dateEleA.innerHTML = getDatetime(durationNum).client;
dateEleB.innerHTML = '北京时间<br>'+ serverToClient(datetimeStr) + datetimeDesc + '<br>' + now + '<br>已经持续了';
durEleB.innerHTML = strOut + getTimeLength(datetimeStr);
}
interval()
// 以下,开启定时器
setInterval(function(){
interval()
},1000);
</script>
</html>
十二、对象相关
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、多态
附、同一操作作用于不同的对象上面,可以产生不同的执行结果。比如你说“叫”,鸭子听了会发出“嘎嘎声”,鸡听了会发出“咯咯声”。
(1)非多态代码示例
var makeSound = function(animal) {
if(animal instanceof Duck) {
console.log('嘎嘎嘎');
} else if (animal instanceof Chicken) {
console.log('咯咯咯');
}
}
var Duck = function(){}
var Chiken = function() {};
makeSound(new Chicken());
makeSound(new Duck());
(2)多态的代码示例
var makeSound = function(animal) {
animal.sound();
}
var Duck = function(){}
Duck.prototype.sound = function() {
console.log('嘎嘎嘎')
}
var Chiken = function() {};
Chiken.prototype.sound = function() {
console.log('咯咯咯')
}
makeSound(new Chicken());
makeSound(new Duck());
另外,组件化开发的组件就相当于一个“对象”,一个操作“封装”成一个函数,通过调用内置方法“继承”内部的功能,“多态”没有体现
5、创建对象
(1)工厂模式:
function creatAnimal(name,age) {
var animal = new Object();
animal.name = name;
animal.age = age;
animal.eat = function(){
this.name + "会吃饭"
}
return animal
}
var dog = creatAnimal('小狗','2')
console.log(dog);
(2)构造函数模式:
function Animal(name, age) {
this.name = name;
this.age = age;
}
this.prototype.eat = function () {
console.log(this.name + "会吃饭");
}
var dog = creatAnimal('小狗','2')
console.log(dog);
6、Object.create(proto[, propertiesObject])
(1)参数,
A、proto:原型
B、propertiesObject:属性
(2)返回值,新对象
(3)实例
var prototype = { proto: 'proto' };
var props = {
attr: {
enumerable: true,
value: 'attr'
}
};
var obj = Object.create( prototype, props )
console.log( obj )
console.log( obj.attr )
console.log( obj.proto )
console.log( obj.__proto__ === prototype, 'obj.__proto__ === prototype' )
7、Object.defineProperty(myObject, prop, descriptor)
来源,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
(1)参数,
A、myObject要定义或修改的属性所在的对象
B、prop要定义或修改的属性的名称
C、descriptor要定义或修改的属性的描述符,共4个,
(2)返回值,myObject
(3)作用,定义或者修改myObject的属性
(4)共享-描述符
A、configurable,默认为false。为true时,prop可删,描述符可改
B、enumerable,默认为false。为true时,prop可枚举
(5)数据-描述符,和访问器描述符不能并存
A、value,属性值,可以是数值、对象、函数等,默认为undefined
B、writable,默认为false。为true时,prop可重写
(6)访问器-描述符,和数据描述符不能并存
A、get,属性的getter函数,当访问prop时,会调用此函数,该函数的返回值被用作属性值,默认为undefined。
B、set,属性的setter函数,当属性值被修改时,会调用此函数,该函数接受1个参数,默认为undefined。
(7)示例
A、数据-描述符
var myObj = { thisValue: 5 };
Object.defineProperty(myObj, "key", {
configurable: false,
enumerable: false,
value: 'myValue',
writable: true,
});
myObj.key = 'youValue';
console.log(myObj.key);
B、访问器-描述符
var myObj = { thisValue: 5 };
Object.defineProperty(myObj, "key", {
configurable: false,
enumerable: false,
get: function () {
console.log(this);
return key+2;
},
set: function (value) {
console.log(this);
key = value + 1;
},
});
myObj.key = 5;
console.log(myObj.key);
C、访问器-描述符
var eric={
ename:"艾力克",
eage:25
};
Object.defineProperties(eric,{
_eage:{
value:eric.eage,
writable:true
},
eage:{
get:function(){
return this._eage;
},
set:function(value){
if(value>=18 && value<=65){
this._eage=value;
}else{
throw Error("年龄必须介于18~65岁之间");
}
},
enumerable:true,
configurable:false
}
});
console.log(eric.eage);//25
eric.eage=18;
console.log(eric.eage);//18
eric.eage=16;
console.log(eric.eage);//超出范围,报错
(8)vue响应式相关代码
function def(obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
configurable: true,
enumerable: !!enumerable,
value: val,
writable: true,
});
}
Object.defineProperty(obj, key, {
configurable: true,
enumerable: true,
get: function reactiveGetter() {
var value = getter ? getter.call(obj) : val;
return value;
},
set: function reactiveSetter(newVal) {
var value = getter ? getter.call(obj) : val;
dep.notify();
},
});
十三、函数相关
1、相关定义
(1)函数:一段可以重复使用的代码块
(2)高阶函数:操作其他函数的函数,接受一个到多个函数作为参数,或者返回一个函数作为结果,比如map、forEach
(3)柯里化函数(curry):将一个接受多个参数的函数转换为一系列接受单一参数的函数
(4)重载:Java一个函数可以有多个定义,只要这每个定义的签名(参数的类型和数量)不同即可。JS没有签名,所以不能重载
(5)预解释:在代码执行之前,JavaScript引擎会先对代码进行一次扫描,将变量声明和函数声明提升到当前作用域的顶部
2、闭包:有权访问另一个函数作用域中变量的函数
(1)好处,实现数据封装、实现状态持久化、实现模块化设计
(2)坏处,只要闭包存在,外部函数的变量就不会被释放
(3)闭包占用内存的场景
A、场景1:DOM元素与闭包的循环引用
function createMenu() {
const div = document.createElement("div");
div.innerHTML = "Menu";
//闭包引用了 div,导致 div 无法被回收
document.body.addEventListener("click", () => {
console.log(div.textContent);
});
return div;
}
//假设menu不再需要,但事件监听器中的闭包仍引用div
const menu = createMenu();
document.body.removeChild(menu); //DOM元素被移除,但内存未释放
function cleanup() {
document.body.removeEventListener("click", ...);
}
B、场景2:全局变量引用闭包
let globalClosure;
function createClosure() {
const largeData = new Array(1000).fill("data");
globalClosure = function() {
console.log(largeData.length); //闭包捕获largeData
};
}
createClosure();
//即使不再需要largeData,它也不会被回收,因为globalClosure仍然存在
C、场景3:计数器工具,状态持久化
function createCounter(initialValue = 0) {
let count = initialValue; // 闭包捕获的状态变量,外部无法直接访问
return {
increment: () => { // 累加并返回新值
count++;
return count;
},
decrement: () => { // 递减并返回新值
count--;
return count;
},
reset: () => { // 重置为初始值
count = initialValue;
return count;
},
getCurrent: () => count // 获取当前值
};
}
// 使用案例
const counter1 = createCounter(10); // 初始值为10的计数器
console.log(counter1.increment()); // 输出11(状态变为11)
console.log(counter1.increment()); // 输出12(状态变为12)
console.log(counter1.decrement()); // 输出11(状态变为11)
console.log(counter1.getCurrent()); // 输出11(状态保持11)
const counter2 = createCounter(); // 初始值为0的独立计数器
console.log(counter2.increment()); // 输出1(与counter1状态完全隔离)
console.log(counter1.getCurrent()); // 输出11(counter1状态不受影响)
3、内存泄漏的情形
(1)意外的全局变量
(2)未解除的事件监听器
(3)未移除的定时器或回调函数
(4)闭包,有权访问另一个函数作用域中变量的函数
4、函数this指向
(1)普通函数:指向window
(2)构造函数:指向实例对象
(3)箭头函数:继承自定义时的外层作用域,而不是调用时的对象
(4)对象方法:指向对象
function getDate() {
var now = new Date();
var years = now.getFullYear();
var months = now.getMonth() + 1;
var days = now.getDate();
return {
years: years,
months: months,
days: days,
toString: function() {
return `${this.years}年 ${this.months}个月 ${this.days}天`;
}
};
}
console.log( getDate() );
console.log( getDate().toString() );
5、函数执行(函数调用)方式
(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)
}