批改状态:合格
老师批语:
这里面好像没啥好说的,都是老师之前教过的知识,老师也说了event都可以自己查询,是常用的
<button onclick="shou()">点击</button><ul class="lisk"><li class="li1">测试1</li><li class="li2">测试2</li><li class="li3">测试3</li><li class="li4">测试4</li><li class="li5">测试5</li></ul><script>function shou() {// event事件的对象,包含了很多的值 type 类型,查看当前类型,肯定就是点击咯console.log(event.type);// currentTarget绑定事件的主体,也就是标签,当前绑定的是button按钮console.log(event.currentTarget);//target事件触发者,自己想想,你点击的是谁?button按钮,所以button按钮就是触发者,也等于事件的主体console.log(event.target);// 可以试下//返回了一个trueconsole.log(event.currentTarget === event.target);if (event.currentTarget === event.target) {console.log("完全相同");}// // 拿到li标签的一组,声明为li// const li = document.querySelectorAll("li");// // for循环拿到每一个li标签// for (i = 0, length = li.length; i < length; i++) {// // 给li标签添加一个点击事件并返回一下绑定的事件主体// // li[0]到4,.onclick添加一个点击事件// li[i].onclick = console.log(event.currentTarget);// // 查看每一个li的idconsole.log(i);}
示例截图:
对li标签的冒泡代码:
const li = document.querySelectorAll("li");// for循环拿到每一个li标签for (i = 0, length = li.length; i < length; i++) {// 给li标签添加一个点击事件并返回一下绑定的事件主体// li[0]到4,.onclick添加一个点击事件// 老师说onclick此时,就是事件冒泡 默认就是冒泡阶段li[i].onclick = function () {console.log(event.currentTarget.textContent);console.log(event.target.textContent);console.log(event);};}
示例截图:
冒泡的逐级上报!!
代码示例:
//逐级冒泡,就是从下往上,一直到html// ul.onclickdocument.querySelector("ul").onclick = () => console.log(event.currentTarget);// body.onclickdocument.body.onclick = () => console.log(event.currentTarget);// html.onclick// documentElement 拿到html 别忘了语法糖document.documentElement.onclick = () => console.log(event.currentTarget);
示例代码截图:
事件阻止冒泡:
阻止冒泡代码:stopPropagation()
使用示例:event.stopPropagation();
需在点击事件执行命令的函数中使用
示例代码:
for (i = 0, length = li.length; i < length; i++) {// 给li标签添加一个点击事件并返回一下绑定的事件主体// li[0]到4,.onclick添加一个点击事件// 老师说onclick此时,就是事件冒泡 默认就是冒泡阶段li[i].onclick = function () {// 阻止冒泡语法糖.stopPropagation()event.stopPropagation();console.log(event.currentTarget.textContent);console.log(event.target.textContent);console.log(event);};}
示例截图:
事件的委托:
顾名思义 事件的委托就是让另外一个标签去执行当前标签的事件行为,代码示例的li标签让父级ul
示例代码:
///////////////事件委托///////把点击事件绑定到ul上面// 将原来逐个添加到li上的click,现在根据冒泡原理,只需要添加到父级ul一次就可以// 事件委托: 子元素上的事document.querySelector("ul").onclick = function () {// 阻止冒泡// event.stopPropagation();// 打印的绑定者是ulconsole.log(event.currentTarget);// 但触发者竟然是liconsole.log(event.target);console.log(event.currentTarget === event.target);// 从79行开始,这就是事件的委托,事件的代理,委托给了ul,li的上级触发};
示例截图:
全部代码示例:
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>事件的冒泡与事件的委托代理</title></head><body><button onclick="shou()">点击</button><ul class="lisk"><li class="li1">测试1</li><li class="li2">测试2</li><li class="li3">测试3</li><li class="li4">测试4</li><li class="li5">测试5</li></ul><script>function shou() {// event事件的对象,包含了很多的值 type 类型,查看当前类型,肯定就是点击咯console.log(event.type);// currentTarget绑定事件的主体,也就是标签,当前绑定的是button按钮console.log(event.currentTarget);//target事件触发者,自己想想,你点击的是谁?button按钮,所以button按钮就是触发者,也等于事件的主体console.log(event.target);// 可以试下//返回了一个trueconsole.log(event.currentTarget === event.target);if (event.currentTarget === event.target) {console.log("完全相同");}// // 拿到li标签的一组,声明为li// const li = document.querySelectorAll("li");// // for循环拿到每一个li标签// for (i = 0, length = li.length; i < length; i++) {// // 给li标签添加一个点击事件并返回一下绑定的事件主体// // li[0]到4,.onclick添加一个点击事件// li[i].onclick = console.log(event.currentTarget);// // 查看每一个li的idconsole.log(i);}//为什么拿出来,因为目前实在button里面,拿到外面li就是一个单独的点击事件const li = document.querySelectorAll("li");// for循环拿到每一个li标签for (i = 0, length = li.length; i < length; i++) {// 给li标签添加一个点击事件并返回一下绑定的事件主体// li[0]到4,.onclick添加一个点击事件// 老师说onclick此时,就是事件冒泡 默认就是冒泡阶段li[i].onclick = function () {console.log(event.currentTarget.textContent);console.log(event.target.textContent);console.log(event);};}//逐级冒泡,就是从下往上,一直到html// ul.onclickdocument.querySelector("ul").onclick = () => console.log(event.currentTarget);// body.onclickdocument.body.onclick = () => console.log(event.currentTarget);// html.onclick// documentElement 拿到html 别忘了语法糖document.documentElement.onclick = () => console.log(event.currentTarget);for (i = 0, length = li.length; i < length; i++) {// 给li标签添加一个点击事件并返回一下绑定的事件主体// li[0]到4,.onclick添加一个点击事件// 老师说onclick此时,就是事件冒泡 默认就是冒泡阶段li[i].onclick = function () {// 阻止冒泡语法糖.stopPropagation()event.stopPropagation();console.log(event.currentTarget.textContent);console.log(event.target.textContent);console.log(event);};}for (i = 0, length = li.length; i < length; i++) {// 停止点击事件li[i].onclick = null;}///////////////事件委托///////把点击事件绑定到ul上面// 将原来逐个添加到li上的click,现在根据冒泡原理,只需要添加到父级ul一次就可以// 事件委托: 子元素上的事document.querySelector("ul").onclick = function () {// 阻止冒泡// event.stopPropagation();// 打印的绑定者是ulconsole.log(event.currentTarget);// 但触发者竟然是liconsole.log(event.target);console.log(event.currentTarget === event.target);// 从79行开始,这就是事件的委托,事件的代理,委托给了ul,li的上级触发};</script></body></html>
event配合标签preventDefault禁止用户主动提交/本来的php提交禁用掉,用js提交数据stopPropagation禁用掉冒泡操作
示例:
// 禁用函数preventDefault()event.preventDefault();// 禁用冒泡event.stopPropagation();
老师的提交作业,是当用户失去焦点时验证,我在网上查了下代码,写了一个
使用了一个空函数message写的
这一部分代码,没啥需要截图的,直接上代码:
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>表单事件</title></head><body><div><form action="" method="post" id="login"><label class="title">用户登录</label><label for="email">邮箱:</label><input type="email" id="email" name="email" value="" autofocus onblur="message()" /><label for="password">密码:</label><input type="password" id="password" name="password" onblur="message()" /><button name="submit" onclick="ad(this)">登录</button></form><script>function ad(ele) {// 测试下弹窗// alert("测试");// 禁用提交行为,就是php带的提交 改为js提交 或者是在from里面使用一个onsubmit 返回一个false// 禁用函数preventDefault()event.preventDefault();// 禁用冒泡event.stopPropagation();console.log(event);// 拿到email方便以后的操作/是通过函数的储存的值拿的,我只能这么理解,就是ele拿的let email = ele.form.email;// 拿到password 方便以后的操作let password = ele.form.password;// 判断value的长度是否为空if (email.value.length === 0) {alert("请输入邮箱");// 如果没输入提示用户输入,光标回到email框email.focus();// 返回值,我注释了,发现还可以return false;// 判断password长度是否为空} else if (password.value.length === 0) {alert("请输入密码");// 给email 输入框返回一个光标password.focus();return false;} else {alert("验证通过");}}// 1. submit: 提交时触发// 2. focus: 获取焦点时触发// 3. blur: 失去焦点时触发// 4. change: 值发生改变且失去焦点时触发// 5. input: 值发生改变就会触发,不必等失去焦点,与change不同// 作业: 将该表单验证方式,改为失去焦点就进行非空验证, 而不必等到点击提交按钮时///////////////////////////////////////////////////////////////////////作业部分////////////////////function message() {if (email.value.length === 0) {alert("请输入邮箱");// 给email 输入框返回一个光标email.focus();return false;} else if (password.value.length === 0) {alert("请输入密码");return false;} else {alert("验证通过");}}</script></div><style>* {margin: 0;padding: 0;box-sizing: border-box;}body {width: 100vw;height: 100vh;display: grid;background-color: aquamarine;place-content: center;}div {width: 20em;height: 10em;}form {display: grid;}#login > label:first-of-type {text-align: center;}</style></body></html>
js运行机制之单线程(主线程)+事件循环
代码示例介绍:
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>js运行机制: 单线程+事件循环</title></head><body><script>// 1. 单线程: 主线程// 同步: 代码的"书写顺序"与"执行顺序"一致, 写在前面先执行console.log(1);console.log(2);console.log(3);console.log("--------------");console.log(1);setTimeout(() => console.log(2), 3000);console.log(3);// 定时任务: setTimeout(函数,等待时间)// console.log(1);// 要等待三秒后才执行,这三秒理论上说,是什么也做不了,代码到这里就"阻塞"// setTimeout(() => console.log(2), 3000);// console.log(3);// js引擎将第二个任务离开了"主线程",放到了"任务队列"的地方// 当主线程上已没有任务的时候,由"事件循环"将这个任务重新放回到主线程中执行// 执行机制// 1. 同步任务: 主线程// 2. 异步任务: 任务队列, 事件循环来调度// 哪些是异步任务?// 1. 定时任务, 2. 事件, 3, IO操作(input,output), 4. http请求// 怎么实现异步? 回调函数</script></body></html>
我的代码:(我的两个输入框注意区分,一个是带定时器的 一个是不带的)
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><form action=""><input type="text" name="text" id="text" value="" onkeydown="ad(this)" /><inputtype="text"name="text"id="text"value=""onkeydown="setTimeout(() => {adb(this)// 当主线程带有延迟操作时,使用一次性定时器给线程增加一个定时操作,也就是异步操作}, 1); "/><input type="text" oninput="adc(this)" placeholder="input" /></form><script>function ad(ele) {let value = ele.value;console.log(value);}function adb(ele) {let value = ele.value;console.log(value);}function adc(ele) {let value = ele.value;console.log(value);}</script></body></html>
老师的代码:
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>单线程异步的演示</title></head><body><form action="" style="display: grid; gap: 1em; width: 20em"><input type="text" onkeydown="console.log(this.value)" placeholder="同步" /><!-- 慢半拍 --><!-- 分析为什么第一次获取不到?1. 当按下某个键时, 它会触发keydown事件,事件循环将事件方法从任务队列添加到主线中,并立即调用,即console.log立即执行2. 但是此时这个键对应的值,例如1, 还没有被DOM渲染到input输入框中,所以console.log获取不到值,或获取一个空值3. 因为"按下1" 和 "显示1" 是2步操作,而console.log是在第1步结束后一瞬间执行,而值还没被显示到input框中4. 所以, console.log()执行时机,是在"按下"与"显示"之间,所以无法获取到用户输入的内容或获取到一个空值5. 为什么第2次按下时,console.log()可以获取到了呢?6. 是因为当用户再次按下时, console.log()立即执行,此时input中已经有了内容,就是上一次的输入内容,所以就正常输出了 --><!-- 解决方案也很简单,让console.log()异步执行即可,等到input框中的内容渲染完成之后再执行 --><!-- 必知内容:1. DOM渲染是同步任务, 这里的dom渲染,是指将内容显示到input框中2. DOM事件是异步任务, 使用keydown3. 异步必须在同步完成后执行--><!-- 现在简单了, 将console.log异步调用就可以,直接放在setTimeout中就可以,将延迟设置为0 --><!-- 因为setTimeout是异步任务,一定会等主线程中的同步任务执行完毕才会进入主线程,转为同步任务执行而此时, dom渲染已完成, input框中已经有了数据,所以console.log()就正确的, 同步的获取到了数据完美解决了"慢半拍"的问题 --><input type="text" onkeydown="setTimeout(()=>console.log(this.value),0)" placeholder="异步" /><!-- 解决方案 --><input type="text" oninput="console.log(this.value)" placeholder="input" /><!-- input = keydown + setTimeout --></form></body></html>
| 代码 | 示例 | 说明 |
|---|---|---|
| .length | str.length | 索引长度 |
| .charAt | str.charAt(5) | 索引返回当前的索引的字符,示例是第五个 |
| .indexOf | str.indexOf(“p”) | 根据字符返回当前字符的索引 |
| .search | str.search(“中”) | 跟indexOf一样,但是支持正则 |
| .concat | str.concat(“(“, “学php”, “)”) | 拼装,不过现在都用+习惯来拼装了 |
| .replace | str.replace(“php”, “PHP”) | 查找替换,并不是完整意义上的替换 |
| .slice | str.slice(0, 3) | 根据索引取字符串 |
| .substr | str.substr(-5, 3) | 根据索引字符串,支持正则和从后面提取 |
| .split | str.split(“”) | 字符串拆分成数组,如果不加””就直接一个数组 |
| .toLowerCase | str.toLowerCase() | 转换字母的大小写 |
| html相关的一组 | html相关的一组 | html相关的一组 |
| .link(“域名”) | str.link(“域名”) | 直接插入域名 |
| .bold() | str.bold() | 加粗操作 |
| .big() | str.big() | 加大操作 |
| .anchor(“url”) | str.anchor(“url”) | 增加一个锚点,增加到name属性 |
没有什么比截图示例更好的了!!
上代码:
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>字符中常用api-1操作</title></head><body><script>let str = "pHp中文网";// .length 查询字符长度 经常用了..console.log(str.length);//.charAt 根据索引返回当前的索引的字符console.log(str.charAt(5)); //返回的是--网//.indexOf 根据字符获取索引 输入相关字符数字,返回当前数组的索引值console.log(str.indexOf("p")); //发现一个问题,里面要是俩p.返回离索引0包括索引0在内的最近的一个// .search跟indexOf一样的 但支持正则// console.log(str.search("中"));// concat 字符串的拼装console.log(str.concat("(", "学php", ")"));// 都用+和`` 拼装了// replace替换console.log(str.replace("php", "PHP")); //查找替换,只是无中生有的替换,并不是真正意义上的替换,// 重新输出一下,仍然是原来的console.log(str);// slice 取子串// 取值从0到3索引//不支持从后面取console.log(str.slice(0, 3));// substr 取子串 支持从后往前取substr(-3,3)代表从后面第三个取三个 -1就是最后一个console.log(str.substr(-5, 3));// split 字符串拆分变成数组console.log(str.split("")); //要转为数组,括号里面加个引号,如果不加,整个字符都变成了一个数组// toLowerCase, toUpperCase 大小写转换console.log(str.toLowerCase()); //刚刚改了下,中间H改成了大小,转换后又为小小了console.log(str.toUpperCase()); //转换成大写// html相关的一组console.log(`<a href="https://php.cn">${str}</a>`);// 以字符的写法更简单,其实愿意用哪个用哪个console.log(str.link("https:www.baidu.com"));console.log(str.bold()); //加粗console.log(str.big()); //加大console.log(str.anchor("url")); //增加一个name属性,值为url,也叫锚点</script></body></html>
上截图:
使用到了…rest展开、复制操作
使用到了Array.of 离散值改变为数组操作
Array.from 类数组进行转换
代码部分:
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body></body><script>let arr = [1,2,3,"arr","true",{x: 1,y: 2,},[1, 2, 3],function () {},];console.log(arr);// 复制数组 使用... 展开let arr1 = [...arr];console.log(arr1);// 扩展数组 拿到上面的arr在展开arr = [1, 2, 3, ...arr, 7, 8, 9];console.log(arr);// Array.of 把离散值创建成数组arr = Array.of(1, 2, 3, 4, 5, 6);console.log(arr);// 如果得到的数组需要分两步操作,所以就需要二次使用,可以用...展开二次使用let ab = [1, 2, 3, 4, 5, 6];arr = Array.of(...ab);console.log(arr);</script></html>
代码示例截图:
数组字符串
尾部新增:push,
尾部删除pop,
头部新增unshift,
头部删除shift,
删除数组delete,但索引长度不变
代码部分:
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><script>// 数组字符串// 尾部新增:push, 尾部删除pop, 头部新增unshift, 头部删除shift, 删除数组delete,但索引长度不变let ab = [];// 头部新增console.log(ab.push(10, 12));// 为了确认下 看下日志console.log(ab.length);// 看下数组console.log(ab);// 删除最后一个console.log(ab.pop(12));// 查看下数组console.log(ab);// 头部新增console.log(ab.unshift(1, 8));console.log(ab);// 头部删除console.log(ab.shift(1));console.log(ab);// 删除数组delete ab[1];console.log(ab);// 先创建一个数组let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];delete arr[0];console.log(arr);</script></body></html>
示例截图:
| 参数 | 示例 | 说明 |
|---|---|---|
| forEach | forEach(function(item, index, arr){…}) | forEach使用方法 forEach(function(item, index, arr){…})没个元素单独的逐个回调处理,使用方法 forEach(function(数组的值, 数组的索引(或者键), 数组){…}) |
| map | 跟forEach类似,但是有返回值 | |
| every | arr.every(item => item >= 3) | every数组成员全部满足条件则返回true 否则返回false |
| some | arr.some(item => item >= 3) | some数组成员只要任何一方满足条件就为真,否则为假//////类似或操作 |
| filter | arr.filter(ab => ab >= 3) | 返回满足条件的元素的新数组 |
| find | arr.find(ab => ab >= 5) | 返回满足条件的第一个元素 |
| findIndex | arr.findIndex(ab => ab >= 5) | 返回满足条件的第一个元素的索引 |
| reduce | arr.reduce((acc, cur, index, arr) => acc + cur); | 归并操作 有四个值,第一个值是acc累加器,第二个值是当前的值,第三个是当前值对应的索引,第四个值是当前要处理的数组 |
以上代码中可能示例带了arr数组看不明白,可以直接看下代码跟截图
代码示例:
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>数组常用api迭代遍历</title></head><body></body><script>// 迭代方法, 遍历元素// 1. forEach,map///foreach不带返回值,map带返回值// 2. every, some// 3. filter, find, findIndex// 4. reduce//append, 追加元素//classlist.add是添加css样式名/////////////////////////////////////////////////forEach查询/////////////////// forEach使用方法 forEach(function(item, index, arr){...})没个元素单独的逐个回调处理// forEach使用方法 forEach(function(数组的值, 数组的索引(或者键), 数组){...})没个元素单独的逐个回调处理// item为值,index为索引,arr为数组let arr = [2, 3, 4, 5, 6, 7, 8, 9, 10];// 其中,item 也就是数组的值,是必须的,其他都是可选的let arrs = arr.forEach(function (item, index, arr) {console.log(item, index, arr);// 在当前这个函数中,把item值追加到body中document.body.append(item);});// console.log(arrs); //测试验证 无返回值///////////////////////////////////////////map与foreach是一样的,只不过有返回值/////// 比如我想为当前元素的值*2返回示例arrs = arr.map(function (item) {return item * 2;});// 返回值比22行的arr全部都*2了console.log(arrs);////////////////////////////////////every, some只有俩值,true,false//////////// every数组成员全部满足条件则返回true 否则返回false//console.log(arr.every(function (item) {return item >= 0;}));console.log(arr.every(item => item >= 3));///some数组成员只要任何一方满足条件就为真,否则为假//////类似或操作console.log(arr.some(function (ab) {return addEventListener >= 0;}));console.log(arr.some(ab => ab <= 5));// filter 返回满足条件的元素的新数组console.log(arr.filter(ab => ab >= 3));console.log(arr.filter(ab => ab <= 5));//find 返回满足条件的第一个元素console.log(arr.find(ab => ab >= 5));//findIndex 返回满足条件的第一个元素的索引console.log(arr.findIndex(ab => ab >= 5));//reduce 归并操作 有四个值,第一个值是acc累加器,第二个值是当前的值,第三个是当前值对应的索引,第四个值是当前要处理的数组// res = arr.reduce(function(acc, cur, index, arr) {// console.log("acc=", acc, "cur=", cur, "index=", index, "arr=", arr);// // 最终的结果用acc累加器返回// return acc + cur;// }//起始值);//支持起始值 例如从1开始,或者从2开始res = arr.reduce(function (acc, cur, index, arr) {console.log("acc=", acc, "cur=", cur, "index=", index, "arr=", arr);// 最终的结果用acc累加器返回return acc + cur;});console.log(res);res = arr.reduce((acc, cur, index, arr) => acc + cur);console.log(res);</script></html>
示例截图:
使用到的参数:sort 数组内的数字排序join 拼接,对数组转为字符串拼接slice 数组的子元素取值splce 支持任何位置删除 新增,替换操作
扩展学习 数据比较多,是一个数组,照样可以插入
使用splce +…
例如arr.splice(1, 0, ...art)
代码示例:
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body></body><script>//sort 数组内的数字排序let arr = [1, 10, 20, 6];// 默认是安装unf啥的浏览器编码排序的console.log(arr.sort());// 要想把数字进行正确排序,该怎么排序呢?增加一个函数 如下console.log(arr.sort(function (a, b) {// 升序排列return a - b;}));console.log(arr.sort(function (a, b) {//降序排列return b - a;}));//join 拼接,对数组转为字符串拼接console.log(arr.join("-"));console.log(arr.join("."));arr = ["red", "blue", "green"];// 默认为逗号拼接console.log(arr.join());//slice 数组的子元素取值arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];console.log(arr.slice(2, 4));// 省略后取值是直接从第3个,因为索引是0,所以后面全要console.log(arr.slice(2));// 支持负数提取console.log(arr.slice(-6, -3));//splce 支持任何位置删除 新增,替换操作console.log(arr);// 前面是索引,后面是删除数,就是从索引1的位置删除2个console.log(arr.splice(1, 2));console.log(arr);//更新操作 删除之后》增加新值console.log(arr.splice(1, 2, "666", "888"));console.log(arr);//插入操作 插入操作即不删除,只新增,所以删除的值为0console.log(arr.splice(1, 0, "niu", "b"));console.log(arr);// 扩展学习 数据比较多,是一个数组,照样可以插入let art = ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"];console.log(arr.splice(1, 0, ...art));console.log(arr);</script></html>
示例截图:
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号