一、class类的定义
按照前面的构造函数形式创建类,不仅仅和编写普通的函数过于相识,而且代码并不容易理解
- 在ES6(ECMAScript2015)新的标准中使用了class关键字来直接定义类
- 但是类本质上依然是构造函数、原型链的语法糖而已
定义方式
方式一: 字面量
1
2
3
4
5
6
7
|
class Person {
}
var p1 = new Person()
var p2 = new Person()
console.log(p1, p2)
|
方式二: 表达式
1
2
3
4
5
|
var Student = class { }
var s1 = new Student()
var s2 = new Student()
console.log(s1, s2)
|
类的构造函数
- 每个类都可以有一个自己的构造函数(方法),这个方法的名称是固定的
constructor
- 当通过new操作符,操作一个类的时候会调用这个类的构造函数
constructor
- 每个类只能有一个构造函数,如果包含多个构造函数,就会抛出异常
当通过new操作类时,就会调用这个constructor
函数,并且执行以下操作:
- 在内存中创建一个新的空对象
- 这个对象内部的[[prototype]]属性会被赋值为该类的prototype属性
- 构造函数内部的this,会指向创建出来的对象
- 执行构造函数的内部代码(函数体代码)
- 如果构造函数没有返回非空对象,则返回创建出来的新对象
1
2
3
4
5
6
7
8
9
10
11
12
|
class Person {
// 构造函数
constructor(name, age) {
this.name = name
this.age = age
}
}
var p1 = new Person("mocha", 18)
console.log(p1.name, p1.age) // mocha 18
console.log(Person.prototype === p1.__proto__) // true
|
类的实例方法
本质上是放在类的原型上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
// 实例方法
// 本质上是放在Person.prototype
running() {
console.log(this.name + " running~")
}
eating() {
console.log(this.name + " eating~")
}
}
var p1 = new Person("mocha", 18)
p1.running() // mocha running
p1.eating() // mocha eating
|
类的静态方法
静态方法(类方法)通常用于定义最直接使用类来执行的方法,不需要有类的实例,使用static
关键字来定义
1
2
3
4
5
6
7
|
class Person {
static hello() {
console.log("say hello")
}
}
Person.hello()
|
类的访问器方法
补充: 针对对象的访问器——描述符方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
var obj = {
_name: ""
}
Object.defineProperty(obj, "name", {
configurable: true,
enumerable: true,
set: function (value) {
this._name = value
},
get: function () {
return this._name
}
})
obj.name = "mocha"
console.log(obj._name) // mocha
|
补充: 针对对象的访问器——定义访问器
1
2
3
4
5
6
7
8
9
10
11
12
|
var obj = {
_name: "",
set name(value) {
this._name = value
},
get name() {
return this._name
}
}
obj.name = "mocha"
console.log(obj._name) // mocha
|
class类的访问器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class Person {
/* 约定:以_开头的属性和方法,不在外界访问 */
constructor(name, age) {
this._name = name
this.age = age
}
set name(value) {
this._name = value
}
get name() {
return this._name
}
}
var p1 = new Person("mocha", 18)
p1.name = "kobe"
console.log(p1.name)
|
应用场景:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class Rectangle {
constructor(x, y, width, height) {
this.x = x
this.y = y
this.width = width
this.height = height
}
get position() {
return { x: this.x, y: this.y }
}
get size() {
return { width: this.width, height: this.height }
}
}
var rect = new Rectangle(10, 20, 100, 200)
console.log(rect.position) // {x: 10, y: 20}
console.log(rect.size) // {width: 100, height: 200}
|
class类和function类区别
class类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
function Person1(name, age) {
this.name = name
this.age = age
}
Person1.prototype.running = function () { }
Person1.prototype.eating = function () { }
var p1 = new Person1("mocha", 18)
console.log(p1.__proto__ === Person1.prototype) // true
console.log(typeof Person1) // function
// 不同点:可以作为普通函数调用
Person1("abc", 100)
|
function类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class Person2 {
constructor(name, age) {
this.name = name
this.age = age
}
running() { }
eating() { }
}
var p2 = new Person2("latte", 20)
console.log(p2.__proto__ === Person2.prototype) // true
console.log(typeof Person2) // function
// 不同点:不能作为普通函数调用
Person2("cba", 100)
|
二、class类的继承
在ES6中新增了extends
关键字,可以直接实现继承
1
2
3
4
5
6
7
8
9
|
// 父类
class Person {
}
// 子类
class Student extends Person {
}
|
super 关键字
- 执行super.method(…)来调用一个父类方法
- 执行super(…)来调用constructor
- 注意:在子类的构造函数中使用this或返回默认对象之前,必须先通过super调用父类的构造函数
super的使用位置
-
子类构造函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// 父类
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
}
// 子类
class Student extends Person {
constructor(name, age, sno, score) {
super(name, age)
this.sno = sno
this.score = score
}
}
|
-
实例方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// 父类
class Person {
running () {
console.log("running~")
}
}
// 子类
class Student extends Person {
running () {
console.log("let us")
super.running()
}
}
|
-
静态方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// 父类
class Person {
static sleep () {
console.log("sleep")
}
}
// 子类
class Student extends Person {
static sleep () {
console.log("let us")
super.sleep()
}
}
|
继承内置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class MCArray extends Array {
get firstItem() {
return this[0]
}
get lastItem() {
return this[this.length - 1]
}
}
var arr = new MCArray(10, 20, 30)
console.log(arr.firstItem) // 10
console.log(arr.lastItem) // 10
|
三、class类的混入
JavaScript只支持单继承,不支持多继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
function mixinAnimal(BaseClass) {
return class extends BaseClass {
running() {
console.log("running~")
}
}
}
function mixinRunner(BaseClass) {
return class extends BaseClass {
flying() {
console.log("flying~")
}
}
}
class Bird {
eating() {
console.log("eating~")
}
}
var NewBird = mixinRunner(mixinAnimal(Bird))
var bird = new NewBird()
bird.running() // running~
bird.flying() // flying~
bird.eating() // eating~
|
四、JavaScript的多态
维基百科的定义: 指为不同数据类型的实体提供统一的接口,或使用一个单一的符号来表示多个不同的类型。
我的总结: 不同的数据类型进行同一个操作,表现出不同的行为就是多态的体现。
Java中面向对象的多态:
在严格意义的面向对象语言中,多态是存在如下条件的:
- 必须有继承(或者是实现接口)
- 必须有父类引用指向子类对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
// 父类
class Shape {
getArea() { }
}
// 子类
class Rectangle extends Shape {
constructor(width, height) {
super()
this.width = width
this.height = height
}
getArea() {
return this.width * this.height
}
}
// 子类
class Circle extends Shape {
constructor(radius) {
super()
this.radius = radius
}
getArea() {
return this.radius ** 2 * 3.14
}
}
var rect1 = new Rectangle(100, 200)
var rect2 = new Rectangle(20, 30)
var c1 = new Circle(10)
var c2 = new Circle(15)
// 多态的表现形式
function getShapeArea(shape) {
const area = shape.getArea()
console.log(area)
}
getShapeArea(rect1) // 20000
getShapeArea(rect2) // 600
getShapeArea(c1) // 314
getShapeArea(c2) // 706.5
|
JavaScript中面向对象的多态:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// 多态的表现
// 为不同数据类型的实体提供统一的接口
function sum(a1, a1) {
return a1 + a2
}
sum(20, 30)
sum("abc", "cba")
// 使用一个单一的符号来表示多个不同的类型
var foo = 123
foo = "hello world"
foo = {}
foo = []
|
五、对象字面量的增强
ES6中对 对象字面量 进行了增强,称之为Enhanced object literals(增强对象字面量)
主要包括:
-
属性的简写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
var name = 'mocha'
var age = 18
var obj = {
name,
age
}
console.log(obj.name) // mocha
console.log(obj.age) // 18
// 函数中的表现
function foo() {
var message = "hello world"
var info = "my name is mocha"
return {
message,
info
}
}
var result = foo()
console.log(result.message) // hello world
console.log(result.info) // my name is mocha
|
-
方法的简写
1
2
3
4
5
6
7
8
9
10
11
12
|
var name = 'mocha'
var age = 18
var obj = {
name,
age,
running() {
console.log(this.name + " running~")
}
}
obj.running() // mocha running~
|
-
计算属性名
1
2
3
4
5
6
7
|
var key = "address"
var obj = {
[key]: "广州"
}
console.log(obj.address) // 广州
|
六、解构Destructuring
ES6中 新增了一个从数组或对象中方便获取数据的方法,称之为解构。
解构赋值是一种特殊的语法,它使我们可以将数组或对象”拆包“至一系列变量中。
可以划分为:
-
数组解构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
var names = ["abc", "cba", "nba", "mba"]
// 基本使用
var [name1, name2, name3] = names
console.log(name1, name2, name3) // abc cba nba
// 有严格的顺序
var [n1, , n3] = names
console.log(n1, n3) // abc nba
// 解构出数组
var [nn1, nn2, ...newNames] = names
console.log(nn1, nn2, newNames) // abc cba (2) ['nba', 'mba']
// 解构的默认值
var [nnn1, nnn2, nnn3, nnn4, nnn5 = "aaa"] = names
console.log(nnn5) // aaa
|
-
对象解构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
var obj = {
name: "mocha",
age: 18,
height: 1.88
}
var { name, age, height } = obj
console.log(name, age, height) // mocha 18 1.88
// 对变量进行重命名
var { name: mName } = obj
console.log(mName) // mocha
// 默认值
var { adress: mAddress = "广州" } = obj
console.log(mAddress) // 广州
// 剩余内容
var { name: newName, ...newObj } = obj
console.log(newObj) // {age: 18, height: 1.88}
|