[译]<<Effective TypeScript>> 技巧56:不要依赖private来隐藏信息

380 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

本文的翻译于<<Effective TypeScript>>, 特别感谢!! ps: 本文会用简洁, 易懂的语言描述原书的所有要点. 如果能看懂这文章,将节省许多阅读时间. 如果看不懂,务必给我留言, 我回去修改.

技巧56:不要依赖private来隐藏信息

js一直都缺少隐藏class属性的方法,通常的解决办法在非公有的字段前面加下划线:

class Foo {
  _private = 'secret123';
}

但是这种方法很容易绕开:

const f = new Foo();
f._private;  // 'secret123'

ts添加了public,protected,private字段用来强制隐藏信息:

class Diary {
  private secret = 'cheated on my English test';
}

const diary = new Diary();
diary.secret
   // ~~~~~~ Property 'secret' is private and only
   //        accessible within class 'Diary'

但是关键字private是类型系统的功能。当js运行时,该功能就会消失。当编译成js后,变成了这样:

class Diary {
  constructor() {
    this.secret = 'cheated on my English test';
  }
}
const diary = new Diary();
diary.secret;

private关键字只是不鼓励你获取该属性,但是无法阻止你。加上断言后,ts甚至都不厚警告你了:

class Diary {
  private secret = 'cheated on my English test';
}

const diary = new Diary();
(diary as any).secret  // OK

换句话说,不要依赖private来隐藏信息。那么我们该怎么做?传统最可靠隐藏js的方法是:闭包。 你可以这样做:

eclare function hash(text: string): number;

class PasswordChecker {
  checkPassword: (password: string) => boolean;
  constructor(passwordHash: number) {
    this.checkPassword = (password: string) => {
      return hash(password) === passwordHash;
    }
  }
}

const checker = new PasswordChecker(hash('s3cret'));
checker.checkPassword('s3cret');  // Returns true

js没有办法在constructor外获取到 passwordHash这个变量。当然这种方法也有问题:

  • 任何想获取passwordHash变量的方法只能写在constructor里面。
  • 实例化一个class就需要复制一份写在constructor的方法
  • 防止了同一class其他实例访问data

闭包或许不方便,但是能让你的数据保证安全。

更新的方法就是使用私有字段,这一语言特性正在被巩固。在这项提议中,想让字段变得私有,在前面加#:

class PasswordChecker {
  #passwordHash: number;

  constructor(passwordHash: number) {
    this.#passwordHash = passwordHash;
  }

  checkPassword(password: string) {
    return hash(password) === this.#passwordHash;
  }
}

const checker = new PasswordChecker(hash('s3cret'));
checker.checkPassword('secret');  // Returns false
checker.checkPassword('s3cret');  // Returns true

字段 #passwordHash无法在class外获取该字段。和闭包方法相比:class内的方法,和同类的其他实例可以获取该属性。js原生不支持私有字段,一种应变的方法使用WeakMaps来实现。该提议正在步骤3,当本书印刷出来,该提议也应该被接受。如果要使用该提议,建议先查阅ts release notes,看其是否可用。