JavaScript中的类

本文详细介绍了JavaScript中的类,包括基本的类声明语法、类表达式、静态成员、访问器属性、可计算成员名称、生成器方法、继承和派生类。类声明不被提升,所有方法默认不可枚举。类表达式可以作为一等公民,可以在函数中传递、返回和赋值。类支持getter和setter,以及可计算方法名称。同时,文章还讨论了静态成员的使用和继承机制,强调了super关键字在构造函数中的重要性以及类方法遮蔽的现象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

1、类的声明

1.1 基本的类声明语法

要声明一个类,首先编写class关键字,紧跟着的是类的名字。

class Person {
    constructor(name) {
        this.name = name;
    }
    sayName() {
        console.log(this.name);
    }
}

类的使用

let person = new Person("yff");
person.sayName();//output yff

console.log(person instanceof Person);
console.log(person instanceof Object);

console.log(typeof Person);//"function"
console.log(typeof Person.prototype.sayName);//"function"

执行结果:

类中使用简洁语法来定义方法,因而不需要添加function关键字。

除constructor外没有其他保留的方法名,所以可以尽情添加方法。

自有属性是实例中的属性,不会出现在原型上,且只能在类的构造或方法中创建,此例中的name就是一个自有属性(成员变量)。

1.2 为何使用类语法

类和函数的区别,类的特点:

  • 函数声明可以被提升,而类声明不能被提升。真正执行声明语句之前,它们会一直存在于临时死区中。
  • 类声明中的所有代码将自动运行在严格模式,而且无法强行让代码脱离严格模式执行。
  • 在自定义类型中,需要通过Object.defineProperty()方法手工指定某个方法为不可枚举;而在类中,所有方法都是不可枚举的。
  • 每个类都有一个名为[[Construct]]的内部方法,通过关键字new调用那些不含[[Construct]]的方法会导致程序抛出错误。
  • 使用除关键字new以外的方式调用类的构造函数会导致程序抛出错误。
  • 在类中修改类名会导致程序报错

2、类表达式

类和函数都有两种存在形式:声明形式和表达式形式。

  • 声明形式的函数和类都由相应的关键字(function和class)进行定义,随后跟一个标识符。
  • 表达式形式不需要在关键字后添加符。

声明形式:

//类的声明形式
class Person {
    constructor(name) {
        this.name = name;
    }
    sayName() {
        console.log(this.name);
    }
}
//函数的声明形式
function test() {
    console.log("test");
}

类的表达式不需要标识符在类后。类声明和类表达式功能一样,不会像函数声明和函数表达式一样被提升。

//类的表达式语法
let Person = class {
    constructor(name) {
        this.name = name;
    }
    sayName() {
        console.log(this.name);
    }
}

3、作为一等公民的类

一等公民是指:

  • 可以传入函数。
  • 可以从函数返回。
  • 可以赋值给变量的值。

JavaScript中函数是一等公民、类也是一等公民。

示例1:调用createObject()函数时传入一个匿名表达式作为参数,然后通过关键字new实例化这个类并返回实例,将其储存在变量obj中。

function createObject(classDef) {
    return new classDef();
}
let obj = createObject(
    class Person {
        sayName() {
            console.log("yxy");
        }
    }
)
obj.sayName();

示例2:通过立即调用类构造函数创建单例。用new调用类表达式,紧接着通过一对小括号调用这个表达式,例如:

let person = new class {
    constructor(name) {
        this.name = name;
    }
    sayName() {
        console.log(this.name);
    }
}("yxy");
person.sayName();//"yxy"

4、访问器属性

尽管应该在类构造函数中创建自己的属性,但是类也支持直接在原型上定义访问器属性。

  • 创建getter时,需要在关键字get后紧跟一个空格和相应的标识符。
  • 创建setter时,把关键字get替换为set即可。
class CustomHTMLElement {
    constructor(element) {
        this.element = element;
    }
    get html() {
        return this.element.innerHTML;
    }
    set html(value) {
        this.element.innerHTML = value;
    }
};

let descriptor = Object.getOwnPropertyDescriptor(CustomHTMLElement.prototype, "html");
console.log("get" in descriptor);//true
console.log("set" in descriptor);//true

下面是运行结果:

这段代码中的CustomHTMLElement类是一个针对现有DOM元素的包装器,并通过getter和setter方法将这个元素的innerHTML方法委托给html属性,这个访问器属性是在CustomHTMLElement.prototype上创建的。与其他方法一样,创建时声明该属性不可枚举。

5、可计算成员名称

类和对象字面量还有很多相似之处,类方法和访问器属性也支持使用可计算名称。下面示例中,Person类通过变量来给类定义中的方法命名,字符串"sayName"被赋值给methodName变量,然后methodName又被用于声明随后可直接访问的sayName()方法。

let methodName = "sayName";
class Person {
    constructor(name) {
        this.name = name;
    }
    [methodName]() {
        console.log(this.name);
    }
};
let person1 = new Person("yxy");
person1.sayName();//"yxy"

6、生成器方法

通过Symbol.iterator定义生成器方法即可为类定义默认叠加器。

class Collection {
    constructor() {
        this.items = [];
    }
    *[Symbol.iterator]() {
        yield* this.items.values();
    }
};

let collection = new Collection();
collection.items.push(1);
collection.items.push(2);
collection.items.push(3);

for (let x of collection) {
    console.log(x);
}

运行结果如下:

这个示例用可计算名称创建了一个代理this.items数组values()迭代器的生成器方法。任何管理一系列值的类都应该引入默认迭代器,因为一些与特定集合有关的操作需要所操作的集合含有一个迭代器。现在可以将Collection的实例直接用于for-of循环中或用展开运算符操作它。

7、静态成员

在类方法前面加上static关键字,则该方法定义为静态方法。类中的所有方法和属性都可以使用static关键字来定义,但不能用来定义构造函数。

class Person {
    constructor(name) {
        this.name = name;
    }
    sayName() {
        Person.singSong();//"lalalala"
        console.log(this.name);
    }
    static singSong() {
        console.log("lalalala");
    }
};
let person1 = new Person("yxy");
person1.sayName();//"yxy"
// person1.singSong();//报错,person1.singSong is not a function
let person2 = Person.singSong();//"lalalala"

 静态方法的使用:

  • 静态方法不能在类实例中访问,只能通过类名.方法名的方法访问。

8、继承和派生类

使用extends关键字可以指定类继承的函数。原型会自动调整,通过调用super()方法即可访问基类的构造函数。

class Rectangle {
    constructor(length, width) {
        this.length = length;
        this.width = width;
    }
    getArea() {
        return this.length * this.length;
    }
}

class Square extends Rectangle {
    constructor(length) {
        super(length, length);
    }
}

let square1 = new Square(3);
console.log(square1.getArea());//9
console.log(square1 instanceof Square);//true

**instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。

 继承自其他类的类被称作派生类,如果在派生类中指定了构造函数则必须要调用super(),如果不调用,程序会报错。如果选择不使用构造函数,则创建新的类实例时会自动调用super()并传入参数。

                                                     super()注意事项

1、只可在派生类的构造函数中使用super(),如果在非派生类(不是用extends声明的类)或函数中使用则会抛出错误。

2、在构造函数中,访问this前,一定要调用super(),它负责初始化this,如果在调用super()之前尝试访问this会导致程序出错

3、如果不想调用super(),则唯一的方法是让类的构造函数返回一个对象。

8.1 类方法遮蔽

派生类中的方法总会覆盖基类中的同名方法。

class Rectangle {
    constructor(length, width) {
        this.length = length;
        this.width = width;
    }
    getArea() {
        return this.length * this.length;
    }
}

class Square extends Rectangle {
    constructor(length) {
        super(length, length);
    }
    getArea() {
        let sArea = super.getArea();
        console.log("sArea:" + sArea);//9
        return this.length * this.length * 3;
    }
}

let square1 = new Square(3);
console.log(square1.getArea());//27

由于为Square定义了getArea()方法,便不能在Square的实例中调用Rectangle.prototype.getArea()方法,如果想调用基类中的该方法,可以调用super.getArea()方法。

8.2 静态成员继承

如果基类中有静态成员,那这些静态成员也可以在派生类中使用。

class Rectangle {
    constructor(length, width) {
        this.length = length;
        this.width = width;
    }
    getArea() {
        return this.length * this.width;
    }
    static create(length, width) {
        return new Rectangle(length, width);
    }
}

class Square extends Rectangle {
    constructor(length) {
        super(length, length);
    }
}

let rect = Square.create(3, 4);
console.log(rect.getArea());//12,不是9
console.log(rect instanceof Square);//false,rect是Rectangle的实例不是Square的实例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值