当前位置: 首页 > news >正文

JavaScript 新语法详解:Class 的私有属性与私有方法 ...

proposal-class-fields与proposal-private-methods定义了 Class 的私有属性以及私有方法,这 2 个提案已经处于 Stage 3,这就意味着它们已经基本确定下来了,等待被加入到新的 ECMAScript 版本中。事实上,最新的 Chrome 已经支持了 Class 私有属性。

那么,对于 Class 的私有属性与私有方法,它们究竟是什么呢?它们是怎样工作的?为什么要使用#符号来定义呢?

Class 的私有属性语法如下:

class Point {
    #x;
    #y;

    constructor(x, y) {
        this.#x = x;
        this.#y = y;
    }

    equals(point) {
        return this.#x === point.#x && this.#y === point.#y;
    }
}

我们可以将其语法理解为 2 个部分:

  • 定义 Class 私有属性
  • 引用 Class 私有属性

定义 Class 私有属性

私有属性与公共属性的定义方式几乎是一样的,只是需要在属性名称前面添加#符号:

class Foo {
    publicFieldName = 1;
    #privateFieldName = 2;
}

定义私有属性的时候也可以不用赋值:

class Foo {
    #privateFieldName;
}

引用 Class 私有属性

引用私有属性也只需要使用#就好了。

class Foo {
    publicFieldName = 1;
    #privateFieldName = 2;
    add() {
        return this.publicFieldName + this.#privateFieldName;
    }
}

其中,this.#可以简化,去掉 this 也没问题,下面两种写法是等价的:

method() {
  #privateFieldName;
}
method() {
  this.#privateFieldName;
}

在 Class 定义中引用 Class 实例的私有属性

对于私有属性,我们是不可以直接通过 Class 实例来引用的,这也是私有属性的本来含义。但是有一种情况除外,在 Class 定义中,我们可以引用 Class 实例的私有属性:

class Foo {
    #privateValue = 42;
    static getPrivateValue(foo) {
        return foo.#privateValue;
    }
}

Foo.getPrivateValue(new Foo()); // >> 42

其中,fooFoo的实例,在 Class 定义中,我们可以通过 foo 来引用私有属性#privateValue

Class 的私有方法

Class 的私有属性是提案proposal-class-fields的一部分,这个提案只关注 Class 的属性,它并没有对 Class 的方法进行任何修改。而 Class 的私有方法是提案proposal-class-fields的一部分。

Class 的私有方法语法如下:

class Foo {
    constructor() {
        this.#method();
    }
    #method() {
        // ...
    }
}

我们也可以将函数赋值给私有属性:

class Foo {
    constructor() {
        this.#method();
    }

    #method = () => {
        // ...
    };
}

封装(隐藏)私有属性

我们不能直接通过 Class 实例引用私有属性,我们只能在 Class 定义中引用它们:

class Foo {
  #bar;
  method() {
    this.#bar; // Works
  }
}
let foo = new Foo();
foo.#bar; // Invalid!

另外,要做到真正的私有的话,我们应该无法检测这个私有属性是否存在,因此,我们需要允许定义同名的公共属性:

class Foo {
    bar = 1; // public bar
    #bar = 2; // private bar
}

如果我们不允许公共属性与私有属性同名,我们则可以通过给同名的公共属性复制监测该私有属性是否存在:

foo.bar = 1; // Error: `bar` is private! (报错,说明私有属性存在)

不报错也行:

foo.bar = 1;
foo.bar; // `undefined` (赋值失败,说明私有属性存在)

对于 subclass 应该同样如此,它也允许公共属性与私有属性同名:

class Foo {
    #fieldName = 1;
}

class Bar extends Foo {
    fieldName = 2; // Works!
}

关于 Class 私有属性的封装,可以参考Why is encapsulation a goal of this proposal?。

为什么使用#符号?

很多人都有一个疑问,为什么 JS 不能学习其他语言,使用private来定义私有属性和私有方法?为什么要使用奇怪的#符号?

使用 private 的话,代码要舒服很多:

class Foo {
  private value;

  equals(foo) {
    return this.value === foo.value;
  }
}

为什么不使用 private 来定义私有属性?

很多语言使用 private 来定义私用属性,如下:

class EnterpriseFoo {
  public bar;
  private baz;
  method() {
    this.bar;
    this.baz;
  }
}

对于这些语言属性,私用属性和公共属性的引用方式是相同的,因此他们可以使用 private 来定义私有属性。

但是,对于 JavaScript 来说,我们不能使用 this.field 来引用私有属性(我接下来会解释原因),我们需要在语法层面上区分私有属性和公共属性。在定义和引用私有属性的时候,使用#符号,私有属性与公共属性可以很好地区分开来。

为什么引用私有属性的时候需要#符号?

引用私有属性的时候,我们需要this.#field,而不是this.field,原因如下:

  • 因为我们需要封装私有属性,我们需要允许公共属性与私有属性同名,因此私有属性与公共属性的引用方式必须不一样。这一点我们在前文已经详述。
  • 公共属性可以通过this.field以及this['field']来引用,但是私有属性不能支持this['field']这种方式,否则会破坏私有属性的隐私性,示例如下:
class Dict extends null {
    #data = something_secret;
    add(key, value) {
        this[key] = value;
    }
    get(key) {
        return this[key];
    }
}

new Dict().get("#data"); // 返回私有属性

因此,私有属性与公共属性的引用方式必须不一样,否则会破坏this['field']语法。

  • 私有属性与公共属性的引用方式一样的话,会导致我们每次都需要去检查属性是公共的还是私有的,这会造成严重的性能问题。

这篇文章遵循Creative Commons Attribution 4.0 International License。

参考

  • Why is encapsulation a goal of this proposal?

版权声明

转载时请注明作者Fundebug以及本文地址:
https://blog.fundebug.com/2019/04/23/javascript-class-private-field-and-private-method/

相关文章:

  • SoftMax多分类器原理及代码理解
  • BugkuCTF web基础$_GET
  • Android逆向之旅---静态方式分析破解视频编辑应用「Vue」水印问题
  • 2017-12-24 手机编程环境初尝试-用AIDE开发Android应用
  • 2018 年第一季度报告:JavaScript 仍是最热门语言
  • Windows 7 用户已开始收到停止支持的提醒
  • 将用Axis2编写的Web Service项目打包成aar包
  • 微信端web页面传值
  • 知识阅读的好处你都了解吗?芒果xo来告诉你答案
  • git 常用命令
  • 开源一个自用的Android事件分发中心库,实现类似系统广播功能。
  • 九九乘法表
  • CentOS 7 LNMP部署—php
  • 利用原生javascript完成倒计时
  • pycharm 设置py文件的默认模版头部信息
  • JavaScript-如何实现克隆(clone)函数
  • 【笔记】你不知道的JS读书笔记——Promise
  • 77. Combinations
  • css的样式优先级
  • js中的正则表达式入门
  • k个最大的数及变种小结
  • Service Worker
  • SQLServer插入数据
  • Sublime text 3 3103 注册码
  • ViewService——一种保证客户端与服务端同步的方法
  • vue-loader 源码解析系列之 selector
  • Web设计流程优化:网页效果图设计新思路
  • 开发基于以太坊智能合约的DApp
  • 聊聊redis的数据结构的应用
  • 软件开发学习的5大技巧,你知道吗?
  • 实战|智能家居行业移动应用性能分析
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 用element的upload组件实现多图片上传和压缩
  • 智能合约开发环境搭建及Hello World合约
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • python最赚钱的4个方向,你最心动的是哪个?
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • ​虚拟化系列介绍(十)
  • # Java NIO(一)FileChannel
  • (2)STL算法之元素计数
  • (java)关于Thread的挂起和恢复
  • (LeetCode 49)Anagrams
  • (NO.00004)iOS实现打砖块游戏(十二):伸缩自如,我是如意金箍棒(上)!
  • (附源码)spring boot基于小程序酒店疫情系统 毕业设计 091931
  • (介绍与使用)物联网NodeMCUESP8266(ESP-12F)连接新版onenet mqtt协议实现上传数据(温湿度)和下发指令(控制LED灯)
  • (四)库存超卖案例实战——优化redis分布式锁
  • (学习日记)2024.02.29:UCOSIII第二节
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • (转)linux自定义开机启动服务和chkconfig使用方法
  • ***汇编语言 实验16 编写包含多个功能子程序的中断例程
  • .java 指数平滑_转载:二次指数平滑法求预测值的Java代码
  • .net on S60 ---- Net60 1.1发布 支持VS2008以及新的特性
  • .net 流——流的类型体系简单介绍
  • .NET应用架构设计:原则、模式与实践 目录预览