资讯专栏INFORMATION COLUMN

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

opengps / 1267人阅读

摘要:事实上,最新的已经支持了私有属性。而的私有方法是提案的一部分。自从年双十一正式上线,累计处理了亿错误事件,付费客户有金山软件百姓网等众多品牌企业。

译者按: 为什么偏要用#符号?

原文:JavaScript"s new #private class fields

译者:Fundebug

本文采用意译,版权归原作者所有

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

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

版权声明

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/103786.html

相关文章

  • ES5原型链和ES6类实现详解

    摘要:类才支持实例属性代码解读上面暂时只是概念性的写法,事实上,的类只是一个原型链的语法糖而已,主要是从写法上更接近于面相对象的类而已,另外一个作用就是区分的构造函数和函数之间的区分。 ES5的原型链和ES6的类实现详解 JavaScript最初设计时受到了面相对象编程的影响,从而引入了new关键字,来实例化对象。而在ES5中new后面跟着的是构造函数(也是函数),而到了ES6则改成了cla...

    王伟廷 评论0 收藏0
  • ES5原型链和ES6类实现详解

    摘要:类才支持实例属性代码解读上面暂时只是概念性的写法,事实上,的类只是一个原型链的语法糖而已,主要是从写法上更接近于面相对象的类而已,另外一个作用就是区分的构造函数和函数之间的区分。 ES5的原型链和ES6的类实现详解 JavaScript最初设计时受到了面相对象编程的影响,从而引入了new关键字,来实例化对象。而在ES5中new后面跟着的是构造函数(也是函数),而到了ES6则改成了cla...

    Warren 评论0 收藏0
  • 详解JS类概念实现

    摘要:它继承自构造函数被执行,相应的参数会被传入,同时上下文会指向这个新的实例除非明确返回值,否则返回新的实例至此,我们实现了里面的类对象和属性的概念,和有相同的属性,但是值并不相同即属性是私有的。 众所周知,JS并没有类(class)的概念,虽然说ES6开始有了类的概念,但是,这并不是说JS有了像Ruby、Java这些基于类的面向对象语言一样,有了全新的继承模型。ES6中的类,仅仅只是基于...

    _ipo 评论0 收藏0
  • ECMASCript 2019可能会有哪些特性?

    摘要:可能不会包括所有的语法提案。事实上,有些提案已经被搁置很多年了。因此,很可能也会在今年月份发布。 译者按: 又过了1年... 原文:Whats New in JavaScript for 2019 译者: Fundebug 为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。 最近这些年,ECMASCript标准发展节奏非常稳定,每年都会发布新的特...

    tuantuan 评论0 收藏0
  • Java基础-内部类详解

    摘要:序内部类是定义在另一个类内部的类。之所以定义在内部是因为内部类有一些普通类没有的特权,可以方便实现一些需求。还有明确的使用内部类的构建函数。在以后,使用表达式会比匿名内部类更加方便。 我的博客 转载请注明原创出处。 序 内部类(inner class)是定义在另一个类内部的类。之所以定义在内部是因为内部类有一些普通类没有的特权,可以方便实现一些需求。 内部类 先来看一个简单的例子...

    graf 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<