TS 实现类中的私有变量

深入理解 privily:实现类中的私有变量

在现代 JavaScript 开发中,封装和数据保护始终是重要的编程原则之一。随着 ES6 class 语法的引入以及后续 ES2022 的私有字段支持,开发者们可以更好地保护类中的内部数据。然而,私有字段的语法存在一定的局限性,例如无法在类之外动态访问和管理。为了解决这些问题,我创建了 privily,一个轻量级的高阶函数库,它提供了一种优雅且灵活的方式,通过作用域来实现类中的私有变量。

设计理念

在设计 privily 时,我希望它能够:

  1. 简单易用:不依赖复杂的语言特性,提供直观的 API。
  2. 灵活性:能够适用于不同的开发场景,支持任意复杂的类结构。
  3. 数据封装:确保类的私有数据只能通过定义好的方法访问,无法从外部直接操作。

privily 的核心理念是:通过一个共享的作用域对象将类的私有变量进行封装,而这个作用域只在类的内部可访问。通过这种方式,开发者可以轻松定义私有变量,同时保持类的灵活性。

实现原理

我们来看一下 Privily 的核心实现代码,并逐步进行解析。

export default function privily<S extends object, T extends Constructor>(c: (scope: S) => T): T {
    // 包装器函数,负责通过原始类构造实例并注入私有作用域
    const wrapper = function (...args: never[]) {
        // 创建一个空对象 `scope`,作为私有作用域,类型为 S
        const scope = {} as S;

        // 调用传入的函数 `c`,并将 `scope` 传递进去,以获得要包装的原始类
        const WrappedClass = c(scope);

        // 使用包装后的类创建实例,传入所有构造函数的参数
        const instance = new WrappedClass(...args);

        // 设置新实例的原型为 WrappedClass 的原型,确保新实例可以访问到原始类的所有原型方法
        Object.setPrototypeOf(wrapper.prototype, WrappedClass.prototype);

        // 将原始类的静态属性复制到 `wrapper` 上,保留类的静态方法和属性
        Object.getOwnPropertyNames(WrappedClass).forEach(key => {
            const descriptor = Object.getOwnPropertyDescriptor(WrappedClass, key);
            if (key !== "prototype" && descriptor?.writable) {
                (wrapper as any)[key] = (WrappedClass as any)[key];
            }
        });

        // 返回创建好的实例
        return instance;
    };

    // 返回的 wrapper 函数需要保持与原始类类型兼容,因此我们将其转换为构造函数类型 T
    return wrapper as unknown as T;
}

privily 函数解析

privily 函数是整个库的核心,它接收一个函数 c,这个函数本质上是一个高阶函数,它接受一个共享的作用域对象 scope,并返回一个要封装的类 WrappedClass。

  • 作用域初始化:
    在 wrapper 函数内部,我们首先创建一个空对象 scope,这个对象将在类的每个实例之间共享,用于存储私有变量。这个对象的类型是泛型 S,它可以根据不同的类定义进行灵活扩展。
  • 包装类的创建:
    通过调用 c(scope),我们获得了一个基于共享作用域的类 WrappedClass。这个类可以是任何具有构造函数的类,而 scope 在类内部作为共享的私有数据存储。
  • 实例化和原型链设置:

通过 new WrappedClass(…args),我们使用包装后的类创建一个实例,并将其原型链设置为 WrappedClass 的原型。这样可以确保新实例能够访问到类的原型方法和属性。

  • 静态属性继承:

为了保留原始类的静态方法和属性,我们将 WrappedClass 的静态属性复制到 wrapper 函数上,避免静态属性的丢失。

  • 返回兼容的构造函数:

最后,privily 返回的是一个类型与原始类兼容的构造函数,使得外部使用时看起来与原始类一致,但其内部实现已经被扩展,具备了私有作用域的能力。

用例解析

通过 privily,我们可以轻松定义一个带有私有变量的类。以下是一个简单的示例:

import privily from 'privily';

// 定义一个带私有作用域的类
const MyClass = privily((scope: { privateData: string }) => {
    class MyClass {
        constructor(public name: string) {
            scope.privateData = "This is private!";
        }

        getPrivateData() {
            return scope.privateData;
        }
    }
    return MyClass;
});

const instance = new MyClass('example');
console.log(instance.name);  // 输出: 'example'
console.log(instance.getPrivateData());  // 输出: 'This is private!'

在这个例子中,我们通过 privily 包装了一个 MyClass 类,并在类的内部定义了一个私有变量 privateData,它只能通过类的方法访问,而无法从外部直接获取。这样,我们实现了数据封装和安全性

为什么选择 privily?

相比于传统的 JavaScript 闭包或 Symbol 实现,privily 提供了以下几个优势:

  1. 统一的封装方式:通过共享作用域对象的方式,我们可以在任意类中轻松实现私有变量的封装,避免了重复和复杂的实现
  2. 静态属性支持:Privily 保证了类的静态方法和属性不会在封装过程中丢失,提供了完整的类封装能力。
  3. 灵活性:无论是简单的类,还是具有复杂继承关系的类结构,Privily 都能轻松适应,确保封装逻辑不被破坏

结语

privily 是一个轻量、灵活且强大的工具,旨在帮助开发者更好地实现 JavaScript 中的私有数据封装。如果你正在寻找一种优雅的方式来管理类的私有变量,而不想依赖最新的语言特性或复杂的模式,那么 Privily 是一个理想的选择。
你可以通过 npm 获取 privily,并在你的项目中开始使用它!希望它能为你的开发过程带来便利

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值