Stampit 开源项目教程:JavaScript 组合式对象工厂的革命性实践
引言:告别传统继承,拥抱组合的力量
你是否曾为 JavaScript 中的继承问题而头疼?复杂的原型链、难以维护的类层次结构、私有状态管理的困境...这些痛点让面向对象编程(Object-Oriented Programming,OOP)在 JavaScript 中变得异常复杂。
Stampit 的出现彻底改变了这一局面。这个仅 1.3KB gzipped 的轻量级库,通过组合式对象工厂(Composable Object Factories)的概念,为 JavaScript 开发者带来了前所未有的灵活性和强大功能。
读完本文,你将掌握:
- ✅ Stampit 的核心概念和工作原理
- ✅ 三种原型继承模式的实战应用
- ✅ 组合式设计的优势与最佳实践
- ✅ 实际项目中的高级技巧和模式
- ✅ 与传统 OOP 的对比和迁移策略
什么是 Stampit?
Stampit 是一个基于 Stamp 规范 的 JavaScript 库,它提供了三种原型继承模式:
- 委托继承(Delegation) - 通过原型链实现方法共享
- 拼接继承(Concatenation) - 通过对象合并实现属性复制
- 函数式继承(Functional) - 通过闭包实现私有状态和特权方法
核心概念对比表
| 特性 | 传统类继承 | Stampit 组合 |
|---|---|---|
| 耦合度 | 高(父子强耦合) | 低(行为解耦) |
| 灵活性 | 有限(单继承) | 极高(任意组合) |
| 私有状态 | 困难(需约定) | 简单(闭包实现) |
| 代码复用 | 继承层次 | 行为组合 |
| 维护成本 | 高(牵一发而动全身) | 低(独立演进) |
快速入门:从零开始使用 Stampit
安装与引入
npm install stampit
// ESM 方式引入
import stampit from 'stampit'
// CommonJS 方式引入
const stampit = require('stampit')
基础示例:创建你的第一个 Stamp
// 定义基础角色 Stamp
const Character = stampit({
props: {
name: null,
health: 100
},
init({ name = this.name }) {
this.name = name
}
})
// 创建战士行为
const Fighter = stampit({
props: {
stamina: 100
},
methods: {
fight() {
console.log(`${this.name}发动攻击!`)
this.stamina--
}
}
})
// 创建法师行为
const Mage = stampit({
props: {
mana: 100
},
methods: {
cast() {
console.log(`${this.name}施放法术!`)
this.mana--
}
}
})
// 组合创建圣骑士
const Paladin = stampit(Character, Fighter, Mage)
// 实例化对象
const hero = Paladin({ name: '罗兰' })
hero.fight() // 罗兰发动攻击!
hero.cast() // 罗兰施放法术!
核心 API 深度解析
1. 属性管理:props vs deepProps
const Configurable = stampit({
props: {
// 浅拷贝属性(引用共享)
settings: { theme: 'dark' }
},
deepProps: {
// 深拷贝属性(独立副本)
config: { timeout: 3000 }
}
})
const obj1 = Configurable()
const obj2 = Configurable()
obj1.settings.theme = 'light' // 会影响 obj2.settings.theme
obj1.config.timeout = 5000 // 不会影响 obj2.config.timeout
2. 方法定义与原型委托
const Logger = stampit({
methods: {
// 方法定义在原型上,所有实例共享
log(message) {
console.log(`[${this.name}]: ${message}`)
},
error(message) {
console.error(`[ERROR][${this.name}]: ${message}`)
}
}
})
3. 初始化器:init 函数的强大能力
const SecureStorage = stampit({
init(opts, { instance }) {
// 创建真正的私有状态
let privateData = new Map()
// 特权方法:访问私有数据
instance.set = (key, value) => {
if (typeof key !== 'string') {
throw new Error('Key must be a string')
}
privateData.set(key, value)
return instance
}
instance.get = (key) => {
return privateData.get(key)
}
instance.has = (key) => {
return privateData.has(key)
}
}
})
const storage = SecureStorage()
storage.set('token', 'secret123')
console.log(storage.get('token')) // secret123
4. 静态属性:为 Stamp 本身添加功能
const Factory = stampit({
statics: {
// 静态方法
createDefault() {
return this() // this 指向 Stamp 本身
},
// 静态属性
version: '1.0.0',
author: 'Dev Team'
}
})
console.log(Factory.version) // 1.0.0
const defaultObj = Factory.createDefault()
高级组合模式
模式 1:行为组合(Behavior Composition)
// 定义可重用行为
const Loggable = stampit({
methods: {
log(action, data) {
console.log(`Action: ${action}`, data)
}
}
})
const Validatable = stampit({
methods: {
validate() {
return Object.keys(this.rules || {}).every(key => {
const rule = this.rules[key]
const value = this[key]
return rule(value)
})
}
}
})
const Persistent = stampit({
methods: {
save() {
this.log('save', this)
// 保存逻辑...
return true
}
}
})
// 组合成完整对象
const User = stampit({
props: {
name: '',
email: '',
rules: {
name: val => val.length > 0,
email: val => /.+@.+\..+/.test(val)
}
}
}).compose(Loggable, Validatable, Persistent)
const user = User({ name: '张三', email: 'zhangsan@example.com' })
if (user.validate()) {
user.save()
}
模式 2:装饰器模式(Decorator Pattern)
const WithTiming = stampit({
methods: {
timedMethod(originalMethod, methodName) {
return function(...args) {
const start = performance.now()
const result = originalMethod.apply(this, args)
const end = performance.now()
console.log(`${methodName} executed in ${end - start}ms`)
return result
}
}
}
})
const OriginalService = stampit({
methods: {
processData(data) {
// 复杂处理逻辑
return data.map(item => item * 2)
}
}
})
// 装饰原始服务
const TimedService = OriginalService.compose(WithTiming)
const service = TimedService()
// 替换方法以添加计时
service.processData = service.timedMethod(service.processData, 'processData')
service.processData([1, 2, 3])
模式 3:中间件模式(Middleware Pattern)
const MiddlewareSupport = stampit({
init() {
this.middlewares = {
pre: [],
post: []
}
},
methods: {
use(middleware, type = 'pre') {
this.middlewares[type].push(middleware)
return this
},
async execute(action, ...args) {
// 执行前置中间件
for (const middleware of this.middlewares.pre) {
await middleware.call(this, action, args)
}
// 执行主要动作
const result = await action.call(this, ...args)
// 执行后置中间件
for (const middleware of this.middlewares.post) {
await middleware.call(this, action, args, result)
}
return result
}
}
})
const Service = stampit({
methods: {
async fetchData(url) {
const response = await fetch(url)
return response.json()
}
}
}).compose(MiddlewareSupport)
const service = Service()
service.use(async (action, args) => {
console.log('Before:', action.name, args)
}, 'pre')
service.use(async (action, args, result) => {
console.log('After:', action.name, 'Result:', result)
}, 'post')
service.execute(service.fetchData, '/api/data')
实战案例:构建一个完整的应用
案例:电商购物车系统
// 基础购物车项
const CartItem = stampit({
props: {
productId: '',
name: '',
price: 0,
quantity: 1
},
methods: {
getTotal() {
return this.price * this.quantity
},
updateQuantity(newQuantity) {
this.quantity = Math.max(0, newQuantity)
return this
}
}
})
// 折扣功能
const Discountable = stampit({
props: {
discountRate: 0
},
methods: {
applyDiscount(rate) {
this.discountRate = Math.max(0, Math.min(1, rate))
return this
},
getDiscountedPrice() {
return this.getTotal() * (1 - this.discountRate)
}
}
})
// 持久化功能
const Persistable = stampit({
methods: {
save() {
const data = JSON.stringify(this)
localStorage.setItem(this.storageKey, data)
return this
},
load() {
const data = localStorage.getItem(this.storageKey)
if (data) {
Object.assign(this, JSON.parse(data))
}
return this
}
}
})
// 日志功能
const Loggable = stampit({
methods: {
log(action, data) {
console.log(`[${new Date().toISOString()}] ${action}:`, data)
}
}
})
// 购物车主类
const ShoppingCart = stampit({
props: {
items: [],
storageKey: 'shopping_cart'
},
methods: {
addItem(product, quantity = 1) {
const existingItem = this.items.find(item => item.productId === product.id)
if (existingItem) {
existingItem.updateQuantity(existingItem.quantity + quantity)
} else {
const newItem = CartItem({
productId: product.id,
name: product.name,
price: product.price,
quantity: quantity
}).compose(Discountable)
this.items.push(newItem)
}
this.log('addItem', { product, quantity })
return this
},
removeItem(productId) {
this.items = this.items.filter(item => item.productId !== productId)
this.log('removeItem', { productId })
return this
},
getTotal() {
return this.items.reduce((total, item) => {
return total + (item.getDiscountedPrice ? item.getDiscountedPrice() : item.getTotal())
}, 0)
},
clear() {
this.items = []
this.log('clear', {})
return this
}
}
}).compose(Persistable, Loggable)
// 使用示例
const cart = ShoppingCart()
cart.addItem({ id: '1', name: '商品A', price: 100 }, 2)
cart.addItem({ id: '2', name: '商品B', price: 200 }, 1)
// 应用折扣
cart.items[0].applyDiscount(0.1) // 商品A打9折
console.log('总金额:', cart.getTotal()) // 100*2*0.9 + 200*1 = 380
cart.save() // 保存到本地存储
性能优化与最佳实践
1. 避免过度组合
// 不推荐:过度细分行为
const Nameable = stampit({ props: { name: '' } })
const Ageable = stampit({ props: { age: 0 } })
const Addressable = stampit({ props: { address: '' } })
// 推荐:合理分组
const PersonalInfo = stampit({
props: {
name: '',
age: 0,
address: ''
}
})
2. 使用深拷贝避免引用共享问题
// 存在问题:引用共享
const Problematic = stampit({
props: {
items: [] // 所有实例共享同一个数组引用
}
})
// 解决方案1:使用 deepProps
const Solution1 = stampit({
deepProps: {
items: [] // 每个实例获得独立的数组副本
}
})
// 解决方案2:在 init 中初始化
const Solution2 = stampit({
props: {
items: null
},
init() {
if (!this.items) {
this.items = [] // 延迟初始化
}
}
})
3. 性能对比表
| 操作 | 传统类 | Stampit | 优势 |
|---|---|---|---|
| 对象创建 | ⚡ 快 | 🐢 稍慢 | 可忽略 |
| 方法调用 | ⚡ 快 | ⚡ 快 | 相当 |
| 内存占用 | 低 | 中 | 可接受 |
| 灵活性 | 低 | 高 | 🎯 Stampit 胜出 |
| 维护性 | 中 | 高 | 🎯 Stampit 胜出 |
迁移指南:从传统 OOP 到 Stampit
传统类写法
class User {
constructor(name, email) {
this.name = name
this.email = email
this.createdAt = new Date()
}
getProfile() {
return {
name: this.name,
email: this.email,
memberSince: this.createdAt.toISOString().split('T')[0]
}
}
static createAnonymous() {
return new User('Anonymous', 'anonymous@example.com')
}
}
class Admin extends User {
constructor(name, email, permissions) {
super(name, email)
this.permissions = permissions
}
hasPermission(permission) {
return this.permissions.includes(permission)
}
}
Stampit 等价实现
const User = stampit({
props: {
name: '',
email: '',
createdAt: null
},
init({ name, email }) {
this.name = name || this.name
this.email = email || this.email
this.createdAt = this.createdAt || new Date()
},
methods: {
getProfile() {
return {
name: this.name,
email: this.email,
memberSince: this.createdAt.toISOString().split('T')[0]
}
}
},
statics: {
createAnonymous() {
return this({ name: 'Anonymous', email: 'anonymous@example.com' })
}
}
})
const Admin = stampit({
props: {
permissions: []
},
methods: {
hasPermission(permission) {
return this.permissions.includes(permission)
}
}
})
// 组合使用
const AdminUser = stampit(User, Admin)
const admin = AdminUser({
name: '管理员',
email: 'admin@example.com',
permissions: ['read', 'write', 'delete']
})
常见问题解答(FAQ)
Q1: Stampit 适合什么类型的项目?
A: Stampit 特别适合:
- 需要高度可复用组件的项目
- 复杂的业务逻辑需要灵活组合
- 希望避免深层次继承结构的项目
- 需要良好测试性和可维护性的项目
Q2: Stampit 的性能如何?
A: 虽然对象创建速度稍慢于传统类,但在大多数应用场景中差异可以忽略不计。Stampit 带来的开发效率和维护性提升远远超过微小的性能代价。
Q3: 如何调试 Stampit 创建的对象?
A: Stampit 对象是普通的 JavaScript 对象,可以使用标准的调试工具。建议:
- 使用有意义的 Stamp 名称
- 利用
console.log检查对象结构 - 使用浏览器开发者工具检查原型链
Q4: Stampit 与 TypeScript 兼容吗?
A: 是的,Stampit 提供了完整的 TypeScript 类型定义(stampit.d.ts),可以完美支持 TypeScript 项目。
总结
Stampit 为 JavaScript 开发者提供了一种全新的面向对象编程范式。通过组合代替继承,它解决了传统 OOP 中的许多痛点:
🎯 核心优势:
- 极致的灵活性和可组合性
- 真正的封装和私有状态支持
- 出色的代码复用和维护性
- 渐进式采用,与现有代码完美兼容
🚀 适用场景:
- 复杂的企业级应用
- 需要高度可配置的系统
- 组件化的前端架构
- 插件系统和扩展机制
Stampit 不是要完全取代类,而是提供了一个更有力的工具来应对复杂的软件设计挑战。当你需要超越传统继承模型的限制时,Stampit 将是你的强大盟友。
下一步行动:
- 在项目中尝试使用 Stampit 替换一个简单的类
- 体验组合式设计带来的灵活性
- 探索 Stampit 生态系统中的其他工具
- 参与 Stampit 社区贡献和讨论
开始你的组合式编程之旅,体验 Stampit 带来的开发革命!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



