04-ES6
let和const命令
ES6中新增了let命令,用来声明变量,用法类似与var
let和var的不同:
1、不存在变量提升
console.log(a); //Cannot access 'a' before initialization
let a = 100;
2、同一个作用域不能重复定义同一个名称
var c = 20;
let c = 30;
console.log(c);//Identifier 'c' has already been declared
3、有严格的作用域
function fn() {
var a = "a";
if (true) {
var a = "b";
}
console.log(a);//b
}
fn();
function fn() {
var a = "a";
//ES6中引入了块级作用域,var a 和let a不在一个作用域中
if (true) {
let a = "b";
}
console.log(a);//a
}
fn();
4、块级作用域的重要性
//for循环的i会提升为全局变量
for (var i = 0; i < 5; i++) {}
console.log(i); //5
for (let i = 0; i < 5; i++) {} //i的作用域范围只能在for循环的过程中
console.log(i); //i is not defined
const的使用
const 声明一个只读的常量。一旦声明,常量的值就不能改变。
保持let的不存在变量提升,同一个作用域内不能重复定义同一个名称,有着严格的作用域;
常量obj储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把obj指向另一个地址, 但对象本身是可变的,所以依然可以为其添加新属性
const x = 10;//如果声明的是常量,则不能修改
x = 100; //Assignment to constant variable.
console.log(x);
const y;//Missing initializer in const declaration const声明变量时,需要立即初始化
console.log(y);
//const声明引用类型,可以对值进行修改
const arr = [1,2,3,4];
arr[0] = 100;
console.log(arr);
const obj = {};
obj.id = 1;
console.log(obj);
解构
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)
解构是ES6的新特性, 比ES5代码简洁,清晰,减少代码量
数组解构
左边模式匹配,定义变量,右边是定义的数组或者对象
let [a, b, c] = [1, 2, 3];
let [a2, b2, c2, d2] = ["red", "blue", "green"];
let [x, [y1, y2], z] = [1, [2, 3], 4];
let arr = [100, 200];
var x = arr[0] || 1;
//默认值写法
let [x = 1] = arr; //arr[0]存在在解构成x,如果不存在,则赋值为1
//当值是undefined的时候,才会触发默认值机制
let [x = 1] = [undefined, undefined];
console.log(x);//1
var [y = 1, z] = [100, null];
console.log(y,z);//100 null//默认值如果是表达式【函数】,值在用到的时候,才会执行,惰性求值
var [y = 1, z = f()] = [100, 200];
console.log(y,z);//100,200
对象解构
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对 象的属性没有次序,变量必须与属性同名,才能取到正确的值
对象解构的写法:let { id, name } = { id: 1, name: "jack" };
如果没有对应的属性名,则返回undefined
//id:_id 别名
let { id:_id, name:_name } = { id: 1, name: "jack" };
//i为undefined,则返回1
let { id: _id, name: _name, i = 1 } = { id: 1, name: "jack" };
let { x = 10 } = {};
console.log(x);//10
let { x:a = 10 } = {};
console.log(x);//x is not defined,x是匹配模式,真正的变量是a
//解构用于方法入参
const fn = ({ count, num }) => num + count;
var obj = { count: 100, num: 99 };
let sumcount = fn(obj);
console.log(sumcount);//累加&解构
let obj = { id: 1, msg: "error", arr: [5, 6, 7, 8] };
const sum = ({ id, arr }) =>
arr.reduce((total, current) => total + current, id);
console.log(sum(obj));
如果要将一个已经声明的变量用于解构赋值,必须非常小心
//报错的原因:因为 JavaScript 引擎会将{x}理解成一个代码块,从而发生语法错误。只有不将大括号写在 行首,避免 JavaScript 将其解释为代码块,才能解决这个问题
let x;
{x} = {x:1};//编译错误
console.log(x);正确写法:
let x;
({x} = {x: 1});
将整个解构赋值语句,放在一个圆括号里面,就可以正确执行
函数参数解构
//数组参数解构
function sum([x = 0, y = 0]=[]) {
return x + y;
}函数参数
[x = 0, y = 0] = []
表示:
- 参数是一个数组,默认值为空数组
[]
- 如果传入的数组没有第一个元素,x默认为0
- 如果传入的数组没有第二个元素,y默认为0
sum();//0+0=0
sum([1]);//1+0=1;
sum([1,2]);
//对象参数解构
function fn2({ x = 1, y = 2 } = {}) {
console.log(x + y);
}
var obj = { x: 10, y: 20 };
fn2(obj);
扩展
字符串扩展
includes(): 返回布尔值,表示是否找到了参数字符串。
startsWith(): 返回布尔值,表示参数字符串是否在原字符串的头部。
endsWith(): 返回布尔值,表示参数字符串是否在原字符串的尾部
repeat(): 返回一个新字符串,表示将原字符串重复n次。
padStart(): 用于头部补全
padEnd(): 用于尾部补全
数值扩展
Number.parseInt("100");
Number.parseFloat("100.01");
//用来判断数字是否是一个整数,返回bool
Number.isInteger(100.01);
Math.ceil() 返回大于或等于一个给定数字的最小整数,ES5
Math.floor() 返回小于或等于一个给定数字的最大整数,ES5
Math.round() 返回一个数字四舍五入后最接近的整数,ES5
//ES6
Math.trunc() 用于去除一个数的小数部分,返回整数部分
Math.sign() 方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数 值
//运算符:**,指数运算符
2**3 = 8
数组扩展
//用于将一组值,转换为数组
var v = Array.of(); //[]
var v = Array.of(1, 2, 3); //[1,2,3]
//ES6
var v = Array.of(); //[]
var v = Array.of(1, 2, 3); //[1,2,3]
//创建指定长度的数组,ES5
var v = new Array(3);//参数是数字,则是数组长度
console.log(v.length);
//创建一个数组,数组内的元素是字符3,ES5
var v1 = new Array("3");
console.log(v1);
//find()用于找出第一个符合条件的数组成员
//find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组
var arr = [3, 4, 7, 9];
var a = arr.find((value, index, arr) => {
return value > 5;
});
//findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,
// 如果所有成员都不符合条件,则返回-1
var arr = [3, 4, 7, 9];
var a = arr.find(function (item, index, arr) {
//返回第一个符合条件的数组成员的位置
return item > 5;
});
//用于将一个固定值替换数组的元素
var arr = ["blue", "Orange", "red", "green"];
arr.fill("abc", 2, 4); //["blue", "Orange", "abc", "abc"]
//方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的 includes 方法类似
[1, 2, 3].includes(2); // true
[(1, 2, 3)].includes(4); // false
var arr = ["blue", "Orange", "red", "green"];
arr.includes("blue"); //true
//用于将嵌套的数组“拉平”,变成一维的数组
var v = [1, 2, [3, 4], 5, [6, 7, 8]].flat(); //[1, 2, 3, 4, 5, 6, 7, 8]
对象扩展
//对象属性2种定义方式
var o = {};
o.name = "xp";
let v = "age";
o[v] = 20; //这种定义v指的是变量
console.log(o);
//对象属性定义方式
let age = "age1";
o = {
[age]: 22,
};
console.log(o);
//链判断运算符
let firstName = message.body.user.firstName; //可以这样写,但是不规范,没有对数据的存在进行校验
firstName =
(message &&
message.body &&
message.body.user &&
message.body.user.firstName) || 'default';
//这样的层层判断非常麻烦,引入了“链判断运算符” ?.,简化上面的写法
firstName = message?.body?.user?.firstName || "default";
//空值合并运算符
let info = "aaaa";
let b = info ?? "bbbb";//如果info为null或者undefined,则取很后面的值
函数扩展
函数默认参数
/****************ES5*************** */function fun(x, y) {y = y || 20;console.log(y);}fun(123);/****************ES6********************* */function fun(x, y = 10) {console.log(y);}fun(234);
rest参数
/****************arguments******************* *///方法的参数是动态的,使用arguments参数集合,类似与数组function fn() {console.log(arguments); //类似数组}fn(1, 2, 3);////rest动态参数function fn(...values) {console.log(values);let filterNum = values.filter((value, index, values) => {return value > 20;});console.log(filterNum); //[ 222, 333 ]}fn(11, 222, 333, 13); //结果是一个标准的数组//报错function fun(a,...b,c){}fun(1,2,3,4)
箭头函数
var f = (n1, n2) => n1 + n2;//定义一个方法,返回一个对象var f = function (n1, n2) {return { n1, n2 };};//箭头函数写法---写法错误var f = (n1, n2) => {n1, n2;};//正确写法var f = (n1, n2) => {return { n1, n2 };};var a = f(1, 2);console.log(a); //{ n1: 1, n2: 2 }//正确写法,返回对象,需要使用()把对象包起来var f = (n1, n2) => ({ n1, n2 });var a = f(1, 2);console.log(a); //{ n1: 1, n2: 2 }///方法入参解构let f = ({ x = 0, y = 0 } = {}) => {return [x, y];};f({ x: 3, y: 3 });
箭头函数的坑
1、箭头函数不能当成构造函数,也就是不能使用new关键字
//箭头函数不能当成构造函数,也就是不能使用new关键字var p_fn = (name, age) => {this.name = name;this.age = age;};var pfn = new p_fn("aaa", 30); //p_fn is not a constructor
2、箭头函数没有原型对象
3、不可以使用arguments对象 该对象在函数体内不存在 替代 rest
var f = () => {console.log(arguments[2]);};f(); //Uncaught ReferenceError: arguments is not defined//使用rest代替var f = (...rest) => {console.log(rest);};f(1, 2, 3, 4);function fn3() {console.log(arguments[1]);}fn3(1, 2, 3, 4); //2
4、this指向 由于箭头函数不绑定this,它会捕获其所在上下文的this的值,作为自己的this值
let x = "x";let y = "y";let obj = {x: "x2",y: "y2",getX: function () {console.log(this.x);},getY: () => {console.log(this); //this指向Windowconsole.log(this.y);},};obj.getX(); //this指向objobj.getY(); //undefined//箭头函数中this指向://箭头函数中没有this,但是箭头函数会捕获所在(定义的位置)上下文的this的值。//这里的getY在obj对象中,obj对象定义在Window里。
尽量不要在对象的方法里写箭头函数【不确定就在箭头函数中打印this】
扩展运算符
扩展运算符是三个点...
它好比rest参数的逆运算。
扩展运算符将一个数组转为用逗号分隔的参数序列。
该运算符主要用于函数调用。
//合并两个数组let arr2 = [1, 2, 3];let arr3 = [4, 5];let arr4 = [...arr2, ...arr3];console.log(arr4);function sum(x, y) {return x + y;}const item = [1, 2];sum(...item);/***************************************************///第一种写法var a = [];function f(x, y) {a.push(...y);}f(a, [1, 2, 3, 4]);console.log(a); //[ 1, 2, 3, 4 ]//第二种写法var a = [];function f(x, ...y) {//这的...y是可变参数,会将参数变成数组,也就是[[1, 2, 3, 4]]a.push(...y);//这的...y是扩展运算符,会将[[1, 2, 3, 4]]分解成[ 1, 2, 3, 4 ]}f(a, [1, 2, 3, 4]);console.log(a); //[[ 1, 2, 3, 4 ]]/**********************扩展************************ */let array = [{ id: 1, name: "a" },{ id: 3, name: "a" },{ id: 10, name: "a" },{ id: 4, name: "a" },{ id: 27, name: "a" },{ id: 50, name: "a50" },];//求id最大的对象?let maxId = Math.max(...array.map((v) => v.id));console.log(maxId);
扩展运算符可以和解构结合起来使用
let [first, ...rest] = [1, 2, 3, 4, 5];console.log(first);//1console.log(rest);//[ 2, 3, 4, 5 ]
扩展运算符在对象中的使用
let o1 = { id: 1 };let o2 = { name: "o2" };let o3 = { ...o1, ...o2 };console.log(o3);//{ id: 1, name: 'o2' }