JavaScript 的新特性:类的 #private 字段
这是什么,如何使用,为什么需要?
一边听“Noise Pollution” —— Portugal. The Man,一边阅读本文简直就是享受
JavaScript 标准的第二阶段(Stage 2)加入了类私有字段。它还没有最终确定,但 JavaScript 标准委员会认为这个特性会被开发出来并最终纳入标准(虽然它可能还会改变)
它的语法(当前)看起来像这样:
class Point {
**#x;**
**#y;**
constructor(x, y) {
**this.#x** = x;
**this.#y** = y;
}
equals(point) {
return **this.#x** === **point.#x** && **this.#y** === **point.#y**;
}
}
这个语法有两个主要部分:
定义私有字段
引用私有字段
定义私有字段
定义私有字段与定义公共字段基本相同:
class Foo {
**publicFieldName =** 1;
**#privateFieldName =** 2;
}
为了访问私有字段,你需要定义它。如果你不想在定义的时候给它赋值,可以这样:
class Foo {
**#privateFieldName;**
}
引用私有字段
引用私有字段与访问其它属性类似,只是它有一点特殊的语法。
class Foo {
publicFieldName = 1;
#privateFieldName = 2;
add() {
return this.publicFieldName + **this.#privateFieldName**;
}
}
this.# 也可以简写:
method() {
**#privateFieldName;**
}
它与下面这段代码等价:
method() {
**this.#privateFieldName;**
}
引用实例(对象)的私有字段
引用私有字段并不局限于 this。也可以访问类实例(对象)的私有字段:
class Foo {
#privateValue = 42;
static getPrivateValue(**foo**) {
return **foo.#privateValue**;
}
}
Foo.getPrivateValue(**new Foo()**); // >> 42
这里,foo 是 Foo 的实例,所以我们允许在类定义中去寻找 #privateValue。
私有方法(很快有会有?)
私有字段这个提案只关注了为类添加私有字段这个问题。提案并没有提到会对类的方法做什么改变,所以私有方法可能会在后续建议中提到,像这样:
class Foo {
constructor() {
**this.#method();**
}
**#method() {**
// ...
**}**
}
这之前,可以将函数赋值给私有字段:
class Foo {
constructor() {
**this.#method();**
}
**#method = () => {**
// ...
**};**
}
封装
使用某个类的实例的时候,不能引用这个类的私有字段。只能在定义这些私有字段的类中引用它们。
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! (boom... detected)
或者不报错的版本:
foo.bar = 1;
console.log(foo.bar); // undefined (boom... detected again)
这种封装同样对子类有效。子类应该可以拥有相同名称的私有字段,而不必担心父类是否已经存某个私有字段是这个名称。
class Foo {
**#fieldName** = 1;
}
class Bar extends Foo {
**fieldName** = 2; // Works!
}
注意:关于封装或“强私有”背后的动机,阅读 FAQ 中这个部分。
那么, 为什么用 # 号[译者注:URL 的 Hash标记]?
很多人想知道“为什么不像其它语言那样使用 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;
}
}
在这些语言中,以同样的方式访问私有字段和公共字段。所以它们才会这样定义。
但是在 JavaScript 中,我们不能使用 this.field 来引用私有属性(稍后深入),我们需要一种基于语法的方法来连接它们的关系。这两个地方使用 # 更能清楚的表明引用的是什么。
什么引用需要 # (Hash 标记)?
因为如下原因,我们需要使用 this.#field 而不是 this.field:
因为 #封装 (参阅上面“封装”一节),我们要能同时访问公共和私有的同名字段。因此访问一个私有字段不是按常规的方式查找。
JavaScript 中的公共字段可以通过 this.field 或 this['field'] 来访问。私有字段不能支持第二种语法(因为它需要是静态的),这样会导致混淆。
你需要费劲去检查:
来看一段示例代码。
class Point {
**#x;**
**#y;**
constructor(x, y) {
**this.#x** = x;
**this.#y** = y;
}
equals(other) {
return **this.#x** === **other.#x** && **this.#y** === **other.#y**;
}
}
注意我们是如何引用 other.#x 和 other.#y 的。为了访问私有字段,我们假设 other 是 Point 类的实例。
因为我们使用了 #Hash标签 语法,它告诉 JavaScript 编译器我们正在从当前类中寻找私有属性。
那么,如果不用 #Hash标签,会发生什么呢?
equals(otherPoint) {
return this.x === otherPoint.x && this.y === otherPoint.y;
}
现在我们有个问题:我们怎么知道 otherPoint 是什么?
JavaScript 没有静态类型系统,所以 otherPoint 什么都可能是。
有两个原因导致这个问题:
我们的函数行为依赖于传入的值类型,不同的值类型导致不同的行为:有时候是在访问私有属性,有时候又是在查找公共属性。
我们每次检查 otherPoint 的类型:
if (otherPoint instanceof Point && isNotSubClass(otherPoint, Point)) { return getPrivate(otherPoint, 'foo'); } else { return otherPoint.foo; }
更糟糕的是,如果我们引用一个私有属性的话,就不得不在类中对每次属性访问进行这样的检查。
访问属性已经非常慢了,我们绝不想让它变得更慢。
TL;DR: 我们需要对私有属性使用 #Hash标记,因为不正常的使用标准属性访问机制会导致意外行为,并造成巨大的性能问题。
将私有属性添加到语言中是件非常好的事情。感谢努力工作在 TC39 勤劳的人们,他们正在让这美好的事情发生!
JavaScript 的新特性:类的 #private 字段的更多相关文章
- 前端笔记之ES678&Webpack&Babel(中)对象|字符串|数组的扩展&函数新特性&类
一.对象的扩展 1.1对象属性名表达式 ES6可以在JSON中使用[]包裹一个key的名字.此时这个key将用表达式作为属性名(被当做变量求值),这个key值必须是字符串. var a = 'name ...
- MongoDB 4.2新特性:分布式事务、字段级加密、通配符索引、物化视图
MongoDB 4.2已经发布,我们来看看它增加了哪些新特性?分布式事务?数据库加密?通配符索引? 在2019年MongoDB World大会上,CTO Eliot Horowitz介绍了MongoD ...
- javascript ES6 新特性之 扩展运算符 三个点 ...
对于 ES6 新特性中的 ... 可以简单的理解为下面一句话就可以了: 对象中的扩展运算符(...)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中. 作用类似于 Object.assign() ...
- 003.ES2015和ES2016新特性--类.md
JavaScript使用的是基于原型的OO模型,用对象字面量或者函数来实例化对象,用原型链来实现继承. 这样对于数据传统C++.Java的OO范式的开发者来说,会感到比较困惑,于是从ES2015开始逐 ...
- JavaScript ECAMScript5 新特性——get/set访问器
之前对get/set的理解一直有误,觉得get set 是对象属性方法.看了别人的博客也有很多疑问,今天系统的做了很多测试终于弄明白了.(自己通过看书和写demo测试的,如有不对欢迎大家批评指正) g ...
- JavaScript ES6 新特性详解
JavaScript ES6 带来了新的语法和新的强大功能,使您的代码更现代,更易读 const , let and var 的区别: const , let 是 ES6 中用于声明变量的新关键字. ...
- 用javascript实现完全的类(private、pubulic等)
js是面向对象的,但是其不像java一样完全的面向对象,但是利用其灵活性,我们可以使用它进行高度的模拟,来看下面的代码: function Student(name){ this.name=name; ...
- oracle 12c 新特性之(相同字段上的多重索引、ddl 日志、限制PGA的大小、分页查询)
1. 相同字段上的多重索引 在Oracle 12c R1之前,一个字段是无法以任何形式拥有多个索引的.或许有人会想知道为什么通常一个字段需要有多重索引,事实上需要多重索引的字段或字段集合是很多的. ...
- javascript ES6 新特性之 Promise,ES7 async / await
es6 一经推出,Promise 就一直被大家所关注.那么,为什么 Promise 会被大家这样关注呢?答案很简单,Promise 优化了回调函数的用法,让原本需要纵向一层一层嵌套的回调函数实现了横向 ...
随机推荐
- idea右侧的工具栏不见,包括maven以及其他
通用,设置后右侧工具栏可见 可以通过view-->windows tool看到maven的
- jquery/js/a标签实现当前页面跳转的两种方法
在逛购物网站首页时经常看到侧边导航栏,当我们点击导航栏中某一项时会跳转到当前页面的某一处 有两种方法实现,一种是利用js计算好各位置的高度,通过绑定事件使页面跳转到指定位置,另一种是利用a标签进行当前 ...
- phpstudy配置SSL证书的步骤(Apache环境)以及一些注意事项
准备工具(我自己的): 腾讯云的域名和云主机,还有SSL证书,以及phpstudy 首先要下载自己的SSL证书,会得到一个压缩包,解压以后会得到四个文件夹和一个csr文件, Apache文件夹内三个文 ...
- nginx的缓存服务
都知道缓存的目的是为了减小服务端的压力,可以在客户端直接取到数据 客户端---------------nginx(代理缓存)------------------服务端 代理缓存的描述: 就是客户端发送 ...
- 12.1.VUE学习之-循环li,if判断示例讲解class中应用表达式
功能: 当点击按键时,改变当前循环数组里的status里的值, 判断staus里的当前的值来,切换显示 删除 和 恢复 的按钮 判断staus里的当前的值来改变span标签里的字体颜色样式 <! ...
- python3.7 random模块
#!/usr/bin/env python __author__ = "lrtao2010" #python3.7 random模块 import random #随机模块 # r ...
- 回顾Scrum学习:《Scrum实战》第4次课【全职的Scrum Master】作业
回顾Scrum学习 1.回顾目标 1.1 期望结果 了解和学习Scrum,为将来换方向打好理论基础 如果能在目前公司引入和推行也很好,但是根据目前公司的文化氛围来看,推行希望不大 把敏捷思想应用到 ...
- 洛谷P1424小鱼的航程改进版
题目链接https://www.luogu.org/problemnew/show/P1424
- sql server 不可见字符处理 总结
前言 问题描述:在表列里有肉眼不可见字符,导致一些更新或插入失败. 几年前第一次碰见这种问题是在读取考勤机人员信息时碰见的,折腾了一点时间,现在又碰到了还有点新发现就顺便一起记录下. 如下图所示 go ...
- vue --子父组件传值
1.父组件可以使用 props 把数据传给子组件. 2.子组件可以使用 $emit 触发父组件的自定义事件. vm.$emit( event, arg ) //触发当前实例上的事件 vm.$on( e ...
