JS入门
js介绍
javascript是一个基于浏览器的客户端编程语言,是浏览器默认的脚本语言
是一门基于面向对象的弱类型语言
node.js是基于 javascript的 服务端技术
使用 js
是嵌入在浏览器的脚本语言,需要在 html 网页中通过
<script>
标签引入
js 的引入方式
- 1.在标签上,通过触发事件 执行 一段 js 脚本
<div id = "app" onclick = alert('这是一个div')"></div>
- 2.通过script标签, 来定义 js 脚本
<script>
// 使用 js 获取 div
var tag = document.querySelector("#app");
alert(tag);
// alert 会阻塞程序的执行
alert("javascript入门!");
</script>
- 3.通过外链的方式,引入js文件
<script src = "js/xxx.js"></script>
如果一个script标签,提供了src属性,那么该标签体中不能写任何的js代码
var变量的用法
var 定义的变量 默认情况下是 全局变量,会自动成为 window的属性 , 但不能通过 delete 删除
window 对象中的 变量 能够通过 delete 被删除
var 修饰的变量 可以被重复声明
var 存在 变量提升的 现象
一个网页中的所有全局变量,都会默认自动成为 window 的属性
let变量的用法
let 修饰的变量是一个局部变量
let 不存在 变量提升的现象
let 声明的变量不能被多次重复定义
let 会存在 暂时性死区 现象
const变量的用法
与java中的fianl类似,定义的变量不可修改
其他用法与let相似
js中常见的运算符
1.算术运算符 + , - ,* , / , % ,x**y
let a = "-10.1" ;
let b = "abc" ;
// 如果 除以 0 ,则返回 Infinity
// 如果 算术运算符,有任意一个不是数字,则返回 NaN
let c = a / b ;
console.log(c == -Infinity)
// 判断 一个结果是 无穷 ??
// isFinite 是否是有穷的
let x = !isFinite(c);
// 判断一个数字是否是 非数
// isNaN 会对内容进行转换,如果能转成数字,则返回 false, 反则返回 true
let y = isNaN("ABC"); // true
// Number.isNaN 结果只有是 NaN 才返回 true, 其他一律返回 false
let z = Number.isNaN("ABC"); // false
console.log(c, x, y, z);
2.关系运算符 > , >= , <,= , == , !=,=(等于),!(不等)
==
比较的值 是否相等,类型如果不一致,会自动转换成同一种数据类型进行比较
===
比较 类型 和 值 是否 相同
js 中 有两个特殊的 值
null : 代表 值为 空
undefined : 代表 值 未定义
typeof(null) // object 3 + null // 3
typeof(undefined) // undefined 3 + undefined // NaN
3.逻辑运算符 && , || , ! , & , |
4.位运算符 &, | , ~ , ^
5.位移运算符 << , >> , >>>
6.—元运算符
??
:给null或者undefined设置默认值
7.三元运算符 ? :
8.赋值运算符+=,-=, …|
##流程控制语句
if语句
- if条件 只强调结果 结果为真, 即为真, 否则为假
1.boolean 表达式 , true 代表真 , false代表假
2.数字,0 (+0,-0也为假)代表假,其他数字均为0
3.字符串,长度为0的字符串为 假, 其他均为真
4.null和undefined代表假
5.对象均为真
console.log():以日志的方式打印一个对象
console.dir():以对象的本身打印一个对象,可以看到对象的很多内容
6.数组(列表)均为真
各类型转数字
""
可以转数字为0
null
转成数字也为0
undefined
无法转成数字 undefined-num = NaN
true
转数字为1false
转数字为0
空数组转数字为0 空数组+空数组为长度为0的空字符串
空对象无法转成数字(赋值给变量的对象无法转成数字:下一行实例) (未赋值给变量的对象可以转为数字0,下下行实例)
let x = {} x-3; //NaN
{} - 3 // -3; 控制台输出为这种情况
各类型转字符串
空对象转字符串为[object Object]长度为15的字符串
空数组转字符串为长度为0的字符串
null+null=0 null转不了字符串
null+undefined为NaN
true + true = 2; true + false = 1; false + false = 0;
switch支持的类型
java中支持 byte、 short、 char 、int 、String 、 enum 以及前4种类型的包装类
js中无要求
js中常见的数据类型
Number
null 和 undefined
number 表示 数字 , 使用 Number 类表示
数字的构建方式
string 表示 字符串, 字符串可以用单引号 、 双引号 来表示
let a = 10; // 通过 字面量定义一个数字
let x = Number(10); // 创建一个对象
a == x; // true a === x; //true
lex y = new Number(10); // 创建一个object对象 new出来的类型全是Object类型
x == y; // true x === y; // false
x.typeof() // Number
y.typeof() // Object
查看类型使用
typeof()
toFixed(0~20) 保留 几位小数、并进行四舍五入,传入的范围是0~20
String
左对齐 padStart(n,sep) n:补后的长度 sep:默认是空格
右对齐 padEnd(n,sep) n:补后的长度 sep:默认是空格
spilt(“”,n) 第一个值为分割符 n为分割的次数
JS 逐个字符进行拆分,获取拆分后的前n个值
Java 逐个字符进行拆分,整个字符串最多被拆分n次
substr(index,length)
substring(startindex,endindex)
let x = "hello world";
x.substr(6,5); // world
x.substring(6,11); // world
x.substring(6,-1); // hello 为负值时截取它前面的内容
字符串字面量的表示形式
- 1.双引号
- 2.单引号
- 3.反逗号 (a) 支持多行字符串的定义 (b) 支持定义字符串模板
let name = "张三";
let tel = "15124346578";
let age = 18;
// 打印一句话:张三的手机号是15124346578
// 通过 ${}占位符, {}里面写的是 js 表达式
let x = `年龄${age},姓名为${name}的手机号是${tel.substring(0,3)****${tel.substring(7)}}`
Regexp 正则表达式
// 定义一个正则表达式,用来匹配手机号
let re = new RegExp("1[3-9]\\d{9}");
// 使用正则表达式的字面量 定义正则
let regex = /^1[3-9]\d{9}$/;
- 正则表达式的test方法
test验证正则表达式是否匹配某一指定的字符串(部分匹配)
RegExp.($1~$9) 获取指定分组的内容 需要分组
exec获取正则匹配的内容及其分组匹配的内容
- 正则的修饰符
exec 默认 只能获取 正则 第一次匹配的内容 如果想要获取 匹配的多个内容 , 需要在正则上,添加一个 g 模式
g 全局模式
let re = new RegExp("1[3-9]\\d{9}","g");
let regex = /\d+/ g;
i 忽略大小写
let re = new RegExp("^[a-z]+$","i");
let regex = /[a-z]+/ i;
m 多行模式
必须使用g模式
修饰符可以写多个
s 点模式
- 字符串中支持正则表达式的方法有,如果需要替换所有,正则需要使用g模式
split
let s = "abc 123 ttt";
// 按照空格拆分字符串
let array = s.split(/\s+/);
replace 默认只能替换一次匹配的内容
let x = s.replace(/\d+/g,"***");
match 获取匹配的内容, 如果在非 g 模式下, 返回一个和 exec方法相同的结果,如果是g模式,则返回一个匹配的内容组成的数组
search 获取正则第一次匹配内容的索引位置
Math 数学类
ceil 向上取整
floor 向下取整
round 四舍五入
max(…)支持传入多个值 最大值
min(…)支持传入多个值 最小值
random 返回 0 ~ 1 的随即小数, 包含 0 不包含 1 parseInt 转整数
Date 日期类
// 创建一个当前日期
let date = new Date();
// 获取距离 1970-1-1指定毫秒数的日期
let date = new Date(毫秒数);
// 获取指定的 年 月 日 的日期
let date = new Date(year,month,date)
// 获取时间戳
date.getTime();
// 获取年份
date.getFullYear();
// 获取距离1900年的年份
date.getYear();
// 获取月份 0 ~ 11 月
date.getMonth();
// 获取天
date.getDate();
// 获取小时 24进制的
date.getHours();
//获取分钟
date.getMinutes();
// 获取秒
date.getSeconds();
// 获取毫秒
date.getMillisecond();
// 获取星期 星期从零开始表示 零代表周日
date.getDay();
// 修改时间戳
date.setTime(1000);
Array 数组
创建一个数组的方式
new Array(); 创建一个空的数组,返回一个数组对象
new Array(n); 创建一个指定长度的数组
new Array(a,b,c...); 创建一个包含内容为 a,b,c...的数组对象
[a...] : 闯将一个 包含 a...的数组(字面量的)
数组常见的属性
- length: 获取数组的长度
数组常见的方法
- array[i]: 获取指定位置的元素:
- array[i] = object: 修改数组的元素
- includes(val): 判断数组中是否包含某个元素
- indexOf(value): 获取元素第一次出现的位置
- lastIndexOf(value): 获取元素最后一次出现的位置
数组添加的方法
- pash(): 向数组的尾部添加元素,支持批量添加元素
- unshift(): 向数组的头部添加元素,支持批量添加元素
- splice(index,0,newValue…): 向指定位置添加元素,支持批量添加元素
数组删除的方法
- pop(): 删除数组尾部的元素并返回删除的元素
- shift(): 删除数组的元素,并返回删除的元素
- splice(index,n): 从指定位置开始删除n个元素
数组的高级方法
- map( (val, index, arr)=> {…} ) : 映射
- filter((val, index, arr)=> {…}): 过滤
- reduce((result, val) => {}, defaultValue)
使用 reduce 进行去重
let a = array.reduce( (list, val)=> {
// 判断 list 中是否包含 val
if (!list.includes(val)) list.push(val);
return list ;
} , []);
- reduce((result, val)=> {})
- find((val, index, arr)=> {…}) : 查找满足条件的第一个元素,如果找不到,返回undefined
- findIndex((val, index, arr)=> {…}) : 查找满足条件的第一个元素索引,如果找不到,返回-1
- some((val, index, arr)=> {…}): 判断数组中是否有元素满足条件
- every((val, index, arr)=> {…}): 判断数组中是否都满足条件
数组遍历
- fori遍历
for(let i= 0 ; i < array.length; i++) {
console.log(array[i]);
}
- for … in (遍历的是数组中的索引,从0开始)
for(let index in array) {
console.log(array[index]);
}
- for …of 遍历 String也可以用
for(let val of array) {
console.log(val)
}
- forEach 遍历
array.forEach((val, index)=> {
console.log(val, index);
})
- 键遍历
for(let key of array.keys()) {
console.log(array[key])
}
- 值遍历 , for … of 默认使用的就是 值遍历
for(let val of array.values()) {
console.log(val);
}
console.log(array);
- 键值对遍历
for(let entry of array.entries()) {
console.log(entry[0] + "=" + entry[1])
}
数组的解构赋值
解构赋值: ES6中, 可以通过某种规则对 数组 或者 对象 进行数据快速提取的方式
let array = [12,null,6,32,6];
获取数组中的第一个元素和第二个元素 并分别赋值给 a,b
let [a,b] = array;
console.log([a,b]);
let [,a,b,,c,,,d] = array; //逗号表示占位
console.log(a,b,c,d);
let array2 = [[1,2,3],[4,5,6]];
let [[a],[b=1], c=1] = array2; // 没有值可以使用`=`赋默认值 只能对`undefiend`类型的值赋值,`null`无法赋值
console.log(a,b,c);
实现两个数的交换
let a = 10;
let b = 15;
// 实现 a 和 b 的交换
let temp = a ;
a = b;
b = temp;
a = a ^ b;
b = a ^ b;
a = a ^ b;
[a,b] = [b,a];
数组扩展运算符 …
let array = [1,2,3,4,5];
let array2 = [6,7,8];
// 合并两个数组
// let x = array.concat(array2);
let x = [...array,...array2];
// 将x数组中的前三个元素赋值给a,b,c剩余的元素赋值给d
// ...必须出现在 最后一个解构的位置,代表剩余的意思
let [a,b,c,...d] = x;
console.log(x);
console.log(a,b,c,d); // d 为数组
对象 Object
创建一个对象的方式有
- a) let obj = new Object();
- b) let obj = {}; // 创建一个空对象
在 JS 中, 对象的键是一个字符串, 可以省略引号, 如果键不符合标识符的命名规范, 那么必须使用 引号
let obj = {
"2name" : "张三",
"sex" : "男",
"age" : 18,
"say" : function(){
console.log(this.sex + "生正在说话"); // this不能省略
}
};
let name = obj["2name"]; // 不符合标识符命名规范的键可以通过[""]来获取
console.log(name);
// 根据对象中的键获取值
let age = obj.age;
// 修改年龄为20;
obj.age = 20;
// 删除 年龄 这个 属性
delete(obj.age); // delete obj.age
// console.log(obj.age);
console.log(obj);
obj.say();
对象的遍历方式
let obj = {
"name" : "张三",
"age" : 18,
"sex" : "男"
}
// 可以通过 for...in遍历
for(let key in obj){
console.log(key,obj[key]);
}
// 键遍历
// 获取对象中所有的键
let keys = Object.keys(obj);
for(let key of keys){
console.log(key,obj[key]);
}
// 值遍历
let values = Object.values(obj);
for(let val of values){
console.log(val);
}
// 键值对遍历
let entries = Object.entries(obj);
for(let [key,val] of entries){
console.log(key,val);
}
形似数组的对象
- a)对象中的键 是 数字,且应该从0开始
- b)对象中有一个length 属性,且值为 数字
转为数组的方法 Array.from(obj)
let obj = {
0:"AAA",
1:"BBB",
2:"CCC",
length:3
};
let a = Array.from(obj);
对象的属性表达式
var a = "1name1";
// a 变量的值 作为 obj 的属性名
// 如果一个对象中某一个属性名 是动态的, 通过属性名表达式实现
var obj = {
sex : "男",
age : 18,
[a] : "张三"
}
console.log(obj);
对象的简写
1.当对象的属性名字符串表示形式和 值的变量名 相同的时候, 可以省略 :值
2.当某一个属性 对应的值 是一个 函数的时候, 可以省略 :function
对象的解构
// 无法对null 和 undefined 进行 解构赋值
// 对象的解构
let obj = {
username : "zhangsan",
age : 20,
sex : "男",
student:{
name:lisi,
age : 5
},
score : [70,32,54],
birth : null
}
// 通过 对象解构赋值 快速获取学生的 姓名 和 第二个科目的成绩
let {student:{name:studentName} = {},score:[,en]} = obj;
// 获取name属性和sex
let {name,sex} = obj;
console.log(name,sex);
/**
* {name,sex}:
* name,sex是一个解构规则, 按照该名称从对象中找
* name,sex是一个变量 存储的是解构的值
*/
// let {name,sex,birth} = obj; // 简写 解构不到返回 undefined
let {username:name1,sex:sex="女",birth:birth="2000-01-01"} = obj; // 完整写法
// 使用对象的解构赋值,完成对数组的解构
let array = [12,34,12,5];
// 使用对象解构 获取 数组中的第一个 第二个元素
let {0:a,1:b} = array;
// 解构数组中的最后一个元素
let {[array.length - 1]:c,length:len}
// 通过 对象解构, 获取 数组中的push 方法
let {push:d} = array;
console.log(d);
console.log(name1,sex1,birth);
对象解构的注意点: ({0: a,1:b } = array) ;
对象展开运算符
// 如果一个对象 包含 Symbol.iterator, 那么这个对象
// 一定可以使用 for ... of 进行迭代 , 一定可以使用 ... 展开
...合并两个对象
let a ={name:"zhangsan",sex:"男"};
let b ={name:"lisi",birth:"1990-11-11"};
// 将 b 对象 合并到 a 对象中
Object.assign(a,b);
console.log(a);
// 将 a 和 b 合并, 返回一个 c 对象
let c = {...a,...b};
console.log(c);
// 对c对象进行解构赋值
let {name,...d} = c;
console.log(name,d);
let s = "abcdefg"; // 字符串是对象
// 使用 数组解构 字符串
let [,e,,f] = s;
console.log(e,f);
// 将字符串转成数组
let array = [...s];
// 将数组转成字符串
let x = array.join(",");
console.log(array,x);
// 使用 对象解构字符串
let {[s.length-2]:a} = 5;
console.log(a);
// 字符串的遍历 for ...of
for(let x of s){
console.of(x);
}
JSON序列化与反序列化
// JSON序列化: 将数组/对象 转成 字符串
// JSON.stringify(obj)
// JSON反序列化: 将字符串 转成 数组/对象
// JSON.parse(obj)
let user = [
{
name:"张三",
sex:"男"
},
{
name:"李四",
sex:"男"
}
]
let json = JSON.stringify(user);
console.log(json);
let newuser = JSON.parse(json);
console.log(newuser);****
函数
什么是函数?
是一种行为,代表能做什么
函数的作用
可以对代码进行 封装 ,以达到 代码复用 的效果
定义函数
JS中,使用function 关键字 来定义函数, 属于 Function类型
- a) function 函数名([参数列表]){函数体} // []代表可有可无
- b) let 函数名 = function([参数列表]){函数体}
- c) new Function([参数列表,函数体]);
- d) let sum = (a,b) => a + b;
函数的return问题
在JS中, 函数中如果没有return, 则代表 return undefined
如果 return了多个值, 则只能接收到 最后 一个 return 的值
内置变量arguments
在函数中,有一个内置的变量 arguments,是一个形似数组的对象
- a)可以获取到 调用传入的所有参数
- b)可以获取到 函数本身
arguments.callee()
// 计算 传入的所有数字之和
function sum(){
// 如何获取 传入的所有参数
// 在函数中, 有一个内置的变量 arguments , 是一个形似数组的容器
// console.log(arguments);
// 计算数组中所有元素的和
return Array.from(arguments).reduce((a,b) => a + b)
}
// sum(4,5,6);
// 1 + 2 + 3 + ... 100
function sum(){
if(n == 1) return 1;
return n + arguments.callee(n - 1);
// return n + sum(n - 1);
}
函数的加载顺序
虚拟机在加载时,会先将所有的函数加载到内存当中,所以可以在函数前调用函数
重名函数会被覆盖掉
函数参数的默认值
设置默认值的参数推荐放在后边
function max(b,a=0){
return a > b ? a : b ;
}
let m = max(1)
console.log(m);
不定项参数
…不定向参数 可以 替换 arguments 来获取 调用方法传入的参数
不定向参数 会变成一个数组 对象, 如果没有接收到参数, 则是一个 空数组
不定向参数 只能出现在 所有参数的 尾部, 最多只能出现一次
在函数的参数上使用解构赋值
function test({x=0,y=0} = {x:10,y:100}){
return `坐标(${x},${y})`;
}
undefined + [] = undefined
函数中的 this
关键字
在窗口中定义的函数, this指向 window
在对象中定义的函数, this指向当前对象
在构造函数中(通过new构造出来的), this 指向 当前对象
调用函数中的函数
function sum(x,y){
function xxx(){
console.log(this);
}
return xxx;
}
sum(3,5)();
构造函数
构造函数:通过new调用的函数
在函数中,可以通过new.target 来判断一个函数 是否是 构造函数
在普通函数中, new.target 值为 undefined
在构造函数中, new.target返回构造函数本身
- function Point(){
if(new.target === undefined){
//抛出一个错误
throw new Error("point是一个构造方法,请使用new来调用");
}
this.x = x;
this.y = y;
this.loc = function(){
return `坐标(${this.x},${this.y})`;
}
}
// let x = Point(); // 这是一个普通函数
let y = new Point(3,5); // 这是一个构造函数
console.log(y.x,y.y);
箭头函数
箭头函数: 是函数的一种简单写法,语法可以参考 Java lambda表达式
除构造函数
外的所有函数均可转为箭头函数
let sum = (a,b) => a + b;
箭头函数中的 this 默认指向 函数所在环境中的 this 对象
函数本身是一个对象, 所以它拥有方法
- call(obj,param…)
- apply(obj,[param…])
- bind(obj,param…): 不会调用函数,而是返回一个绑定后的函数
上述三个方法,都可以修改函数的 this 指向
sum2.call(null,3,4); // 如果传入的是null 或者 undefined,那么不会修改this 指向
sum2.apply(null,3,4);
sum2.bind(null,3,4)();
如果对一个对象的成员方法进行解构赋值, 那么方法中的this指向会发生改变
//求数组的最大值
//let max = array.reduce((a, b)=> a < b ? b: a)
// console.Log(max);
//let x = Math.max (array);错误写法
//let x = Math.max.appLy(null,array)
let x= Math.max(...array); I
console.log(×);
js prototype(原型)
可以对已知的类型进行扩展
在JS中,对象的键是一个字符串,可以省略 引号
如果一个对象 包含Symbol.iterator,那么这个对象
一定可以使用for … of进行迭代,一定可以使用…展开
let obj={
"2name":"张三",
sex:"男",
age:18,
say:function(){
console.log(this.name+"正在说话")
}
}
//获取对象中命名不规范的属性
let name=obj["2name"]
console.log(name)
//修改年龄20
obj.age=20
//删除 sex
delete obj.age
console.log(obj)
对象遍历
- for …in遍历
for(let key in obj){
console.log(key,obj[key])
}
- 键遍历
let keys=Object.keys(obj)
for(let key of keys){
console.log(key,obj[key])
}
- 值遍历
let values=Object.values(obj)
for(let val of values){
console.log(val)
}
- 键值对遍历
let entries=Object.entries(obj);
for(let [key,val] of entries){
console.log(key,val)
}
对象的简写
1.当对象中的属性名字符串表达式 和 值的变量名 相同的时候 可以省略 :值
2.当 某一个属性 对应的值 是一个函数的时候 ,可以省略 :function
var s="男";
var obj = {
s ,//省略
age : 18,
[a]:"张三",
say(){
console.log(this[a]+"在说话")
}
}
console.log(obj)
对象的解构
let obj={
name:"zhangsan",
age:20,
sex:"男"
};
//let{name,sex,birth}=obj;//简写
//解构失败附默认值 null 与undefined
let{name:name,sex:sex="女",birth:birth="1998-03-18"}= obj;
console.log(name,sex,birth)
let obj={
name:"zhangsan",
age:20,
sex:"男",
student:{
name:"兔子",
age:25,
sex:"女"
},
score:[70,24,36]
};
let {student:{name:studentname},score:[,sc]}=obj;
console.log(studentname,sc)
//
let g = {name:"李四",birth:"1990-12-15"};
let {name,...b}=g//剩余属性解构给b
console.log(name)
console.log(b)
数组解构
let array=[12,25,14,16];
let {0:a,1:b}=array;
//写法二 避免解析时错认代码块
<!-- let a,b;
({0:a,1:b}=array) -->
//解构数组最后一个元素
let{[array.length-1]:c,length:len}=array
console.log(a,b,c,len)
对象合并
let f = {name:"张三" ,sex:"男"};
let g = {name:"李四",birth:"1990-12-15"};
let j =Object.assign(f,g)
//let j ={...f,...g}
console.log(j)
数组转字符串
let s="abcdefg";
let array=[...s];//字符串转数组
let x=array.join(",")//数组转字符串
console.log(array,x)
//使用对象解构字符串
let{[s.length-2]:a} = s;
console.log(a)
for(let x of s){
console.log(x)
}
Json序列化
let j=[
{
name:"zhangsan",
age:18
},
{
name:"ruzu",
age:22
}
]
//转为文本 我们使用的
let json=JSON.stringify(j);
//转json
let obj=JSON.parse(json);
console.log(json)
console.log(obj)
模板
let name = "张三";
let tel = "15125846324";
let age = 18;
let x=`年龄${age},姓名${name}的手机号是${tel.substring(0,3)}****${tel.substring(7)}`
console.log(x)
形式数组的对象
//形似数组的对象
//a)对象中的键 是 数字,且应该从0开始
//b)对象有一个length属性 且值位数字
let ob={
3:"AAA",
1:"BBB",
2:"ccc",
length:3
};
let a=Array.from(ob)
console.log(ob,a)
解构赋值: ES6中,可以通过某种规则对数组 或者对象 进行数据快速提取的方式
-
concat方法合并数组
-
基本用法
let array=[12,4,58,71,2];
//获取数组中 的 第一个元素 和 第二个元素 并分别赋值a,b
let [,a,,b,c,d]=array;
console.log(a,b,c,d)
结果
//4 71 2 undefined
- 二维数组 解构赋值
let array2=[[12,4],[58,71,2]]
let [[f],[e]]=array2
console.log(f,e)
let array2=[[12,4],[58,71,2]]
let [[f],[e=1],c=1]=array2 // =默认赋值 如果为null默认值不生效 为undefined默认值生效
console.log(f,e,c)
利用解构交换两个数
let a=15;
let b=10;
[a,b]=[b,a]
console.log(a,b)
解构遍历
let array=[12,4,58,71,2];
for(let [key,value] of array.entries()){
console.log(key,value)
}
合并数组 —>解构
let a=[1,2,3]
let b=[1,2,3,9,8,7]
let c=[...a,...b]//合并
//将数组中前三个元素赋值给x,y,z,剩余赋值给h
let[x,y,z,...h]=b
console.log(x,y,z,h)