静态方法与属性
2025年3月7日大约 11 分钟
静态方法与属性
静态成员是属于类本身而不是实例的属性和方法。本文将介绍如何使用static关键字定义静态方法和属性,以及它们的常见应用场景,如工厂方法、辅助函数等。
静态方法基础
定义静态方法
在ES6中,使用static
关键字定义静态方法:
class Calculator {
// 实例方法
add(a, b) {
return a + b;
}
// 静态方法
static multiply(a, b) {
return a * b;
}
}
// 调用静态方法(通过类名调用)
console.log(Calculator.multiply(5, 3)); // 输出:15
// 实例方法需要先创建实例
const calc = new Calculator();
console.log(calc.add(5, 3)); // 输出:8
// 错误:实例不能调用静态方法
// console.log(calc.multiply(5, 3)); // TypeError: calc.multiply is not a function
// 错误:类不能直接调用实例方法
// console.log(Calculator.add(5, 3)); // TypeError: Calculator.add is not a function
静态方法中的this
在静态方法中,this
指向类本身,而不是实例:
class Counter {
static count = 0;
static increment() {
this.count++; // this指向Counter类
return this.count;
}
static decrement() {
this.count--; // this指向Counter类
return this.count;
}
static getCount() {
return this.count;
}
}
console.log(Counter.increment()); // 输出:1
console.log(Counter.increment()); // 输出:2
console.log(Counter.decrement()); // 输出:1
console.log(Counter.getCount()); // 输出:1
静态方法的继承
子类会继承父类的静态方法:
class MathUtils {
static add(a, b) {
return a + b;
}
static multiply(a, b) {
return a * b;
}
}
class AdvancedMathUtils extends MathUtils {
static subtract(a, b) {
return a - b;
}
static divide(a, b) {
if (b === 0) throw new Error('除数不能为0');
return a / b;
}
// 可以重写父类的静态方法
static multiply(a, b) {
console.log('使用高级乘法');
return super.multiply(a, b); // 调用父类的静态方法
}
}
// 使用继承的静态方法
console.log(AdvancedMathUtils.add(5, 3)); // 输出:8
console.log(AdvancedMathUtils.multiply(5, 3)); // 输出:使用高级乘法 15
// 使用自己的静态方法
console.log(AdvancedMathUtils.subtract(5, 3)); // 输出:2
console.log(AdvancedMathUtils.divide(6, 2)); // 输出:3
静态属性
定义静态属性
在现代JavaScript中,可以直接在类内部使用static
关键字定义静态属性:
class Config {
// 静态属性
static API_URL = 'https://api.example.com';
static TIMEOUT = 3000;
static DEBUG = false;
static getApiUrl() {
return this.API_URL;
}
}
console.log(Config.API_URL); // 输出:https://api.example.com
console.log(Config.TIMEOUT); // 输出:3000
console.log(Config.getApiUrl()); // 输出:https://api.example.com
// 修改静态属性
Config.DEBUG = true;
console.log(Config.DEBUG); // 输出:true
在旧版本JavaScript中,静态属性通常在类定义外部添加:
class OldConfig {
static getApiUrl() {
return OldConfig.API_URL;
}
}
// 在类定义外部添加静态属性
OldConfig.API_URL = 'https://api.example.com';
OldConfig.TIMEOUT = 3000;
console.log(OldConfig.API_URL); // 输出:https://api.example.com
私有静态属性和方法
使用#
前缀可以定义私有静态属性和方法:
class SecureConfig {
// 私有静态属性
static #API_KEY = 'secret-key-12345';
static #instance = null;
// 公共静态属性
static API_URL = 'https://api.example.com';
// 私有静态方法
static #validateKey(key) {
return key === SecureConfig.#API_KEY;
}
// 公共静态方法
static getApiKey(password) {
if (password === 'admin') {
return SecureConfig.#API_KEY;
}
return null;
}
static isValidKey(key) {
return SecureConfig.#validateKey(key);
}
// 单例模式
static getInstance() {
if (!SecureConfig.#instance) {
SecureConfig.#instance = new SecureConfig();
}
return SecureConfig.#instance;
}
}
console.log(SecureConfig.API_URL); // 输出:https://api.example.com
console.log(SecureConfig.getApiKey('admin')); // 输出:secret-key-12345
console.log(SecureConfig.isValidKey('secret-key-12345')); // 输出:true
// 错误:无法直接访问私有静态属性
// console.log(SecureConfig.#API_KEY); // SyntaxError
// 单例模式
const instance1 = SecureConfig.getInstance();
const instance2 = SecureConfig.getInstance();
console.log(instance1 === instance2); // 输出:true
静态方法的应用场景
1. 工厂方法
静态方法常用于创建对象的工厂方法:
class User {
constructor(name, role, permissions) {
this.name = name;
this.role = role;
this.permissions = permissions;
this.createdAt = new Date();
}
hasPermission(permission) {
return this.permissions.includes(permission);
}
// 工厂方法
static createAdmin(name) {
return new User(name, 'admin', ['read', 'write', 'delete', 'manage']);
}
static createEditor(name) {
return new User(name, 'editor', ['read', 'write']);
}
static createViewer(name) {
return new User(name, 'viewer', ['read']);
}
static createGuest() {
return new User('Guest', 'guest', ['read']);
}
}
// 使用工厂方法创建不同类型的用户
const admin = User.createAdmin('张三');
const editor = User.createEditor('李四');
const viewer = User.createViewer('王五');
const guest = User.createGuest();
console.log(admin.role); // 输出:admin
console.log(editor.hasPermission('write')); // 输出:true
console.log(viewer.hasPermission('delete')); // 输出:false
2. 辅助函数/工具方法
静态方法适合用作与类相关的辅助函数:
class StringUtils {
static capitalize(str) {
if (!str) return str;
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
}
static reverse(str) {
return str.split('').reverse().join('');
}
static countWords(str) {
return str.trim().split(/\s+/).length;
}
static truncate(str, length, suffix = '...') {
if (str.length <= length) return str;
return str.slice(0, length) + suffix;
}
}
console.log(StringUtils.capitalize('hello')); // 输出:Hello
console.log(StringUtils.reverse('hello')); // 输出:olleh
console.log(StringUtils.countWords('hello world')); // 输出:2
console.log(StringUtils.truncate('hello world', 5)); // 输出:hello...
3. 单例模式
静态属性和方法可以用于实现单例模式:
class Database {
static #instance = null;
constructor() {
if (Database.#instance) {
throw new Error('数据库实例已存在,请使用getInstance()方法');
}
this.connected = false;
Database.#instance = this;
}
connect() {
this.connected = true;
console.log('数据库已连接');
}
query(sql) {
if (!this.connected) {
throw new Error('请先连接数据库');
}
console.log(`执行查询: ${sql}`);
}
static getInstance() {
if (!Database.#instance) {
Database.#instance = new Database();
}
return Database.#instance;
}
}
// 使用单例模式
const db1 = Database.getInstance();
db1.connect();
const db2 = Database.getInstance();
console.log(db1 === db2); // 输出:true
db2.query('SELECT * FROM users');
// 错误:不能直接创建新实例
// const db3 = new Database(); // Error: 数据库实例已存在,请使用getInstance()方法
4. 缓存和记忆化
静态属性可以用于缓存计算结果:
class Fibonacci {
static #cache = {
0: 0,
1: 1
};
static calculate(n) {
// 检查缓存
if (n in this.#cache) {
console.log(`从缓存获取 fib(${n})`);
return this.#cache[n];
}
console.log(`计算 fib(${n})`);
// 计算并缓存结果
const result = this.calculate(n - 1) + this.calculate(n - 2);
this.#cache[n] = result;
return result;
}
static clearCache() {
this.#cache = {
0: 0,
1: 1
};
}
}
console.log(Fibonacci.calculate(5)); // 计算并缓存中间结果
console.log(Fibonacci.calculate(5)); // 直接从缓存获取
5. 常量和配置
静态属性适合用于定义常量和配置:
class AppConfig {
// 环境常量
static ENV = {
DEV: 'development',
PROD: 'production',
TEST: 'testing'
};
// 当前环境
static CURRENT_ENV = AppConfig.ENV.DEV;
// API配置
static API = {
BASE_URL: 'https://api.example.com',
TIMEOUT: 5000,
RETRY_COUNT: 3
};
// 功能开关
static FEATURES = {
NEW_UI: true,
ANALYTICS: true,
DARK_MODE: false
};
static isProduction() {
return this.CURRENT_ENV === this.ENV.PROD;
}
static getApiUrl(endpoint) {
return `${this.API.BASE_URL}/${endpoint}`;
}
}
// 使用配置
if (AppConfig.isProduction()) {
console.log('生产环境');
} else {
console.log('开发环境'); // 输出:开发环境
}
console.log(AppConfig.getApiUrl('users')); // 输出:https://api.example.com/users
// 根据功能开关启用功能
if (AppConfig.FEATURES.DARK_MODE) {
console.log('启用暗黑模式');
} else {
console.log('使用默认模式'); // 输出:使用默认模式
}
静态方法与实例方法的对比
何时使用静态方法
- 不需要访问实例状态:方法不需要访问或修改实例的属性
- 工具函数:方法是与类相关的通用工具
- 工厂方法:创建特定类型的实例
- 单例访问:提供对单例的访问
何时使用实例方法
- 需要访问实例状态:方法需要访问或修改实例的属性
- 对象行为:方法表示对象的行为
- 多态性:方法在不同子类中有不同的实现
class Shape {
constructor(color) {
this.color = color;
}
// 实例方法 - 需要访问实例状态
getColor() {
return this.color;
}
// 实例方法 - 在子类中有不同实现
calculateArea() {
throw new Error('子类必须实现calculateArea方法');
}
// 静态方法 - 工厂方法
static createCircle(radius, color) {
return new Circle(radius, color);
}
static createRectangle(width, height, color) {
return new Rectangle(width, height, color);
}
// 静态方法 - 工具函数
static isValidColor(color) {
const validColors = ['red', 'green', 'blue', 'yellow', 'black', 'white'];
return validColors.includes(color);
}
}
class Circle extends Shape {
constructor(radius, color) {
super(color);
this.radius = radius;
}
// 实现父类的抽象方法
calculateArea() {
return Math.PI * this.radius * this.radius;
}
}
class Rectangle extends Shape {
constructor(width, height, color) {
super(color);
this.width = width;
this.height = height;
}
// 实现父类的抽象方法
calculateArea() {
return this.width * this.height;
}
}
// 使用实例方法
const circle = new Circle(5, 'red');
console.log(circle.getColor()); // 输出:red
console.log(circle.calculateArea()); // 输出:78.53981633974483
// 使用静态方法
const rectangle = Shape.createRectangle(4, 6, 'blue');
console.log(rectangle.calculateArea()); // 输出:24
console.log(Shape.isValidColor('green')); // 输出:true
console.log(Shape.isValidColor('purple')); // 输出:false
静态方法与属性的最佳实践
1. 命名约定
静态成员通常使用更具描述性的名称,有时使用大写字母表示常量:
class DateUtils {
// 常量使用全大写
static DAYS_IN_WEEK = 7;
static MONTHS_IN_YEAR = 12;
// 工具方法使用动词开头
static formatDate(date, format) {
// 实现格式化逻辑
}
// 工厂方法通常使用create或from开头
static fromISOString(isoString) {
return new Date(isoString);
}
// 转换方法通常使用to开头
static toUTCDate(localDate) {
// 转换为UTC日期
}
}
2. 避免过度使用静态方法
不要将所有方法都设为静态方法,这会导致面向过程而非面向对象的编程风格:
// 不推荐:过度使用静态方法
class UserManager {
static createUser(data) { /* ... */ }
static updateUser(id, data) { /* ... */ }
static deleteUser(id) { /* ... */ }
static findUserById(id) { /* ... */ }
static validateUserData(data) { /* ... */ }
}
// 推荐:合理使用实例方法和静态方法
class User {
constructor(data) {
this.id = data.id;
this.name = data.name;
this.email = data.email;
}
// 实例方法
update(data) { /* ... */ }
delete() { /* ... */ }
// 静态方法
static create(data) { /* ... */ }
static findById(id) { /* ... */ }
static validate(data) { /* ... */ }
}
3. 组织相关功能
将相关的静态方法和属性组织在同一个类中:
// 不推荐:分散的工具函数
function formatCurrency(amount) { /* ... */ }
function parseAmount(str) { /* ... */ }
const CURRENCY_SYMBOLS = { USD: '$', EUR: '€', GBP: '£' };
// 推荐:组织在类中
class CurrencyUtils {
static SYMBOLS = {
USD: '$',
EUR: '€',
GBP: '£'
};
static format(amount, currency = 'USD') {
const symbol = this.SYMBOLS[currency] || '';
return `${symbol}${amount.toFixed(2)}`;
}
static parse(str) {
return parseFloat(str.replace(/[^\d.-]/g, ''));
}
static convert(amount, fromCurrency, toCurrency, rates) {
// 货币转换逻辑
}
}
4. 使用静态方法代替全局函数
静态方法比全局函数更有组织性,并且不会污染全局命名空间:
// 不推荐:全局函数
function validateEmail(email) { /* ... */ }
function validatePassword(password) { /* ... */ }
function validateUsername(username) { /* ... */ }
// 推荐:静态方法
class Validator {
static email(email) { /* ... */ }
static password(password) { /* ... */ }
static username(username) { /* ... */ }
// 组合验证
static validateUserData(data) {
return {
email: this.email(data.email),
password: this.password(data.password),
username: this.username(data.username)
};
}
}
// 使用
const validationResult = Validator.validateUserData({
email: 'user@example.com',
password: 'password123',
username: 'user123'
});
实际应用示例
1. 数据模型与ORM
class Model {
constructor(attributes = {}) {
this.attributes = { ...attributes };
this.errors = {};
}
get(key) {
return this.attributes[key];
}
set(key, value) {
this.attributes[key] = value;
return this;
}
toJSON() {
return { ...this.attributes };
}
validate() {
this.errors = {};
return Object.keys(this.errors).length === 0;
}
// 静态方法 - 数据库操作
static async findAll() {
const response = await fetch(`${this.baseUrl}`);
const data = await response.json();
return data.map(item => new this(item));
}
static async findById(id) {
const response = await fetch(`${this.baseUrl}/${id}`);
const data = await response.json();
return new this(data);
}
static async create(attributes) {
const instance = new this(attributes);
if (!instance.validate()) {
throw new Error('验证失败');
}
const response = await fetch(this.baseUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(instance.toJSON())
});
const data = await response.json();
return new this(data);
}
}
// 用户模型
class User extends Model {
// 静态属性 - API端点
static baseUrl = 'https://api.example.com/users';
// 静态属性 - 角色常量
static ROLES = {
ADMIN: 'admin',
USER: 'user',
GUEST: 'guest'
};
constructor(attributes = {}) {
super(attributes);
this.set('createdAt', attributes.createdAt || new Date());
}
// 实例方法
validate() {
super.validate();
const email = this.get('email');
const password = this.get('password');
if (!email) {
this.errors.email = '邮箱不能为空';
} else if (!/\S+@\S+\.\S+/.test(email)) {
this.errors.email = '邮箱格式不正确';
}
if (!password) {
this.errors.password = '密码不能为空';
} else if (password.length < 6) {
this.errors.password = '密码长度不能小于6位';
}
return Object.keys(this.errors).length === 0;
}
// 静态方法 - 特定查询
static async findByEmail(email) {
const users = await this.findAll();
return users.find(user => user.get('email') === email);
}
// 静态方法 - 工厂方法
static createAdmin(email, password) {
return new User({
email,
password,
role: User.ROLES.ADMIN
});
}
}
// 使用示例
async function main() {
// 创建用户
const user = await User.create({
email: 'user@example.com',
password: 'password123',
role: User.ROLES.USER
});
// 查找用户
const admin = await User.findByEmail('admin@example.com');
// 使用工厂方法
const newAdmin = User.createAdmin('newadmin@example.com', 'admin123');
await User.create(newAdmin.toJSON());
}
2. 日志系统
class Logger {
// 日志级别
static LEVELS = {
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3,
FATAL: 4
};
// 当前日志级别
static currentLevel = Logger.LEVELS.INFO;
// 日志格式化器
static formatters = {
simple: (level, message) => `[${level}] ${message}`,
detailed: (level, message) => {
const date = new Date().toISOString();
return `[${date}] [${level}] ${message}`;
}
};
// 当前格式化器
static currentFormatter = 'detailed';
// 日志输出目标
static targets = [];
// 添加日志输出目标
static addTarget(target) {
if (typeof target.log === 'function') {
Logger.targets.push(target);
} else {
throw new Error('日志目标必须实现log方法');
}
}
// 设置日志级别
static setLevel(level) {
if (level in Logger.LEVELS) {
Logger.currentLevel = Logger.LEVELS[level];
}
}
// 日志方法
static log(level, message) {
const levelValue = Logger.LEVELS[level];
if (levelValue >= Logger.currentLevel) {
const formatter = Logger.formatters[Logger.currentFormatter];
const formattedMessage = formatter(level, message);
// 输出到所有目标
Logger.targets.forEach(target => target.log(formattedMessage));
return true;
}
return false;
}
// 便捷方法
static debug(message) {
return Logger.log('DEBUG', message);
}
static info(message) {
return Logger.log('INFO', message);
}
static warn(message) {
return Logger.log('WARN', message);
}
static error(message) {
return Logger.log('ERROR', message);
}
static fatal(message) {
return Logger.log('FATAL', message);
}
}
// 控制台日志目标
class ConsoleTarget {
log(message) {
console.log(message);
}
}
// 文件日志目标
class FileTarget {
constructor(filename) {
this.filename = filename;
}
log(message) {
// 在实际应用中,这里会将日志写入文件
console.log(`[写入文件 ${this.filename}] ${message}`);
}
}
// 配置日志系统
Logger.addTarget(new ConsoleTarget());
Logger.addTarget(new FileTarget('app.log'));
Logger.setLevel('DEBUG');
// 使用日志系统
Logger.debug('这是一条调试信息');
Logger.info('应用已启动');
Logger.warn('磁盘空间不足');
Logger.error('无法连接到数据库');
总结
静态方法和属性是JavaScript面向对象编程中的重要特性,通过本文,我们学习了:
- 静态方法基础:如何使用
static
关键字定义静态方法,静态方法中的this
指向,以及静态方法的继承 - 静态属性:如何定义和使用静态属性,包括私有静态属性和方法
- 静态方法的应用场景:工厂方法、辅助函数、单例模式、缓存和记忆化、常量和配置
- 静态方法与实例方法的对比:何时使用静态方法,何时使用实例方法
- 最佳实践:命名约定、避免过度使用静态方法、组织相关功能、使用静态方法代替全局函数
- 实际应用示例:数据模型与ORM、日志系统
静态方法和属性为我们提供了一种组织与类相关但不依赖于特定实例的功能的方式。合理使用静态成员可以使代码更加模块化、可维护,并减少全局命名空间的污染。