数组的扩展
ES6对数组进行了很多的扩展,具体如下
扩展运算符
扩展运算符是三个点(…),将一个数组转为用逗号分隔的参数序列,通常用在函数参数中。
假如我们需要一个求和函数,并且支持传入任意数量的值。
function sum(...params) {
let sum = arr.reduce(function (prev, cur) {
return prev + cur;
});
return sum;
}
let arr = [1, 2, 3, 4, 5];
console.log(sum(arr)); //输出 15
Array.from()
Array.from()方法从一个类似数组或可迭代对象创建一个新的浅拷贝的数组实例,通常有以下四种实用场景。
//一、克隆一个数组
let num = [1, 2, 3];
let newNum = Array.from(num);
console.log(newNum, num === newNum); //[1, 2, 3] false
//二、使用指定值,初始化一个数组
//给定长度为10,默认值是数组2和对象{key:1}
let length = 4;
let defaultValue = 2;
let defaultObj = { key: 1 };
let arrValue = Array.from({ length }, (item,index) => defaultValue);
let arrObj = Array.from({ length }, (item,index) => defaultObj);
console.log(arrValue); // [2, 2, 2, 2]
console.log(JSON.stringify(arrObj)); //[{"key":1},{"key":1},{"key":1},{"key":1}]
//三、生成值范围数组
function range(end) {
return Array.from({ length: end }, (item, index) => index);
}
let arr = range(4);
console.log(arr); // [0, 1, 2, 3]
//四、数组去重,结合set使用
let arr = [1, 1, 2, 3, 3];
let set = new Set(arr);
console.log(Array.from(set));
创建数组
如何创建一个数组,有下面几种常用方式
//一、数组字面量
const arr1 = [];
//二、构造函数
const arr2 = Array(3); //[null,null,null]
const arr3 = Array("3"); //["3"]
//这时想要用构造函数创建一个数字为7的数组,发现上面方式是无法满足的,而ES6提供了Array.of()能满足我们的需求
const arr3 = Array.of(7); //[7]
数组查找
find()方法返回数组中满足提供的测试函数的第一个元素的值,若没有找到对应元素返回undefined
findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引,若没有找到对应元素则返回-1。
假如我们想要在一个成绩数组中,找到达到及格分数的最低分。
const score = [34, 23, 66, 12, 90, 88, 77, 40];
const passMin = score.find((value) => value > 60);
console.log(passMin); //66
const pass = score.findIndex((value) => value > 60);
console.log(pass); //2
数组遍历
ES6新增 for…of 数组遍历方式
const score = [34, 23, 66, 12,];
for (let value of score) {
console.log(value); // 34, 23, 66, 12
}
函数的扩展
ES6对函数进行了很多的扩展,具体如下
函数参数设置默认值
ES6允许为函数的参数设置默认值,即可以直接写在参数定义的后面。
//参数b设置了默认值为2,在方法调用的时候并没有传值,所以b直接使用默认值
function sum(a, b = 2) {
return a + b;
}
console.log(sum(1)); //3
Rest参数
ES6引入reset参数,形式为…变量名,可以用来获取传递给函数的多余参数。
function sum(a, ...values) {
console.log(a, values); //1 [2, 3, 4, 5]
}
sum(1, 2, 3, 4, 5);
name和length属性
name属性返回函数名,length属性返回没有指定默认值的参数个数。
function sum(a, b, c, d = 1) {
console.log(a, values); //1 [2, 3, 4, 5]
}
console.log(sum.name); //sum
console.log(sum.length); //3
箭头函数
ES6允许使用箭头(=>)的方式定义函数,有下面几种箭头函数实现形式。
想要实现一个加法函数,ES5的形式如下
function sum(a, b) {
return a + b;
}
而如果使用箭头函数实现的话,则如下
sumArrow = (a, b) => {
return a + b;
};
上面是箭头函数的基本变现形式,不同的场景还有不同的实现形式。
//对于上面的sumArrow函
//一、如果只有一个参数,可以省略括号
sumArrow = a => {
return a;
};
二、如果返回值是表达式,可以省略return和{}
sumArrow = a => a;
三、如果返回值是字面量对象,一定要用小括号包起来
sumArrow = () => ({ a: 1, b: 2 });
箭头函数与普通函数除了实现方式不同外,还有个不同的点就是对this的处理方式。
//普通函数
let math = {
name: "mathName",
sum: function (a, b) {
console.log(this.name); //math
return a + b;
},
};
math.sum();
//箭头函数
globalThis.name = "globalName";
let math = {
name: "mathName",
sum: (a, b) => {
console.log(this.name); //globalName
return a + b;
},
};
math.sum();
从上面示例可以看到,箭头函数和普通函数最终打印的this.name不一致。对于普通函数,this指向的是调用sum方法的math对象,所以this.name打印的是“mathName”。而对于箭头函数,this指向的是定义sum方法的全局对象,所以this.name打印的是“globalName”。
在后续的开发过程中,我们将会经常使用到箭头函数,在使用的过程中,我们需要有以下几点注意
- 箭头函数中this指向定义时所在的对象,而不是调用时所在的对象
- 不可以当作构造函数
- 不可以使用yield命令,不能作用generator函数
解构赋值
解构赋值是一种表达式,可以将属性和值从对象和数组中取出,赋值给其他变量。
如何使用
对象解构赋值
假如我们拿到一个对象,需要获取指定的属性值。则解构赋值让我们无需通过调用属性的方式赋值,而是通过指定一个与对象结构相同模板的方式,获取想要的属性值。
const people = {
name: "ES6",
age: 27,
sex: "male",
};
//如果通过调用属性赋值,则需要这么做
let name = people.name;
let age = people.age;
let sex = people.sex;
console.log(name, age, sex); //ES6 27 male
//而使用解构赋值的方式,代码会更加的清晰简单
const { name, age } = People;
console.log(name, age); //ES6 27 male
除了上面这种基本用法,还有其他使用方式
const people = {
name: "ES6",
age: 27,
sex: "male",
};
// 一、属性顺序不需保持一致,名称相同即可
const { age, name, sex } = people;
console.log(name, age, sex); //ES6 27 male
//二、取值时,重新定义变量名
const { age: newAge, name: newName, sex: newSex } = people;
console.log(name, age, sex); //Uncaught ReferenceError: age is not defined
console.log(newName, newAge, newSex); //ES6 27 male
//三、赋值过程中设置默认值
const { nickName = "昵称", age } = people;
console.log(nickName, age); //昵称 27
//四、reset运算符。只获取想要的属性,其他属性都放在新的变量里。
const { name, ...peopleParams } = people;
console.log(name, peopleParams); //ES6 {age: 27, sex: "male"}
//五、嵌套对象取值
const people = {
name: "ES6",
address: {
province: "江苏",
},
};
const { address: { province }} = people;
console.log(province); //江苏
数组解构赋值
假如我们拿到一个数组,需要获取指定的元素值。
const [a, b, c] = [1, 2, 3];
console.log(a, b, c); //1 2 3
除了上面这种基本用法,还有其他使用方式
//一、待解构的除了是数组,还可以是任意可遍历的对象
const [a, b, c] = new Set([1, 2, 3]);
console.log(a, b, c); //1 2 3
//二、被赋值的变量还可以是对象的属性,不局限于单纯的变量
const num = {};
[num.a, num.b, num.c] = [1, 2, 3];
console.log(num); //{a: 1, b: 2, c: 3}
//三、解构赋值在循环体中的应用
const num = {
a: 10,
b: 20,
c: 30,
};
for (const [key, value] of Object.entries(num)) {
console.log(key, value); //a 10 b 20 c 30
}
//四、跳过赋值元素
const [a, , c] = [1, 2, 3]; //存在空位的数组叫稀疏数组
console.log(a, c); //1 3
//五、rest 参数
const [a,...other] = [1, 2, 3];
console.log(a, other); //1 [2, 3]
//六、赋值过程中设置默认值
const [a, , , d = 10] = [1, 2, 3];
console.log(d); //10
字符串解构赋值
字符串解构赋值可以当成数组解构赋值
const [a, b, c, d] = "ECMAScript2015";
console.log(a, b, c, d); //E C M A
对象的扩展
ES6对对象进行了很多的扩展,具体如下
属性的简洁表示法
从ES6开始,如果对象的属性名和属性值相同,则有简写的方式。
let province = "江苏";
const address = {
province, //等同于 province: province
city: "南京",
};
属性名表达式
从ES6开始,可以使用变量或表达式定义对象的属性。
let key = "province";
const address = {
[key]: "省份",
city: "南京",
};
console.log(address); //{province: "省份", city: "南京"}
Object.is()
判断两个值是否是同一个值。在Object.is()之前,有“”和“=”两种方式判断值是否相等,但这两个方式都有一定缺陷,如下
//== 在判断相等前会对不是同一类型的变量进行强制转换,最终导致“”与false相等
console.log("" == false); //true
//=== 会将-0与+0视为相等,而将Number.NaN与NaN视为不相等
console.log(-0 === +0); //true
console.log(Number.NaN === NaN); //false
所以,需要一种运算,在所有场景下,只要两个值是一样的,那么就应该相等,在实际项目开发过程中,推荐使用Object.is()来判断值相等。
console.log(Object.is(-0, +0)); //false
console.log(Object.is(Number.NaN, NaN)); //true
let a = { value: 1 };
let b = { value: 1 };
console.log(Object.is(a, b)); //false 对象都是同一个引用才相等
Object.assign()
用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,它将返回目标对象。
语法:
Object.assign(target, …sources)
参数说明:
target:目标对象
sources:源对象
返回值:合并后的目标对象
const target = { a: 1,};
const source = { b: "B", c: "C" };
const assignObj = Object.assign(target, source);
console.log(assignObj); //{a: 1, b: "B", c: "C"}
//其他应用
//一、如果目标对象与源对象属性具有相同值,则源对象属性值会覆盖目标对象属性值
const target = { a: 1,b: 2};
const source = { b: "B", c: "C" };
const assignObj = Object.assign(target, source);
console.log(assignObj); //{a: 1, b: "B", c: "C"} //目标对象的b属性值被覆盖
//二、源对象可以有多个值
const target = { a: 1 };
const source1 = { b: "B", c: "C" };
const source2 = { d: "D", e: "E" };
const assignObj = Object.assign(target, source1, source2);
console.log(assignObj); //{a: 1, b: "B", c: "C", d: "D", e: "E"}
对象遍历
假如我们想要循环遍历一个对象的键与值,则可以使用下面几种方式进行遍历
const score = {
name: "mango",
age: "25",
score: 80,
};
//for...in
for (let key in score) {
console.log(key, score[key]); // 分别输出:name mango 、 age 25 、score 80
}
//Object.keys()用来获取所有key组成的数组
Object.keys(scoreObj).forEach(key => {
console.log(key, scoreObj[key]) //分别输出:name mango 、 age 25 、score 80
})
//Object.getOwnPropertyNames()用来获取所有key组成的数组
Object.getOwnPropertyNames(scoreObj).forEach(key => {
console.log(key, scoreObj[key]) //分别输出:name mango 、 age 25 、score 80
})
//Reflect.ownKeys()用来获取所有key组成的数组
Reflect.ownKeys(scoreObj).forEach(key => {
console.log(key, scoreObj[key]) //分别输出:name mango 、 age 25 、score 80
})
Class
JavaScript是一种基于对象的语言,我们遇到的所有东西几乎都是对象,但ES6之前是没有class的,而在ES6版本中正式引入了class,让JavaScript成为了一种真正的面向对象语言,我们可以像下面这样在JavaScript中进行面向对象编程。
//通过class关键字定义类
class People{
//类的构造函数
constructor(name, age) {
this.name = name;
this.age = age;
}
//实例方法
getName() {
return this.name;
}
//静态方法
static say() {
console.log("Hello ES6");
}
}
//继承
class Student extends People {
constructor(name, age) {
super(name, age);
}
}
//对象创建与调用
let student = new Student("mango", "27");
student.getName();
Student.say();
通过上面的代码,我们具体说明下JavaScript中进行面向对象编程。
类的声明
通过class关键字声明类,支持构造函数construct做对象初始化。
class People{
constructor() {
//初始化
}
}
属性
Class对象中有两种对象属性,分别是实例属性和静态属性。实例属性必须定义在类的方法里,而静态属性必须定义在类的外面。
class People{
constructor() {
//定义实例属性
this.name = "";
this.age = 0;
}
}
People.desc="类描述"; //定义的静态属性
//访问
People people=new People();
console.log(people.name);
console.log(People.name);
类中定义的属性,默认都是可读可写的,但是如果这时候我们想指定属性不可被修改该如何实现呢?那么便要用到set和get了,set和get可以定义一个属性,但是如果只有get而没有set,则属性不可以进行修改。
class People {
get sex() {
return "男";
}
}
let people = new People();
console.log(people.sex);
people.sex="女" //Uncaught TypeError: Cannot set property sex of #<People> which has only a getter
方法
Class对象中有三种方法,分别是构造方法、实例方法还有静态方法。
class People {
//构造方法
constructor(name, age) {
this.nameA = name;
this.age = age;
}
//实例方法
getName() {
return this.nameA;
}
//静态方法
static say() {
console.log("Hello " + People.desc);
}
}
People.desc = "类描述";
let people = new People("mango", "27");
let name = people.getName();
console.log(name); //mango
People.say(); //Hello 类描述
继承
继承是面向对象语言很重要的一大特征,ES6新加入了extends和super关键字来实现继承。
class People {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
//继承
class Student extends People {
constructor(name, age) {
super(name, age);
}
}
//Student类继承了People类,student对象中super调用了父类的构造函数,并传递了name参数,因为继承的特性,student也拥有了父类的getName()方法
let student = new Student("ES6");
console.log(student.getName());
通过以上对class的学习,我们得知道其实class并不是新引入的数据类型,其实class只是一种语法糖,它的实质完全可以看作构造函数的另一种写法。
class People {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
console.log(typeof People); //function
console.log(People.prototype); //{constructor: ƒ, getName: ƒ}
更多文章内容,请关注公众号【方塘HCI】
参考资料
【1】ECMAScript简介
【2】MDN web docs
【3】ECMAScript2015~2020语法全解析
【4】阮一峰 ECMAScript6(ES6)标准入门教程 第三版
【5】JavaScript深入之词法作用域和动态作用域
【6】Array.from() 五个超好用的用途