ES6 class类中定义私有变量

class类的不足

看起来, es6 中 class 的出现拉近了 JS 和传统 OOP 语言的距离。但是,它仅仅是一个语法糖罢了,不能实现传统 OOP 语言一样的功能。在其中,比较大的一个痛点就是私有变量问题。

何为私有变量?私有变量就是只能在类内部访问的变量,外部无法访问的变量。在开发中,很多变量或方法你不想其他人访问,可以定义为私有变量,防止被其他人使用。在 Java 中,可以使用 private 实现私有变量,但是可惜的是, JS 中并没有该功能。

来看下下面这个代码:

  1. class A {
  2. constructor(x) {
  3. this.x = x
  4. }
  5. // 想要通过该方法来暴露x
  6. showX() {
  7. return this.x
  8. }
  9. }
  10. let a = new A(1)
  11. // 直接访问x成功
  12. a.x // 1

可以看到,虽然本意是通过方法 showX 来暴露 x 的值,但是可以直接通过 a. x 来直接访问 x 的值。

很明显,这影响了代码的封装性。要知道,这些属性都是可以使用 for... in 来遍历出来的。

所以,实现 class 的私有变量功能是很有必要的。

实现 class 私有变量

虽然, class 本身没有提供私有变量的功能,但是,我们可以通过通过一些方式来实现类似于私有变量的功能。

  1. 约定命名

    首先,是目前使用最广的方式:约定命名。

    该方式很简单,就是团队自行约定一种代表着私有变量的命名方式,一般是在私有变量的名称前加上一个下划线。代码如下:

  1. class A {
  2. constructor(x) {
  3. // _x 是一个私有变量
  4. this._x = x
  5. }
  6. showX() {
  7. return this._x
  8. }
  9. }
  10. let a = new A(1)
  11. // _x 依然可以被使用
  12. a._x // 1
  13. a.showX() //1

可以发现,该方法最大的优点是简单、方便,所以很多团队都采用了这种方式。

但是,该方式并没有从本质上解决问题,如果使用 for... in 依然可以遍历出所谓的私有变量,可以说是治标不治本。

不过,该方式有一点值得肯定,那就是通过约定规范来方便他人阅读代码。

  1. 闭包

    闭包在很多时候被拿来解决模块化问题,显而易见,私有变量本质上也是一种模块化问题,所以,我们也可以使用闭包来解决私有变量的问题。

    我们在构造函数中定义一个局部变量,然后通过方法引用,该变量就成为了真正的私有变量。

  1. class A {
  2. constructor(x) {
  3. let _x = x
  4. this.showX = function() {
  5. return _x
  6. }
  7. }
  8. }
  9. let a = new A(1)
  10. // 无法访问
  11. a._x // undefined
  12. // 可以访问
  13. a.showX() // 1

该方法最大的优点就是从本质解决了私有变量的问题。

但是有个很大的问题,在这种情况下,引用私有变量的方法不能定义在原型链上,只能定义在构造函数中,也就是实例上。这导致了两个缺点:

  • 增加了额外的性能开销

  • 构造函数中包含了方法,较为臃肿,对后续维护造成了一定的麻烦

  1. 进阶版闭包方式

    既然在构造函数内部定义闭包那么麻烦,那我放在 class 外面不就可以了吗?

    • 我们可以通过 IIFE (立即执行函数表达式) 建立一个闭包
    • 在其中建立一个变量以及 class ,通过 class 引用变量实现私有变量。

    代码如下:

  1. // 利用闭包生成IIFE,返回类A
  2. const A = (function() {
  3. // 定义私有变量_x
  4. let _x
  5. class A {
  6. constructor(x) {
  7. // 初始化私有变量_x
  8. _x = x
  9. }
  10. showX() {
  11. return _x
  12. }
  13. }
  14. return A
  15. })()
  16. let a = new A(1)
  17. // 无法访问
  18. a._x // undefined
  19. // 可以访问
  20. a.showX() //1

可以发现,该方法完美解决了之前闭包的问题,只不过写法相对复杂一些,另外,还需要额外创建 IIFE ,有一点额外的性能开销。

  • 注:该方式也可以不使用 IIFE ,可以直接将私有变量置于全局,但是这不利于封装性。
  1. Symbol

    这种方式利用的是 Symbol 的唯一性—— 敌人最大的优势是知道我方key值,我把key值弄成唯一的,敌人不知道我的key值,不就无法访问了吗? (人质是这次任务的关键,当敌人不再拥有人质时,任务也就完成了)

    代码如下:

  1. // A模块
  2. const x = Symbol('x')
  3. export default class A {
  4. constructor(a) {
  5. // 利用symbol声明私有变量
  6. this[x] = a
  7. }
  8. showX() {
  9. return this[x]
  10. }
  11. }
  12. // B模块
  13. import A from "A模块"
  14. const a = new A(1)
  15. // 1. 第一种方式
  16. a[_x] // 报错 Uncaught ReferenceError: _x is not defined
  17. // 2. 第二种方式
  18. // 自行定义一个相同的Symbol
  19. const x = Symbol('x')
  20. a[x] // 无法访问,undefined
  21. // 3. 第三种方式,可以访问(正解)
  22. a.showX() //1

ES6 class类中定义私有变量的更多相关文章

  1. Python类中的 私有变量和私有方法

    默认情况下,Python中的成员函数和成员变量都是公开的(public),在python中没有类似public,private等关键词来修饰成员函数和成员变量.在python中定义私有变量只需要在变量 ...

  2. MFC 如何在一个类中使用在其他类中定义的变量或函数

    [声明:本文的知识点来源于网络,参考网址:https://blog.csdn.net/bill_ming/article/details/7407848] [以下三种方法亲测有效,可以根据具体情况来选 ...

  3. 第7.10节 Python类中的实例变量定义与使用

    一.    引言 在前面章节已经引入介绍了类变量和实例变量,类体中定义的变量为类变量,默认属于类本身,实例变量是实例方法中定义的self对象的变量,对于每个实例都是独有数据,而类变量是该类所有实例共享 ...

  4. JavaScript中是如何定义私有变量的

    前言 JavaScript并不像别的语言,能使用关键字来声明私有变量. 我了解的JavaScript能用来声明私有变量的方式有两种,一种是使用闭包,一种是使用WeakMap. 闭包 闭包的描述有很多种 ...

  5. OC中的私有变量和description

    .OC中的私有变量 在类的实现即.m @implementation中也可以声明成员变量,但是因为在其他文件中通常都只 是包含头文件而不会包含实现文件,所以在.m文件中声明的成员变量是@private ...

  6. python中的私有变量

    class Test1: def f1(self): self.name ="张三" self.__age = 20 #使用名称变形实现私有变量 print(self.name) ...

  7. C++ 类中的静态成员变量,静态成员函数

    //类中的静态成员变量,静态成员函数 #define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; /* ...

  8. C++类中的静态成员变量与静态成员函数

    最近一直看c++相关的项目,但总是会被c++类中的静态成员变量与静态成员函数的理解感觉很是模糊,不明白为什么类中要是用静态成员变量.于是在网上搜集了一些资料,自己再稍微总结下. 静态成员的概念: 静态 ...

  9. Java中主类中定义方法加static和不加static的区别

     Java中主类中定义方法加static和不加static的区别(前者可以省略类名直接在主方法调用(类名.方法),后者必须先实例化后用实例调用) 知识点:1.Getter and Setter 的应用 ...

随机推荐

  1. spring cloud学习与思考——总起篇

    接下来就spring cloud( Spring Boot 2.0版本)写一个系列,一方面加深记忆理解,做个日志,另一个方面,借这个方式跟朋友们沟通交流. 1.Spring boot是Spring的一 ...

  2. 25.Zabbix入门必备

    ==Zabbix入门必备== 1.配置zabbix源 [root@zabbix ~]# cat /etc/yum.repos.d/zabbix.repo [zabbix] name=Zabbix Of ...

  3. .netcore2.1 ef 使用外键关联查询

    //实体类 [Table("invoiceinfo", Schema = "obs")] public class invoice { [Key] public ...

  4. Axure导出的原型无法在谷歌浏览器浏览

    1.下载crx后缀的文件. 2.修改crx后缀名为rar的压缩文件 3.解压刚才的rar文件 4.打开谷歌浏览器右上角的三个点 更多工具==>扩展程序 选择刚才的解压文件夹. 上面的图表示安装成 ...

  5. How do I unmute my Lenovo laptop?

    If the FN key does have a green light just press and hold down the FN button on the bottom left of t ...

  6. 团队项目之Scrum3

    小组:BLACK PANDA 时间:2019.11.23 每天举行站立式会议 提供当天站立式会议照片一张 2                            昨天已完成的工作 2 完善用户注册的 ...

  7. 简单搭建docker registry

    已知信息: 服务端IP:192.168.7.2xx 客户端IP:192.168.7.1xx 服务端: docker registry中镜像本地映射地址:/Users/dockergit/private ...

  8. win10+Ubuntu16.04双系统下深度学习环境的搭建

    环境零零碎碎地搭了三四天,虽然碰到各种问题,但还是搭建好了,自己整理记录下,同时也算给有需要的人一些指导吧 一.双系统的安装 Win10硬盘管理助手 压缩或者直接利用未使用的空间,空间大小自定,将腾出 ...

  9. 关于input标签不同type下的盒模型

    刚才发现,在Chrome下input标签的不同type类型所取的盒模型是不一样的.浪费了我很多时间去调试,唉. type="text"时,给它设置宽度width:300px,此时的 ...

  10. [译]Vulkan教程(32)生成mipmap

    [译]Vulkan教程(32)生成mipmap Generating Mipmaps 生成mipmap Introduction 入门 Our program can now load and ren ...