一、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函数,并且执行以下操作:

  1. 在内存中创建一个新的空对象
  2. 这个对象内部的[[prototype]]属性会被赋值为该类的prototype属性
  3. 构造函数内部的this,会指向创建出来的对象
  4. 执行构造函数的内部代码(函数体代码)
  5. 如果构造函数没有返回非空对象,则返回创建出来的新对象
 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. 必须有父类引用指向子类对象
 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. 属性的简写

     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
    
  2. 方法的简写

     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~
    
  3. 计算属性名

    1
    2
    3
    4
    5
    6
    7
    
    var key = "address"
    
    var obj = {
      [key]: "广州"
    }
    
    console.log(obj.address)  // 广州
    

六、解构Destructuring

ES6中 新增了一个从数组或对象中方便获取数据的方法,称之为解构。

解构赋值是一种特殊的语法,它使我们可以将数组或对象”拆包“至一系列变量中。

可以划分为:

  1. 数组解构

     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
    
  2. 对象解构

     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}