百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

谈谈你对ES6中Class类的理解_es6中的class常见使用场景

myzbx 2025-08-31 06:15 8 浏览

我们都知道ES6有了面向对象的概念,而class(类)就是ES6中新的基础性语法糖结构,虽然ES6 class表面上看起来可以支持正式的面向对象编程,但实际上它背后使用的仍然是原型和构造函数的概念,让对象原型的写法更加清晰、更像面向对象编程的语法。

一、为什么要有class

在JS语言中,以前我们生成实例对象的方法是通过构造函数创建。

function Person(name,age) {

this.name = name;

this.age = age;

}

Person.prototype.call= function () {

console.log(`我叫${this.name}, 今年${this.age}岁`);

};

var p1 = new Person('Tom', 18);

上面这种写法跟传统的面向对象语言(比如C++和Java)差异很大,很容易让新学习这门语言的程序员感到困惑。

因此ES6提供了更接近传统语言的写法,引入了class这个概念,作为对象的模板。通过class关键字,就可以直接定义类。

class Person {

}

var p2 = new Person();

上面就是class的基本语法。它的绝大部分功能我们都可以通过ES5的构造函数以及原型做到,class写法只是让对象原型的写法更加清晰,更接近面向对象编程的语法。

下面我们来讲讲class的常用方法以及如何去使用。

二、class的方法

constructor()

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法,默认返回实例对象(即this)。一个类必须有constructor方法,如果没有手动定义,JS引擎会自动为它添加一个空的constructor方法。

class Person {

// 构造

constructor(name, age) {

this.name = name;

this.age = age;

}

}

var p3 = new Person('Tom', 18);

通过上面的案例我们可以看到,Person类可以通过constructor方法给this,也就是实例对象身上添加对应的属性,除了这种方法,我们也可以直接将这些属性定义在类的最外层:

class Person {

brand = "Tom";

price = 18;

// 构造

constructor() {}

}

var p4 = new Person();

通过这种方式定义出来的属性我们把它称为实例属性,我们可以直接将其定义在类的顶部,这样一眼就能看出来这个类有哪些实例属性。

实例方法

和ES5不同,类中的方法不需要加function关键字就可以直接生成,同时多个方法之间也不需要用逗号进行分隔,否则会报错,实例方法调用时同样可以传递参数。

class Person {

// 构造

constructor(name, age) {

this.name = name;

this.age = age;

}

call() {

console.log(`我叫${this.name}, 今年${this.age}岁`);

}

play(ball) {

console.log(`我爱玩${ball}`);

}

}

var p5 = new Person("Tom", 18);

p5.call(); // 我叫Tom, 今年18岁

p5.play("足球"); // 我爱玩足球

静态属性

实例对象身上有自身的属性,那class本身也会有自己的属性,也就是静态属性:

Person.prop = 1;

console.log(Person.prop) // 1

目前,只有这种写法可行,因为ES6明确规定,class内部只有静态方法,没有静态属性。

static静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就是class的静态方法。

class Person {

constructor() {}

call() {

console.log(`我是实例方法`);

}

static staticFn() {

console.log(`我是静态方法`,this);

}

}

var p6 = new Person("Tom", 18);

p6.call();

Person.staticFn();

p6.staticFn();

我们可以看到静态方法中的this指向的是类,而不是实例对象;

静态方法可以与非静态方法重名。实例与类调用时会调用对应的方法;

父类的静态方法,可以被子类继承。

extends 类的继承

class之间可以通过extends关键字实现继承(继承父类的所有属性和方法)。

// 父类

class Person {

constructor(name, age) {

this.name = name;

this.age = age;

}

call() {

console.log(`我叫${this.name}, 今年${this.age}岁`);

}

play(ball) {

console.log(`我喜欢玩${ball}`);

}

}

// 子类

class Student extends Person {

constructor(name, age, score) {

super(name, age);

this.score = score;

}

// 重写父类的方法

call() {

console.log(

`我叫${this.name},今年${this.age}岁, 考了${this.score}分`

);

}

}

var s1 = new Student("Tom", 18, 99);

s1.play("篮球"); // 我喜欢玩篮球

s1.call(); // 我叫Tom,今年18岁, 考了99分

从上面的案例中我们可以看到,当子类中出现与父类同名的方法时,会将父类的方法进行重写,从而使得子类实例能获取到最新的结果。

super

从上一个继承案例中我们可以发现有一个super关键字,子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。

ES5的继承是先创造子类的实例对象this,再将父类的方法添加到this上Person.apply(this),和ES5的机制不同,ES6先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。父类身上的静态方法同样会被子类继承。

取值函数(getter)和存值函数(setter)

这两个方法其实就是对对象的属性进行方法的绑定,当对某一个属性进行获取时,就会执行getter,当对某一个属性进行设置时,则执行setter。

class Person {

constructor(score) {

this.score = score;

}

get myScore() {

console.log("分数被读取了");

return this.score;

}

set myScore(newval) {

console.log("分数被修改了");

this.score = newval;

}

}

// 实例化对象

let p7 = new Person(99);

console.log(p7.myScore);

p7.myScore = 100;

console.log(p7.myScore);

上面的类中,我们自定义了score的读写方法,当读取p7的score属性时,就会调用get myScore方法,并且函数的返回值就是我们想要的score值;当对score进行重新赋值时,就会调用set myScore方法,在方法内部就会对this.score进行修改,这里要注意setter一定要传入修改后的参数,不然是接收不到新的值的。

三、class注意事项

不能重复声明

class Person { }

class Person { }

// 或

let Person = class {}

class Start { }

以上两种写法都会报错:语法错误:“Person”已经被声明。

不能直接调用(必须使用new关键字)

class Person {

constructor() {}

}

console.log(Person());

以上会报错:类型错:没有“new”不能调用类构造函数Person。

不存在提升(存在暂时性死区)

类不存在变量提升,这一点与 ES5 完全不同。

console.log(new Person());

class Person {

constructor() {}

}

以上就会报错:引用错误:初始化前无法访问“Person”。

因此子类必须在父类声明之后定义,实例的声明也是一样。

四、class的实例

ES6 class生成类的实例的写法,与 ES5 完全一样,也是使用new命令。前面说过,如果忘记加上new,像函数那样调用class(),就将会报错。

类的属性和方法,除非直接定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。

class Person {

constructor(name, age) {

this.name = name;

this.age = age;

}

call() {

console.log(`我叫${this.name}, 今年${this.age}岁`);

}

}

var p8 = new Person("Tom", 18);

console.log(p8.hasOwnProperty("name")); // true

console.log(p8.hasOwnProperty("age")); // true

console.log(p8.hasOwnProperty("call")); // false

console.log(p8.__proto__.hasOwnProperty("call")); // true

上面代码中,name和age都是实例对象p8(this)自身的属性,所以hasOwnProperty方法返回true,而call是原型对象的属性(因为直接定义在Person类上),所以hasOwnProperty方法返回false。这些都与ES5保持一致。并且类的所有实例共享一个原型对象。

var p8 = new Person("Tom", 18);

var p9 = new Person("Jack", 22);

console.log(p8.__proto__ === p9.__proto__) // true

上面代码中,p8和p9都是Person的实例,它们的原型都是Person.prototype,所以__proto__属性是全等的。

这也意味着,可以通过实例的__proto__属性为class添加方法。

__proto__ 并不是语言本身的特性,虽然目前很多浏览器的JS引擎中都提供了这个私有属性,它本质上是一个内部属性,而不是一个正式的对外的 API,只是由于浏览器广泛支持,才被加入了 ES6。标准明确规定,只有浏览器必须部署这个属性,其他运行环境不一定需要部署,而且新的代码最好认为这个属性是不存在的。所以不建议在生产中使用该属性,避免对环境产生依赖。生产环境中,我们可以使用 Object.getPrototypeOf() 方法来获取实例对象的原型,然后再来为原型添加方法/属性。

var p8 = new Person("Tom", 18);

var p9 = new Person("Jack", 22);

p8.__proto__.play = function (ball) {

console.log(`我喜欢玩${ball}`);

};

p8.play("足球"); // 我喜欢玩足球

p9.play("篮球"); // 我喜欢玩篮球

var p10 = new Person("Jerry", 20);

p10.play("乒乓球"); // 我喜欢玩乒乓球

上面代码在p8的原型上添加了一个play方法,由于p8的原型就是p9的原型,因此p9也可以调用这个方法。而且,此后新建的实例p10也可以调用这个方法。这意味着,使用实例的__proto__属性改写原型,必须相当谨慎,不推荐使用,因为这会改变class的初始定义方法,影响到所有实例。

总结

  • class类是基于原型的继承的语法糖;
  • class的出现使得代码更加优雅,代码量减少;
  • class类能清晰的指定构造函数和抽象方法;
  • class类在声明时就可以设定属性和方法,不需要再往函数的原型中添加方法;
  • 使用class类时必须实例化;
  • 使用extends关键字实现继承。

相关推荐

半导体行业术语缩写词典总结-JKL_半导体词汇缩写表

作为半导体行业新人来说,最痛苦的莫过于各种缩写词术语了,有的缩写词一样但是会有不同的解释。这里作者给大家整理了部分术语词典,后面会按照更新顺序一一分享出来。废话不多说,直接开始,如有遗漏,欢迎大家在评...

JD.com Deepens Push Into Embodied Intelligence With Investment in Sensor Maker PaXiniTech

ToraOne,thesecond-generationmultidimensionaltactilehumanoidrobotdevelopedbyPaXiniTechTMTPOS...

Hong Kong's Consumer Market Becomes New Battleground for Chinese Mainland Internet Giants

AI-generatedimageTMTPOST--StrollthroughthestreetsofHongKongtoday,anditmightfeellikey...

http2解决了哪些问题_简述http2的优点

HTTP/2(最初称为SPDY)是HTTP协议的第二个主要版本,它在HTTP/1.1的基础上进行了重大改进,旨在解决其在性能和效率方面的诸多瓶颈。以下是HTTP/2主要解决的问题:队头阻...

China's economy stays strong and vital amid pressure

Peoplevisitthe4thChina-CEECExpo&InternationalConsumerGoodsFairinNingbo,eastChina's...

JD.com Makes $2.4 Billion Bid for Ceconomy in Bold Push to Build a Global Retail Empire

TMTPOST--JD.comhasunveiledplanstoacquireGermany’sCeconomyAG—theparentofEurope’sleading...

深入剖析 Java 中的装饰器设计模式:原理、应用与实践

在Java软件开发的广阔天地里,设计模式犹如璀璨星辰,照亮我们构建高效、可维护系统的道路。今天,让我们聚焦于其中一颗闪耀的星——装饰器设计模式,深入探究它的奥秘,看看如何利用它为我们的代码赋予...

组合模式应用-适配器模式_适配器组件

写在前面Hello,我是易元,这篇文章是我学习设计模式时的笔记和心得体会。如果其中有错误,欢迎大家留言指正!该部分为各模式组合使用,涉及代码较多,熟能生巧。内容回顾定义适配器模式是一种结构型设计模式,...

OOM (Out Of Memory) 故障排查指南

1.确认OOM类型首先需要确认是哪种类型的OOM:JavaHeapOOM:Java堆内存不足NativeMemoryOOM:本地内存不足MetaspaceOOM:元空间内存不足Contai...

刷完这49题,面试官当场给Offer!Java程序员必备指南

1.问题:如果main方法被声明为private会怎样?答案:能正常编译,但运行的时候会提示”main方法不是public的”。2.问题:Java里的传引用和传值的区别是什么?答案:传引用是指传递的是...

C#编程基础(看这一篇就够了)_c#编程入门与应用

C#及其开发环境简介C#概述C#是一个现代的、通用的、面向对象的编程语言,由微软(Microsoft)开发,经Ecma和ISO核准认可。它由AndersHejlsberg和他的团队在.NET框架开发...

说一下JDK的监控和 线上处理的一些case

一句话总结JDK监控常用工具包括JConsole、VisualVM、JMC等,用于实时查看内存、线程、GC状态。线上常见问题处理:内存泄漏通过heapdump分析对象引用链;频繁GC可调整-Xmx/...

JavaScript深拷贝极简指南:3种方法解决嵌套与循环引用难题

为什么需要深拷贝?首先我们看看浅拷贝,point指向的是同一个地址,这时我们修改obj2.point的属性时,obj1的point属性也会被修改再看看深拷贝,point指向的是不同地址,这时我们修改o...

Java 25 在 JEP 519 中集成了紧凑对象头

作者|ANMBazlurRahman译者|刘雅梦策划|丁晓昀Java25通过JEP519将紧凑对象头作为产品特性进行了集成,在不需要更改任何代码的情况下,为开发人员提供了...

每日一练 Python 面试题(1)_python每日一记

以下是5道Python基本语法相关的面试题,涵盖变量、运算符、数据结构、函数和异常处理等核心概念:1.变量与作用域题目:以下代码的输出是什么?解释原因。x=10deffunc():...