各位观众,早上好(或者下午好,晚上好,取决于你正在哪个时区摸鱼)。今天咱们来聊聊 JavaScript 里那些“静态”的家伙们——static
属性和方法。他们就像类里面的老干部,不属于任何具体的实例,而是属于类本身,专门负责处理一些类级别的任务。
开场白:静态是个啥?
想象一下,你开了一家包子铺。每个包子(也就是类的实例)都有自己的馅儿和皮儿。但有些东西是属于整个包子铺的,比如包子铺的名字、包子铺的地址、以及计算所有包子总销量的函数。这些不属于任何一个单独的包子,而是属于整个包子铺。在 JavaScript 的世界里,这些就是 static
属性和方法。
static
属性:类的常量与配置
static
属性通常用于定义一些与类相关的常量或者配置信息。这些信息对于类的所有实例都是通用的,而且通常不会被修改。
举个栗子,咱们来定义一个 MathUtils
类,里面放一些数学相关的常量:
class MathUtils {
static PI = 3.14159265359;
static E = 2.71828182846;
static VERSION = "1.0.0"; // 顺便加个版本号
static getPiRounded() {
return Math.round(MathUtils.PI);
}
}
console.log(MathUtils.PI); // 输出: 3.14159265359
console.log(MathUtils.E); // 输出: 2.71828182846
console.log(MathUtils.VERSION); // 输出: 1.0.0
console.log(MathUtils.getPiRounded()); //输出: 3
在这个例子中,PI
、E
和 VERSION
都是 MathUtils
类的 static
属性。你可以直接通过 MathUtils.PI
、MathUtils.E
和 MathUtils.VERSION
来访问它们,而不需要创建 MathUtils
的实例。getPiRounded
是一个静态方法, 用于返回圆周率的近似整数值。
static
方法:类的工具函数
static
方法通常用于定义一些与类相关的工具函数。这些函数不需要访问类的实例属性,只需要处理一些与类相关的逻辑。
继续用包子铺的例子,我们可以定义一个 Baozi
类,然后定义一个 static
方法来计算所有包子的总成本:
class Baozi {
constructor(xian, pi) {
this.xian = xian;
this.pi = pi;
}
}
class BaoziShop {
static baoziCount = 0;
static baoziPrice = 2; //每个包子2块钱
static calculateTotalRevenue(baoziSold) {
return baoziSold * BaoziShop.baoziPrice;
}
static addBaozi() {
BaoziShop.baoziCount++;
}
static getBaoziCount() {
return BaoziShop.baoziCount;
}
}
BaoziShop.addBaozi();
BaoziShop.addBaozi();
BaoziShop.addBaozi();
console.log(`一共卖了 ${BaoziShop.getBaoziCount()} 个包子,收入 ${BaoziShop.calculateTotalRevenue(BaoziShop.getBaoziCount())} 元`) //一共卖了 3 个包子,收入 6 元
在这个例子中,calculateTotalRevenue
是一个 static
方法。它接收一个参数 baoziSold
,表示卖出的包子数量,然后计算总收入。你可以直接通过 BaoziShop.calculateTotalRevenue(100)
来调用这个方法,而不需要创建 BaoziShop
的实例。
static
方法与 this
需要注意的是,在 static
方法中,this
指向的是类本身,而不是类的实例。因此,你不能在 static
方法中访问类的实例属性。
class MyClass {
constructor(name) {
this.name = name;
}
static myStaticMethod() {
//console.log(this.name); // TypeError: Cannot read properties of undefined (reading 'name')
console.log("This is a static method.");
}
}
MyClass.myStaticMethod(); // 输出: This is a static method.
在这个例子中,如果在 myStaticMethod
中尝试访问 this.name
,会报错,因为 this
指向的是 MyClass
类本身,而不是类的实例。
static
块:静态初始化
ES2022 引入了 static
块,它允许你在类定义中执行一些静态初始化操作。这对于一些复杂的静态属性初始化非常有用。
class MyClass {
static {
// 在这里执行静态初始化操作
MyClass.staticProperty = this.calculateInitialValue();
console.log("Static initialization complete.");
}
static calculateInitialValue() {
// 一些复杂的计算逻辑
return Math.random();
}
}
console.log(MyClass.staticProperty); // 输出一个随机数,并显示 "Static initialization complete."
在这个例子中,static
块会在类定义时执行,并初始化 MyClass.staticProperty
。这对于一些需要复杂计算才能得到的静态属性非常方便。
static
属性与方法的使用场景
- 常量和配置信息: 定义一些与类相关的常量或者配置信息,例如数学常量、API 地址、版本号等。
- 工具函数: 定义一些与类相关的工具函数,例如字符串处理、日期格式化、数据校验等。
- 单例模式: 使用
static
属性和方法来实现单例模式,保证一个类只有一个实例。 - 工厂方法: 使用
static
方法来实现工厂方法,根据不同的参数创建不同的对象。
代码示例:单例模式
单例模式是一种常用的设计模式,它保证一个类只有一个实例,并提供一个全局访问点。可以使用 static
属性和方法来实现单例模式。
class Singleton {
static instance = null;
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
this.data = "Singleton instance";
Singleton.instance = this;
}
getData() {
return this.data;
}
}
const instance1 = new Singleton();
const instance2 = new Singleton();
console.log(instance1 === instance2); // 输出: true
console.log(instance1.getData()); // 输出: Singleton instance
console.log(instance2.getData()); // 输出: Singleton instance
在这个例子中,Singleton.instance
是一个 static
属性,用于保存单例实例。构造函数首先检查 Singleton.instance
是否已经存在,如果存在,则直接返回已存在的实例;否则,创建一个新的实例,并将其赋值给 Singleton.instance
。这样就保证了 Singleton
类只有一个实例。
代码示例:工厂方法
工厂方法是一种创建型设计模式,它定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。可以使用 static
方法来实现工厂方法。
class Product {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class ProductFactory {
static createProduct(type) {
switch (type) {
case "A":
return new Product("Product A");
case "B":
return new Product("Product B");
default:
return new Product("Unknown Product");
}
}
}
const productA = ProductFactory.createProduct("A");
const productB = ProductFactory.createProduct("B");
const productC = ProductFactory.createProduct("C");
console.log(productA.getName()); // 输出: Product A
console.log(productB.getName()); // 输出: Product B
console.log(productC.getName()); // 输出: Unknown Product
在这个例子中,ProductFactory.createProduct
是一个 static
方法,它根据传入的类型参数创建不同的 Product
对象。这使得创建对象的过程更加灵活,并且可以隐藏对象的创建细节。
静态方法与普通方法的区别
为了更清晰地理解 static
方法和普通方法的区别,我们来看一个表格:
特性 | static 方法 |
普通方法 |
---|---|---|
访问方式 | 通过类名直接访问(例如:MyClass.myMethod() ) |
通过类的实例访问(例如:instance.myMethod() ) |
this 指向 |
类本身 | 类的实例 |
访问权限 | 只能访问 static 属性和方法 |
可以访问 static 和实例属性和方法 |
使用场景 | 工具函数、常量、单例模式、工厂方法等 | 操作实例属性、执行实例相关逻辑等 |
高级用法:继承中的 static
static
属性和方法也可以被继承。子类可以访问父类的 static
属性和方法,也可以覆盖父类的 static
方法。
class Parent {
static myStaticProperty = "Parent Static Property";
static myStaticMethod() {
console.log("Parent Static Method");
}
}
class Child extends Parent {
static myStaticMethod() {
console.log("Child Static Method");
super.myStaticMethod(); // 调用父类的 static 方法
}
}
console.log(Child.myStaticProperty); // 输出: Parent Static Property
Child.myStaticMethod(); // 输出: Child Static Method,然后输出: Parent Static Method
在这个例子中,Child
类继承了 Parent
类,并且覆盖了 myStaticMethod
方法。在 Child.myStaticMethod
中,可以使用 super.myStaticMethod()
来调用父类的 static
方法。
最佳实践
- 谨慎使用
static
: 只有在确实需要类级别的属性和方法时才使用static
。过度使用static
可能会导致代码难以测试和维护。 - 避免在
static
方法中访问实例属性:static
方法不应该依赖于类的实例状态。 - 使用
static
块进行复杂的静态初始化: 对于一些需要复杂计算才能得到的静态属性,可以使用static
块进行初始化。 - 合理利用继承中的
static
: 在继承中,可以利用static
属性和方法来实现一些通用的逻辑。
总结
static
属性和方法是 JavaScript 中非常有用的特性,它们允许你定义类级别的工具函数和常量。合理使用 static
可以使你的代码更加简洁、高效和易于维护。记住,static
就像类里面的老干部,不属于任何具体的实例,而是属于类本身,专门负责处理一些类级别的任务。
好了,今天的讲座就到这里。希望大家能够掌握 static
属性和方法的使用,并在实际开发中灵活运用。如果大家还有什么问题,欢迎随时提问。下次再见!