JavaScript的ES6~ES13的类和对象
一. ES6 定义类
1.1. 认识 class 定义类
我们会发现,按照前面的构造函数形式创建 类,不仅仅和编写普通的函数过于相似,而且代码并不容易理解。
在 ES6(ECMAScript2015)新的标准中使用了 class 关键字来直接定义类;
但是类本质上依然是前面所讲的构造函数、原型链的语法糖而已;
所以学好了前面的构造函数、原型链更有利于我们理解类的概念和继承关系;
那么,如何使用 class 来定义一个类呢?
- 可以使用两种方式来声明类:类声明和类表达式;
1 | // 类的声明 |
接着我们就可以使用 new 操作符调用类:
1 | const p1 = new Person(); |
我们来研究一下类的一些特性:
- 你会发现它和我们的构造函数的特性其实是一致的;
1 | const p = new Person(); |
1.2. 类的构造函数
如果我们希望在创建对象的时候给类传递一些参数,这个时候应该如何做呢?
每个类都可以有一个自己的构造函数(方法),这个方法的名称是固定的 constructor;
当我们通过 new 操作符,操作一个类的时候会调用这个类的构造函数 constructor;
每个类只能有一个构造函数,如果包含多个构造函数,那么会抛出异常;
1 | class Person { |
当我们通过 new 关键字操作类的时候,会调用这个 constructor 函数,并且执行如下操作:
1.在内存中创建一个新的对象(空对象);
2.这个对象内部的[[prototype]]属性会被赋值为该类的 prototype 属性;
3.构造函数内部的 this,会指向创建出来的新对象;
4.执行构造函数的内部代码(函数体代码);
5.如果构造函数没有返回非空对象,则返回创建出来的新对象;
1.3. 类的方法定义
1.3.1. 实例方法
在上面我们定义的属性都是直接放到了 this 上,也就意味着它是放到了创建出来的新对象中:
在前面我们说过对于实例的方法,我们是希望放到原型上的,这样可以被多个实例来共享;
这个时候我们可以直接在类中定义;
1 | class Person { |
我们也可以查看它们的属性描述符:
- 会发现它们的 enumerable 都是为 false 的;
1 | console.log(Object.getOwnPropertyDescriptors(Person.prototype)); |
1.3.2. 访问器方法
我们之前讲对象的属性描述符时有讲过对象可以添加 setter 和 getter 函数的,那么类也是可以的:
1 | class Person { |
但是和直接在对象中定义不同的是,类中的 setter 和 getter 方法是放到原型上的:
1 | const p = new Person("why"); |
1.3.3. 静态方法
静态方法通常用于定义直接使用类来执行的方法,不需要有类的实例,使用 static 关键字来定义:
1 | class Person { |
二. ES6 类的继承
2.1. extends 关键字
前面我们花了很大的篇幅讨论了在 ES5 中实现继承的方案,虽然最终实现了相对满意的继承机制,但是过程却依然是非常繁琐的。
在 ES6 中新增了使用 extends 关键字,可以方便的帮助我们实现继承:
1 | class Person {} |
我们知道继承可以让我们复用父类的一些代码结构,比如继承属性和方法:
1 | class Person { |
2.2. super 关键字
我们会发现在上面的代码中我使用了一个 super 关键字,这个 super 关键字有不同的使用方式:
注意:在子(派生)类的构造函数中使用 this 或者返回默认对象之前,必须先通过 super 调用父类的构造函数!
super 的使用位置有三个:子类的构造函数、实例方法、静态方法;
1 | // 调用 父对象/父类 的构造函数 |
下面的代码会报错,因为我们没有调用 super:
1 | class Person {} |
我们可以在子类的方法中调用父类的方法:
1 | class Person { |
2.3. 继承内置类
我们也可以让我们的类继承自内置类,比如 Array:
1 | class HYArray extends Array { |
调用 Array 的方法返回 Array 类型:
1 | class HYArray extends Array { |
三. ES 对象的增强
ES6 中对 对象字面量 进行了增强,称之为 Enhanced object literals(增强对象字面量)。
3.1. 属性的简写
在开发中,对象中的属性可能会经常来自变量,并且变量的名称和属性的名称是相同的,这个时候我们可以使用简写:
- 英文称之为 Property Shorthand
1 | const name = "why"; |
3.2. 方法的简写
另外一个对象增强的写法是针对方法的:
- 英文称之为 Method Shorthand
1 | // ES5的方法写法 |
3.3. 计算属性名
在 ES5 中,如果一个对象中属性的名称来自一个变量,或者需要其他的方法计算得到,那么我们需要这样来做:
1 | const name = "three"; |
在 ES6 中,我们可以直接在字面量中编写计算属性名:
英文称之为 Computed Property Names
1 | const name = "three"; |
四. 解构 Destructuring
ES6 中新增了一个从数组或对象中方便获取数据的方法,称之为解构 Destructuring。
4.1. 数组的解构
数组的解构就是从数组中获取我们需要的数组:
1 | const names = ["abc", "cba", "nba"]; |
数组的解构必须按照数据的顺序依次获取,如果我们只想获取第二个和第三个,那么可以有如下语法:
1 | const [, nameb, namec] = names; |
如果我们希望解构出来一个元素,其他元素继续放到另外一个数组中:
1 | const [namea, ...newNames] = names; |
如果我们解构的数据数量大于数组中原本的数据数量,那么会返回 undefined:
1 | const [namex, namey, namez, namem] = names; |
我们可以在解构出来的数据为 undefined 的时候,给它一个默认值:
1 | const [namex, namey, namez, namem = "aaa"] = names; |
4.2. 对象的解构
对象的解构和数组的解构是相似的,不同之处在于:
数组中的元素是按照顺序排列的,并且我们只能根据顺序来确定需要获取的数据;
对象中的数据由 key 和 value 组成,我们可以通过 key 来获取想要的 value;
1 | const obj = { name: "why", age: 18, height: 1.88 }; |
因为对象是可以通过 key 来解构的,所以它对顺序、个数都没有要求:
1 | const { height, name, age } = obj; |
如果我们对变量的名称不是很满意,那么我们可以重新命名:
1 | const { name: whyName, age: whyAge } = obj; |
我们也可以给变量一个默认值:
1 | const { name, address = "广州市" } = obj; |
4.3. 解构的使用
解构目前在开发中使用是非常多的:
比如在开发中拿到一个变量时,自动对其进行解构使用;
比如对函数的参数进行解构;
文章转载于coderwhy | JavaScript 高级系列(十二) - ES6~ES13-类和对象