概述
在ES6中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。
ES6 提供了更接近传统语言的写法,新引入的class关键字具有正式定义类的能力。类(class)是ECMAScript中新的基础性语法糖结构,虽然ECMAScript 6类表面上看起来可以支持正式的面向对象编程,但实际上它背后使用的仍然是原型和构造函数的概念,让对象原型的写法更加清晰、更像面向对象编程的语法。
类实际上是个“特殊的函数”,就像你能够定义的函数表达式和函数声明一样,类语法有两个组成部分:类表达式和类声明。
严格模式
类和模块的内部,默认就是严格模式,所以不需要使用 use strict 指定运行模式
创建一个类的语法格式如下:
class ClassName { constructor() { ... } }
实例:
class Runoob {
constructor(name, url) {
this.name = name;
this.url = url;
}
}
// 创建对象时会自动调用构造函数方法 constructor()。
一、类的定义
- 定义类也有两种主要方式:类声明和类表达式。这两种方式都使用class关键字加大括号:
// 类声明
class Person {}
// 类表达式
const TestPerson = class {}
- 函数声明和类声明之间的一个重要区别是函数声明会提升,类声明不会。需要先进行声明,再去访问,否则会报错。
var person= new Person()
class Person {
constructor(x, y) {
this.x = x
this.y = y
}
}
// Person is not defined
- 另一个跟函数声明不同的地方是,函数受函数作用域限制,而类受块作用域限制:
{
function FunctionDeclaration () {}
class ClassDeclaration {}
// 使用var 声明
var VarClass = class {}
// 使用let/const 声明
let LetClass = class {}
}
console.log(FunctionDeclaration) // FunctionDeclaration () {}
console.log(ClassDeclaration) // ReferenceError: ClassDeclaration is not defined
console.log(VarClass) // class {}
console.log(LetClass) // ReferenceError: letClass is not defined
类声明不可以重复
class Person {}
class Person {}
// TypeError Identifier 'Person' has already been declared
- 类必须使用 new 调用,否则会报错。这是它跟普通构造函数的一个主要区别,就是后者不用 new 也可以执行
class Person {
constructor(x, y) {
this.x = x
this.y = y
}
}
Person()
// TypeError Class constructor Person cannot be invoked without 'new'
- 类表达式可以是被命名的或匿名的
/* 匿名类 */
let Person = class {
constructor(x, y) {
this.x = x
this.y = y
}
}
/* 命名的类 */
let Person = class Person {
constructor(x, y) {
this.x = x
this.y = y
}
}
- class 类完全可以看成构造函数的另一种写法,这种写法可以让对象的原型属性和函数更加清晰。
class Person {}
console.log(typeof Person) // function
console.log(Person === Person.prototype.constructor) // true
上面代码表明,类的数据类型就是函数,类本身就指向构造函数。
二、类构造函数
首先我们要了解什么是构造方法
构造方法是一种特殊的方法:
- 构造方法名为 constructor()。
- 构造方法在创建新对象时会自动执行。
- 构造方法用于初始化对象属性。
- 如果不定义构造方法,JavaScript 会自动添加一个空的构造方法。
constructor 方法
constructor 方法是类的默认方法,通过 new 命令生成对象实例时,自动调用该方法(默认返回实例对象 this)。一个类必须有 constructor 方法,如果没有显式定义,一个空的 constructor 方法会被默认添加。一个类只能拥有一个名为 “constructor” 的特殊方法,如果类包含多个 constructor 的方法,则将抛出 一个 SyntaxError 。
如果没有定义"constructor"构造函数,class 会默认添加一个空的"constructor"构造函数。
constructor 方法也是一个特殊的方法,这种方法用于创建和初始化一个由`class`创建的对象。
通过 new 关键字生成对象实例时,自动会调用该方法。
class Person {
constructor(x, y) {
this.x = x // 默认返回实例对象 this
this.y = y
}
toString() {
console.log(this.x + ', ' + this.y)
}
}
注意:
- 在类中声明方法的时候,方法前不加 function 关键字
- 方法之间不要用逗号分隔,否则会报错
- 类的内部所有定义的方法,都是不可枚举的(non-enumerable)
- 一个类中只能拥有一个 constructor 方法
静态方法
静态方法可以通过类名调用,不能通过实例对象调用,否则会报错
class Person {
static sum(a, b) {
console.log(a + b)
}
}
var p = new Person()
Person.sum(1, 2) // 3
p.sum(1,2) // TypeError p.sum is not a function
原型方法
类的所有方法都定义在类的 prototype 属性上面,在类的实例上面调用方法,其实就是调用原型上的方法
原型方法可以通过实例对象调用,但不能通过类名调用,会报错
class Person {
constructor() {
// 默认返回实例对象 this
}
sum() {
}
toString() {
console.log('123456')
}
}
// 给 Person 的原型添加方法
Person.prototype.toVal = function() {
console.log('I am is toVal')
}
// 等同于
Person.prototype = {
constructor() {},
sum() {},
toString() {}
}
var p = new Person()
p.toString() // 123456
p.toVal() // I am is toVal
Person.toString() // TypeError Person.toStringis not a function
Person.toVal() // TypeError Person.toVal is not a function
实例方法
实例方法也可以通过实例对象调用,但同样不能通过类名调用,会报错
class Person {
constructor() {
this.sum = function(a, b) {
console.log(a + b)
}
}
}
var p = new Person()
p.sum(1,2) // 3
Person.sum(1,2) // TypeError Person.sum is not a function
继承 extends
- 解决代码的复用
- 使用extends关键字实现继承
- 子类可以继承父类中所有的方法和属性
- 子类只能继承一个父类(单继承),一个父类可以有多个子类
- 子类的构造方法中必须有super() 来指定调用父类的构造方法,并且位于子类构造方法中的第一行
- 子类中如果有与父类相同的方法和属性,将会优先使用子类的(覆盖)
使用 extends 关键字,让子类继承父类的属性和方法。
class Person {
num = 1
text = 'person'
getNum = () => console.log(this.num, this)
addNum () {
console.log(++this.num, this)
}
}
// 继承
class Child extends Person {
constructor () {
super()
this.getText()
}
getText = () => console.log(this.text, this)
}
const a = new Child() // output: person Child {}
console.log(a instanceof Child) // output: true
console.log(a instanceof Person) // output: true
a.getText() // output: person Child {}
a.getNum() // output: 1 Child {}
a.addNum() // output: 2 Child {}
a.getNum() // output: 2 Child {}
a.text // person
a.num // 2
从上面代码中,我们可以看出Child 类 继承了 Person 的属性及方法,在Child 中也是可以调用Person的方法及属性,注意 this 的值会反映调用相应方法的实例或者类。子类中(Child)如果设置了 constructor 方法 就必须调用 super() ,否则就会出现新建实例时报错,如果没有 constructor 构造函数,在实例化继承类时会调用 super() ,而且会传入所有传给继承类的参数(后面会详细讲解)。
class Person {
static staticText = 'staticText';
#private = 'private'
static staticMethods1 (person) {
console.log('staticMethods1', this)
person.#privateMethods()
}
#privateMethods () {
console.log('#privateMethods', this)
}
}
// 使用表达式格式 也是可以使用 extends 继承
const Child = class extends Person {
methods () {
console.log('methods', Child.staticText)
}
}
const a = new Child()
a.methods() // output: methods staticText
Child.staticMethods1(a)
// output: staticMethods1 class Child {}
// output: #privateMethods Child {}
Person.staticMethods1(a)
// output: staticMethods1 class Person {}
// output: #privateMethods Child {}
super 关键字可以作函数使用,也可以作对象使用,但是其只能在继承类中使用,且只能在继承类的constructor 构造函数、实例方法和静态方法中使用。作为函数时是在 继承类的constructor 构造函数中使用,根据要求如果继承类中定义了constructor构造函数就必须要调用super方法(调用父类的constructor),否则就会报错。
class Person {}
class Child extends Person {
constructor () {
// 如果不调用 super() 就会报错
// ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
super() // 调用父级的constructor
console.log(this) // Child {}
}
}
注意: constructor() 中必须super() 顶部首段执行代码,否则也是一样报错;
在使用 super() 时应该注意下面几个问题:
- super只能在继承类构造函数和静态方法中使用。
class Person {
constructor () {
// 在非继承类 的constructor 中使用super 会报错
super() // SyntaxError: 'super' keyword unexpected here
}
methods () {
console.log(super.text) // undefined
}
static staticMethods () {
console.log(super.text) // undefined
}
}
- 不能单独引用super关键字,要么用它调用构造函数,要么用它引用静态方法。
class Person {}
class Child extends Person {
constructor () {
super // SyntaxError: 'super' keyword unexpected here
}
methods () {
console.log(super) // SyntaxError: 'super' keyword unexpected here
}
static staticMethods () {
console.log(super) // SyntaxError: 'super' keyword unexpected here
}
}
- 调用super()会调用父类构造函数,并将返回的实例赋值给this
class Person {}
class Child extends Person {
constructor () {
super()
console.log(this instanceof Person) // output: true
}
}
new Child()
- super() 的行为如同调用构造函数,如果需要给父类构造函数传参,则需要手动传入。
class Person {
constructor (text) {
this.text = text
}
}
class Child extends Person {
constructor (text) {
super(text)
}
}
// 这里注意 其text 会设置到Child 中
const a = new Child('设置 text') // Child { text: '设置 text' }
console.log(a.text) // output: 设置 text
- 如果没有定义类构造函数,在实例化继承类时会调用super(),而且会传入所有传给继承类的参数。
class Person {
constructor (text) {
this.text = text
}
}
class Child extends Person {}
const a = new Child('设置 text'); // Child { text: '设置 text' }
// 上面提到过 会默认 生成 constructor (...arge) {super(...arge)}
- 如果在继承类中显式定义了构造函数,则要么必须在其中调用super(),要么必须在其中返回一个对象。
class Person {
methods () {}
}
class Child1 extends Person {}
class Child2 extends Person {
constructor () {
super()
}
}
class Child3 extends Person {
constructor () {
return {}
}
}
const a = new Child1() // Child1 {}
const b = new Child2() // Child2 {}
const c = new Child3() // {} 指向 实例函数 返回的对象
今天关于class的总结就到此结束吧!