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

译者按: 为何偏要用#符号?javascript

本文采用意译,版权归原做者全部html

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

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

Class 的私有属性语法以下:github

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

参考

关于Fundebug

Fundebug专一于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有Google、360、金山软件、百姓网等众多品牌企业。欢迎你们免费试用

版权声明

转载时请注明做者Fundebug以及本文地址:
https://blog.fundebug.com/201...

相关文章
相关标签/搜索