JavaScript 中创建私有成员的多种实现方案

作者:纸飞机

时间:2026-10-25

浏览:136

本文深入探讨JavaScript中实现私有成员的五种主要方式,包括利用闭包封装、ES6 Class外部定义、ES2020提案的#前缀语法、基于WeakMap的数据存储以及TypeScript的private关键字。通过对比各方案的代码结构、性能表现及适用场景,帮助开发者根据实际需求选择最合适的私有化实现策略,提升代码的安全性与可维护性。

文章目录

前言:

在面向对象编程语言中,private 关键字作为访问修饰符,限制属性和方法仅在声明类内部可见。这种机制有助于隐藏底层实现逻辑,防止外部直接交互,从而增强封装性。

然而,JavaScript 原生并未提供 private 保留字。不过,随着 ES2020 试验草案的推进,JavaScript 引入了以 # 为前缀的新语法来定义类私有成员。尽管语法较为特殊,但这为创建真正的私有属性和方法提供了官方支持。下文将详细介绍几种在 JavaScript 中实现私有化的可行方案。

1.使用闭包

闭包是实现私有属性和方法封装的经典手段。通过利用闭包对外部函数变量的访问特性,可以隐藏内部状态,仅暴露必要的接口。

参考以下代码片段:


function MyProfile() {
    const myTitle = "DevPoint";

    return {
        getTitle: function () {
            return myTitle;
        },
    };
}
const myProfile = MyProfile();
console.log(myProfile.getTitle()); // DevPoint


该模式可转化为将顶层自调用函数(IIFE)的返回值赋值给变量,并通过返回对象公开部分内部函数,从而实现私有化控制。


const ButtonCreator = (function () {
    const properties = {
        width: 100,
        height: 50,
    };

    const getWidth = () => properties.width;
    const getHeight = () => properties.height;
    const setWidth = (width) => (properties.width = width);
    const setHeight = (height) => (properties.height = height);

    return function (width, height) {
        properties.width = width;
        properties.height = height;

        return {
            getWidth,
            getHeight,
            setWidth,
            setHeight,
        };
    };
})();
const button = new ButtonCreator(600, 360);
console.log(button.getHeight()); // 360

2.使用 ES6 类

为了更贴近面向对象编程(OOP)风格,可以利用 ES6 引入的 class 关键字。若需实现私有化,可将属性和方法定义在类外部,通过闭包或共享对象进行隔离。

基于上述 ButtonCreator 示例,使用 class 进行重构的代码如下:


const properties = {
    width: 100,
    height: 50,
};

class ButtonCreator {
    constructor(width, height) {
        properties.width = width;
        properties.height = height;
    }

    getWidth = () => properties.width;
    getHeight = () => properties.height;
    setWidth = (width) => (properties.width = width);
    setHeight = (height) => (properties.height = height);
}
const button = new ButtonCreator(600, 360);
console.log(button.getHeight()); // 360

假设属性为公共状态,但希望在私有方法中访问它们,且方法上下文需指向 ButtonCreator,可通过以下方式实现:


const privates = {
    calculateWidth() {
        return this.width;
    },
};

class ButtonCreator {
    constructor(width, height) {
        this.width = width;
        this.height = height;
    }

    getWidth = () => privates.calculateWidth.call(this);
    getHeight = () => this.height;
    setWidth = (width) => (this.width = width);
    setHeight = (height) => (this.height = height);
}
const button = new ButtonCreator(600, 360);
console.log(button.getHeight()); // 360

上述代码调用了 Function.prototype.call,用于以指定上下文执行函数。示例中,使用了 ButtonCreator 类的上下文环境。

若私有函数需要接收参数,可将其作为附加参数传递给调用函数:


const privates = {
    calculateWidth(percent) {
        return this.width * percent;
    },
};

class ButtonCreator {
    constructor(width, height) {
        this.width = width;
        this.height = height;
    }

    getWidth = () => privates.calculateWidth.call(this, 0.1);
    getHeight = () => this.height;
    setWidth = (width) => (this.width = width);
    setHeight = (height) => (this.height = height);
}
const button = new ButtonCreator(600, 360);
console.log(button.getWidth()); // 60

3.使用 ES2020 提案

该方案仍处于 ES2020 试验草案阶段,引入了以 # 为前缀的私有方法或属性定义语法。虽然语法形式独特,但这是目前最接近原生私有成员的实现方式。


class ButtonCreator {
    #width;
    #height;
    constructor(width, height) {
        this.#width = width;
        this.#height = height;
    }
    // 私有方法
    #calculateWidth() {
        return this.#width;
    }

    getWidth = () => this.#calculateWidth();
    getHeight = () => this.#height;
    setWidth = (width) => (this.#width = width);
    setHeight = (height) => (this.#height = height);
}
const button = new ButtonCreator(600, 360);
console.log(button.width); // undefined
console.log(button.getWidth()); // 600

4.使用 WeakMap

此方法基于闭包思路,利用作用域变量创建私有 WeakMap,再通过 WeakMap 检索关联的私有数据。相比单纯的作用域变量方法,这种方式性能更优,因为所有实例可共享同一个 WeakMap,无需为每个实例重新创建方法逻辑。


const ButtonCreator = (function () {
    const privateProps = new WeakMap();
    class ButtonCreator {
        constructor(width, height, name) {
            this.name = name; // 公共属性
            privateProps.set(this, {
                width, // 私有属性
                height, // 私有属性
                calculateWidth: () => privateProps.get(this).width, // 私有方法
            });
        }

        getWidth = () => privateProps.get(this).calculateWidth();
        getHeight = () => privateProps.get(this).height;
    }
    return ButtonCreator;
})();
const button = new ButtonCreator(600, 360);
console.log(button.width); // undefined
console.log(button.getWidth()); // 600

然而,这种方式在定义私有方法时略显繁琐。

5.使用 TypeScript

可以将 TypeScript 视为 JavaScript 的一种扩展风格。借助 private 关键字,能够从面向对象语言中真正还原私有成员的功能,提供编译时的类型检查和访问控制。


class ButtonCreator {
    private width: number;
    private height: number;
    constructor(width: number, height: number) {
        this.width = width;
        this.height = height;
    }
    private calculateWidth() {
        return this.width;
    }
    public getWidth() {
        return this.calculateWidth();
    }
    public getHeight() {
        return this.height;
    }
}
const button = new ButtonCreator(600, 360);

console.log(button.getWidth()); // 600
console.log(button.width); // error TS2341: Property 'width' is private and only accessible within class 'ButtonCreator'.

总结:

本文介绍了 JavaScript 中创建私有成员的多种实现方式。如需了解更多相关内容,请查阅本站过往文章或继续浏览后续系列。感谢大家的支持!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章