ES6 Class关键字详解:从原型链到现代类定义
本文详细解析ES6中class关键字的核心特性,包括其作为构造函数包装的本质、constructor方法的默认行为、严格模式的强制应用以及私有属性和私有方法的模拟实现方案。重点探讨this在类方法中的指向问题及解决方案,并对比Class表达式与变量提升机制,帮助开发者掌握现代JavaScript面向对象编程的关键规范。
ES6 Class关键字概述
class关键字是ES6标准引入的核心特性之一,它极大地简化了JavaScript中面向对象编程的语法结构。在ES6出现之前,开发者主要依赖原型链机制来模拟类的行为,这种方式虽然灵活但较为晦涩。
Class基础与语法规范
1、基础介绍
//定义类
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
以Point类为例,该类不仅包含构造方法,还定义了一个toString方法。在定义类的方法时,无需添加function关键字,直接将函数体放入类定义块中即可。需要注意的是,各个方法之间不需要使用逗号进行分隔,若错误地添加逗号,代码执行时会抛出语法错误。
//类的数据类型就是函数,类本身就指向构造函数。用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。
class Bar {
doStuff() {
console.log('stuff');
}
}
var b = new Bar();
b.doStuff() // "stuff"
严格模式与Constructor方法
2、严格模式
类定义内部以及模块作用域内,默认强制启用严格模式,因此无需显式使用'use strict'指令。只要代码编写在class或模块结构中,运行环境即自动处于严格模式下,这有助于避免一些常见的JavaScript陷阱。
3、constructor方法
constructor是类的默认构造方法,当使用new命令创建对象实例时,该方法会被自动调用。每个类都必须包含constructor方法,如果开发者未显式定义,解释器会默认添加一个空的constructor方法,以确保类的实例化过程正常进行。
class Point {
}
// 等同于
class Point {
constructor() {}//constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象。
}
//注意:类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。
实例对象与属性定义
4、实例对象生成
生成类的实例需使用new命令,这是创建对象的标准方式。
实例属性的归属取决于定义位置:若属性显式定义在实例本身(即this对象上),则属于实例属性;否则,属性默认定义在原型链上(即class定义中),所有实例共享这些属性。
class Point {
// ...
}
// 报错
var point = Point(2, 3);
// 正确
var point = new Point(2, 3);
Class表达式与变量提升
5、Class表达式
可以通过表达式形式定义类,例如将类赋值给变量。需注意,此类表达式的名称(如Me)仅在类内部代码中可用,用于指代当前类,而在外部访问时,应使用赋值给的变量名(如MyClass)。
const MyClass = class Me {
getClassName() {
return Me.name;
}
};
//采用 Class 表达式,可以写出立即执行的 Class。
var person = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}('张三');
person.sayName(); // "张三"
6、变量提升机制
与函数不同,类声明不存在变量提升(hoisting)现象。
若尝试在类定义之前使用该类(如调用new Foo()而Foo类定义在后),代码将抛出ReferenceError。这是因为ES6规定类的声明不会提升到代码头部,这一设计旨在确保子类在定义时,其父类已经存在,从而保证继承关系的正确性。
new Foo(); // ReferenceError
class Foo {}
私有方法与属性实现
7、私有方法模拟
ES6标准本身并未直接提供私有方法的语法支持,但可通过变通方案模拟实现私有行为。
常见的私有方法实现策略包括以下两种:
第一种是通过命名约定进行区分,通常以_(下划线)开头的方法名暗示其为私有方法,尽管这并非真正的私有,仅是开发者的约定。
第二种是利用Symbol值的唯一性,将私有方法名定义为Symbol值,从而在类外部无法通过常规方式访问该方法,实现更严格的私有控制。
/*命名上加以区别*/
class Widget {
// 公有方法
foo (baz) {
this._bar(baz);
}
// 私有方法
_bar(baz) {
return this.snaf = baz;
}
// ...
}
/*利用Symbol*/
const bar = Symbol('bar');
const snaf = Symbol('snaf');
export default class myClass{
// 公有方法
foo(baz) {
this[bar](baz);
}
// 私有方法
[bar](baz) {
return this[snaf] = baz;
}
// ...
};
8、私有属性提案
与私有方法类似,ES6标准初期也不支持私有属性。目前已有相关提案,通过在属性名或方法名前添加#符号来表示私有成员,这是目前实现真正私有属性的主流语法方案。
class Point {
#x;
constructor(x = 0) {
#x = +x; // 写成 this.#x 亦可
}
get x() { return #x }
set x(value) { #x = +value }
}
This指向与Name属性
9、this指向问题
在类的方法内部,this关键字默认指向该类的实例。然而,如果将某个方法单独提取出来使用(例如作为回调函数或解构后调用),this的指向可能会发生改变,指向调用时的上下文环境,这可能导致找不到预期方法而报错。
class Logger {
printName(name = 'there') {
this.print(`Hello ${name}`);
}
print(text) {
console.log(text);
}
}
const logger = new Logger();
const { printName } = logger;
printName();
//上面代码中,printName方法中的this,默认指向Logger类的实例。但是,如果将这个方法提取出来单独使用,this会指向该方法运行时所在的环境,因为找不到print方法而导致报错。
/*解决方法是,在构造方法中绑定this,这样就不会找不到print方法了。*/
class Logger {
constructor() {
this.printName = this.printName.bind(this);
}
// ...
}
10、name属性
由于ES6的类本质上是ES5构造函数的语法糖,它继承了函数的许多特性,其中包括name属性。name属性始终返回紧跟在class关键字后的类名,这有助于调试和识别类的身份。
class Point {}
Point.name // "Point"
总结
以上内容涵盖了ES6中class关键字的主要特性和使用注意事项,旨在帮助开发者更好地理解和应用现代JavaScript的面向对象编程特性。
JS大数值转字符串出现科学计数法怎么办
2026-11-05 17:09:21
JS split切割后出现空字符串怎么过滤
2026-11-05 16:00:30
在 React 或 Vue 项目中封装使用 JS 节流防抖
2026-11-05 15:27:29
JavaScript中怎么将带小数的字符串转为整数?
2026-11-05 14:32:38
怎么用JavaScript策略模式重构电商促销计算逻辑?
2026-11-05 14:01:09
