JavaScript:权威的类指南

朋友们,美好的一天!



JavaScript使用原型继承模型:每个对象都继承原型对象的字段(属性)和方法。



JavaScript中不存在Java或Swift中用作创建对象的模板或架构的类。原型继承中只有对象。



原型继承可以模仿类继承的经典模型。为此,ES6引入了class关键字:用于原型继承的语法糖。



在本文中,我们将学习如何使用类:定义类,它们的私有(私有)和公共(公共)字段和方法,以及创建实例。



1.定义:class关键字



class关键字用于定义类:



class User {
    //  
}


这种语法称为类声明。



该类可能没有名称。使用类表达式,可以将类分配给变量:



const UserClass = class {
    //  
}


可以将类导出为模块。这是默认导出的示例:



export default class User {
    //  
}


这是一个命名导出的示例:



export class User {
    //  
}


类用于创建实例。实例是一个包含类数据和逻辑的对象。







使用new:instance = new Class()运算符创建实例。



以下是创建User类的实例的方法:



const myUser = new User()


2.初始化:构造函数()



构造函数(param1,param2,...)是类中的一种特殊方法,用于初始化实例。这是设置和配置实例字段的初始值的地方。



在以下示例中,构造函数为name字段设置初始值:



class User {
    constructor(name) {
        this.name = name
    }
}


构造函数采用一个名为name的参数,该参数用于设置this.name字段的初始值。



构造函数中的this指向正在创建的实例。



用于实例化类的参数成为其构造函数的参数:



class User {
    constructor(name) {
        name // 
        this.name = name
    }
}

const user = new User('')


构造函数中的name参数的值为'Pechorin'。



如果您没有定义自己的构造函数,则会创建一个标准构造函数,这是一个不影响实例的空函数。



3.领域



类字段是包含特定信息的变量。这些字段可以分为两组:



  1. 类实例字段
  2. 类本身的字段(静态)


这些字段还具有两个访问级别:



  1. Public(公共):字段在类内部和实例中均可用
  2. 私人(私人):只能在课程中访问字段


3.1。类实例的公共字段



class User {
    constructor(name) {
        this.name = name
    }
}


表达式this.name = name创建一个实例字段名称,并为其分配一个初始值。



可以使用属性访问器访问此字段:



const user = new User('')
user.name // 


在这种情况下,名称是一个公共字段,因为它可以在User类之外访问。



在构造函数内部隐式创建字段时,很难获得所有字段的列表。为此,必须从构造函数中检索字段。



最好的方法是显式定义类字段。构造函数做什么无关紧要,实例始终具有相同的字段集。



创建类字段的建议使您可以定义类中的字段此外,您可以在此处为字段分配初始值:



class SomeClass {
    field1
    field2 = ' '

    // ...
}


让我们通过在其中定义一个公共名称字段来更改User类的代码:



class User {
    name

    constructor(name) {
        this.name = name
    }
}

const user = new User('')
user.name // 


这些公共字段具有很强的描述性,快速浏览该类可使您了解其数据的结构。



此外,可以在定义时初始化类字段:



class User {
    name = ''

    constructor() {
        //  
    }
}

const user = new User()
user.name // 


访问开放字段及其更改没有任何限制。您可以在构造函数,方法和类外部读取此类字段并为其分配值。



3.2。类实例的私有字段



封装使您可以隐藏类的内部实现细节。谁使用封装的类,谁都依赖于公共接口,而无需深入了解该类的实现。



当实现细节更改时,这些类更易于更新。



隐藏细节的一种好方法是使用私有字段。这些字段只能在它们所属的类中读取和更改。班级以外不提供私人字段。



要将字段设为私有,请在其名称前加上#符号,例如#myPrivateField。引用此类字段时,必须始终使用指定的前缀。



让我们将名称字段设为私有:



class User {
    #name

    constructor(name) {
        this.#name = name
    }

    getName() {
        return this.#name
    }
}

const user = new User('')
user.getName() // 
user.#name // SyntaxError


#name是一个私有字段。只能在User类内部访问它。getName()方法可完成此操作。



但是,尝试在User类之外访问#name会引发语法错误:SyntaxError:必须在封闭的类中声明专用字段'#name'。



3.3。公共静态字段



在一个类中,您可以定义属于该类本身的字段:静态字段。此类字段用于创建存储类所需信息的常量。



要创建静态字段,请在字段名称之前使用static关键字:static myStaticField。



让我们添加一个新的类型字段来定义用户类型:管理员或常规。静态字段TYPE_ADMIN和TYPE_REGULAR是每种用户类型的常量:



class User {
    static TYPE_ADMIN = 'admin'
    static TYPE_REGULAR = 'regular'

    name
    type

    constructor(name, type) {
        this.name = name
        this.type = type
    }
}

const admin = new User(' ', User.TYPE_ADMIN)
admin.type === User.TYPE_ADMIN // true


要访问静态字段,请使用类名称和属性名称:User.TYPE_ADMIN和User.TYPE_REGULAR。



3.4。私有静态字段



有时,静态字段也是该类内部实现的一部分。要封装此类字段,可以将它们设为私有。



为此,请在字段名称前添加#:static #myPrivateStaticFiled。



假设我们要限制User类的实例数量。可以创建私有静态字段以隐藏有关实例数量的信息:



class User {
    static #MAX_INSTANCES = 2
    static #instances = 0
}

name

constructor(name) {
    User.#instances++
    if (User.#instances > User.#MAX_INSTANCES) {
        throw new Error('    User')
    }
    this.name = name
}

new User('')
new User('')
new User('') //     User


静态字段“用户#MAX_INSTANCES”定义了允许的实例数,而“用户#实例”定义了创建的实例数。



这些私有静态字段仅在User类内部可用。外界都无法影响约束条件:这是封装的优点之一。



大约 lane:如果将实例数限制为一个,则可以得到Singleton设计模式的有趣实现。



4.方法



这些字段包含数据。更改数据的能力由class:方法的一部分的特殊功能提供。



JavaScript支持实例方法和静态方法。



4.1。实例方法



类实例的方法可以修改其数据。实例方法可以调用其他实例方法以及静态方法。



例如,让我们定义一个getName()方法,该方法返回用户名:



class User {
    name = ''

    constructor(name) {
        this.name = name
    }

    getName() {
        return this.name
    }
}

const user = new User('')
user.getName() // 


在类方法以及构造函数中,这都指向要创建的实例。使用它来获取实例数据:this.field,或调用方法:this.method()。



让我们添加一个新方法nameContains(str),该方法带有一个参数并调用另一种方法:



class User {
    name

    constructor(name) {
        this.name = name
    }

    getName() {
        return this.name
    }

    nameContains(str) {
        return this.getName().includes(str)
    }
}

const user = new User('')
user.nameContains('') // true
user.nameContains('') // false


nameContains(str)是User类的一种方法,它带有一个参数。它调用另一个实例方法getName()来获取用户名。



该方法也可以是私有的。要将方法设为私有,请使用#前缀。



让我们将getName()方法设为私有:



class User {
    #name

    constructor(name) {
        this.#name = name
    }

    #getName() {
        return this.#name
    }

    nameContains(str) {
        return this.#getName().includes(str)
    }
}

const user = new User('')
user.nameContains('') // true
user.nameContains('') // false

user.#getName // SyntaxError


#getName()是私有方法。在nameContains(str)方法内部,我们这样称呼它:#GetName()。



由于是私有方法,因此无法在User类之外调用#getName()方法。



4.2。吸气剂和二传手



获取器和设置器是访问器或计算的属性。这些是模拟字段的方法,但允许您读取和写入数据。



Getter用于接收数据,Setter用于修改数据。



为防止将空字符串分配给name字段,请将私有的#nameValue字段包装在getter和setter中:



class User {
    #nameValue

    constructor(name) {
        this.name = name
    }

    get name() {
        return this.#nameValue
    }

    set name(name) {
        if (name === '') {
            throw new Error('     ')
        }
        this.#nameValue = name
    }
}

const user = new User('')
user.name //  , 
user.name = '' //  

user.name = '' //      


4.3。静态方法



静态方法是属于类本身的函数。它们定义类的逻辑,而不是其实例。



要创建静态方法,请在方法名称前面使用static关键字:static myStaticMethod()。



使用静态方法时,要牢记两个简单规则:



  1. 静态方法可以访问静态字段
  2. 它无权访问实例字段


让我们创建一个静态方法来检查是否已创建具有指定名称的用户:



class User {
    static #takenNames = []

    static isNameTaken(name) {
        return User.#takenNames.includes(name)
    }

    name = ''

    constructor(name) {
        this.name = name
        User.#takenNames.push(name)
    }
}

const user = new User('')

User.isNameTaken('') // true
User.isNameTaken('') // false


isNameTaken()是使用私有静态用户的静态方法。



静态方法也可以是私有的:静态#myPrivateStaticMethod()。此类方法只能在类中调用。



5.继承:扩展



JavaScript中的类使用extends关键字支持继承。



在表达式中,Child类扩展了Parent {},Child类从Parent继承了构造函数,字段和方法。



让我们创建一个扩展父类User的子类ContentWriter:



class User {
    name

    constructor(name) {
        this.name = name
    }

    getName() {
        return this.name
    }
}

class ContentWriter extends User {
    posts = []
}

const writer = new ContentWriter('')

writer.name // 
writer.getName() // 
writer.posts // []


ContentWriter从User继承构造函数,getName()方法和名称字段。ContentWriter本身定义了一个新的发布字段。



注意,父类的私有字段和方法不被子类继承。



5.1。父构造函数:构造函数()中的super()


为了在子类中调用父类的构造函数,请使用子类的构造函数中可用的特殊super()函数。



让ContentWriter构造函数调用父构造函数并初始化posts字段:



class User {
    name

    constructor(name) {
        this.name = name
    }

    getName() {
        return this.name
    }
}

class ContentWriter extends User {
    posts = []

    constructor(name, posts) {
        super(name)
        this.posts = posts
    }
}

const writer = new ContentWriter('', ['  '])
writer.name // 
writer.posts // ['  ']


子类ContentWriter中的super(名称)调用父类的构造函数User。



请注意,在使用this关键字之前,将在子构造函数中调用super()。super()调用将父构造函数“绑定”到实例。



class Child extends Parent {
    constructor(value1, value2) {
        //  !
        this.prop2 = value2
        super(value1)
    }
}


5.2。父实例:超级方法


为了访问子类内部的父方法,请使用特殊的超级快捷方式:



class User {
    name

    constructor(name) {
        this.name = name
    }

    getName() {
        return this.name
    }
}

class ContentWriter extends User {
    posts = []

    constructor(name, posts) {
        super(name)
        this.posts = posts
    }

    getName() {
        const name = super.getName()
        if (name === '') {
            return ''
        }
        return name
    }
}

const writer = new ContentWriter('', ['  '])
writer.getName() // 


子ContentWriter类的getName()调用父类User的getName()方法。



这称为方法覆盖。



请注意,super也可以用于父类的静态方法。



6.对象类型检查:instanceof



对象instanceof Class表达式确定对象是否为指定类的实例。



让我们考虑一个例子:



class User {
    name

    constructor(name) {
        this.name = name
    }

    getName() {
        return this.name
    }
}

const user = new User('')
const obj = {}

user instanceof User // true
obj instanceof User // false


instanceof运算符是多态的:它检查整个类链。



class User {
    name

    constructor(name) {
        this.name = name
    }

    getName() {
        return this.name
    }
}

class ContentWriter extends User {
    posts = []

    constructor(name, posts) {
        super(name)
        this.posts = posts
    }
}

const writer = new ContentWriter('', ['  '])

writer instanceof ContentWriter // true
writer instanceof User // true


如果需要定义特定的实例类怎么办?构造函数属性可用于此目的:



writer.constructor === ContentWriter // true
writer.constructor === User // false
// 
writer.__proto__ === ContentWriter.prototype // true
writer.__proto__ === User.prototype // false


7.类和原型



必须说,类语法是对原型继承的一个很好的抽象。您无需引用原型即可使用类。



但是,类只是原型继承的上层结构。任何类都是一个在调用构造函数时创建实例的函数。



接下来的两个示例是相同的。



类:



class User {
    constructor(name) {
        this.name = name
    }

    getName() {
        return this.name
    }
}

const user = new User('')

user.getName() // 
user instanceof User // true


原型:



function User(name) {
    this.name = name
}

User.prototype.getName = function () {
    return this.name
}

const user = new User('')

user.getName() // 
user instanceof User // true


因此,理解类需要对原型继承有充分的了解。



8.类功能的可用性



本文介绍的类功能在考虑的第三阶段分为ES6规范和建议:







大约 Per:根据我可以使用的信息,目前对私有类字段的支持率为68%。



9.结论



JavaScript中的类用于使用构造函数初始化实例,并定义其字段和方法。static关键字可用于定义类本身的字段和方法。



继承是使用extends关键字实现的。super关键字使您可以从子级访问父类。



为了利用封装的优势,即 隐藏内部实现细节,将字段和方法设为私有。这些字段和方法的名称必须以#符号开头。



在现代JavaScript中,类无处不在。



希望本文对您有所帮助。感谢您的关注。



All Articles