• ES6 class类中定义私有变量


    ES6 class类中定义私有变量

    class类的不足

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

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

    来看下下面这个代码:

    class A {
        constructor(x) {
            this.x = x
        }
    
        // 想要通过该方法来暴露x
        showX() {
            return this.x
        }
    }
    
    let a = new A(1)
    
    // 直接访问x成功
    a.x // 1
    

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

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

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

    实现 class 私有变量

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

    1. 约定命名

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

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

       class A {
           constructor(x) {
               // _x 是一个私有变量
               this._x = x
           }
    
           showX() {
               return this._x
           }
    
       }
    
       let a = new A(1)
    
       // _x 依然可以被使用
       a._x // 1
       a.showX() //1
    

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

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

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

    1. 闭包

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

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

       class A {
           constructor(x) {
               let _x = x
               this.showX = function() {
                   return _x
               }
           }
       }
    
       let a = new A(1)
       // 无法访问
       a._x // undefined
       // 可以访问
       a.showX() // 1
    

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

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

    • 增加了额外的性能开销

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

    1. 进阶版闭包方式

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

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

      代码如下:

       // 利用闭包生成IIFE,返回类A
       const A = (function() {
           // 定义私有变量_x
           let _x
    
           class A {
               constructor(x) {
                   // 初始化私有变量_x
                   _x = x
               }
    
               showX() {
                   return _x
               }
           }
    
           return A
    
       })()
    
       let a = new A(1)
    
       // 无法访问
       a._x // undefined
       // 可以访问
       a.showX() //1
    

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

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

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

      代码如下:

        //  A模块
        const x = Symbol('x')
    
        export default class A {
            constructor(a) {
                // 利用symbol声明私有变量
                this[x] = a
            }
    
            showX() {
                return this[x]
            }
        }
    
        // B模块
        import A from "A模块"
    
        const a = new A(1)
    
        // 1. 第一种方式
        a[_x] // 报错 Uncaught ReferenceError: _x is not defined
    
        // 2. 第二种方式
        // 自行定义一个相同的Symbol
        const x = Symbol('x')
        a[x] // 无法访问,undefined
    
        // 3. 第三种方式,可以访问(正解)
        a.showX() //1
    
  • 相关阅读:
    js将页面上取得的元素坐标转换为电脑屏幕坐标
    一个例子形象的理解协程和线程的区别
    为什么有的人把代码写的如此复杂?
    都说Dapper性能好,突然就遇到个坑,还是个性能问题
    springgateway基于数据库 + nacos 的动态路由
    k8s配置deployment的 liveness 和 readiness 探针 若
    UML设计图5部署图 若
    UML设计图1用例图 若
    UML设计图6序列图 若
    UML设计图2数据流图 若
  • 原文地址:https://www.cnblogs.com/guojiabing/p/10990267.html
Copyright © 2020-2023  润新知