34

TS与JS中的Getters和Setter究竟有什么用[每日前端夜话0x92]

 4 years ago
source link: https://www.tuicool.com/articles/mmaIzqa
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

每日前端夜话 0x92

每日前端夜话,陪你聊前端。

每天晚上18:00准时推送。

正文共:1602 字

预计阅读时间:6 分钟

作者:Khalil Stemmler

翻译:疯狂的技术宅

来源: freecodecamp

bI7nieU.jpg!web

在本文中,我们讨论了getter 和 setter 在现代 Web 开发中的实用性。它们有用吗?什么时候使用它们是有意义的?

当 ECMAScript 5(2009)发布时,getters 和 setter(也称为访问器)被引入 JavaScript。

问题是,对于引入它们的原因及实用性存在很多困惑。

我在 reddit 看到了一个帖子【 https://www.reddit.com/r/typescript/comments/87t1h7/are_getters_and_setters_an_antipattern/ 】,讨论的内容是它们是否是反模式。

不幸的是,该主题的普遍共识是 “yes”。我认为这是因为大多数情况下,你所做的前端编程都不会要求提供 getter 和 setter 这样的操作。

尽管我不同意 getter 和 setter 完全是一个反模式。但它们在几种情况下能带来更多的实用性。

它们是什么?

getter 和 setter 是另一种提供对象属性访问的方法。

一般的用法如下所示:

 1interface ITrackProps {
 2  name: string;
 3  artist: string;
 4}
 5
 6class Track {  
 7  private props: ITrackProps;
 8
 9  get name (): string {
10    return this.props.name;
11  }
12
13  set name (name: string) {
14      this.props.name = name;
15  }
16
17  get artist (): string {
18    return this.props.artist;
19  }
20
21  set artist (artist: string) {
22      this.props.artist = artist;
23  }
24
25  constructor (props: ITrackProps) {
26    this.props = props;
27  } 
28
29  public play (): void {    
30      console.log(`Playing ${this.name} by ${this.artist}`);
31  }
32}

现在问题变成了:“为什么不只使用常规类属性?”

那么,在这种情况下, 是可以的

 1interface ITrackProps {
 2  name: string;
 3  artist: string;
 4}
 5
 6class Track {  
 7  public name: string;
 8  public artist: string;
 9
10  constructor (name: string, artist: string;) {
11    this.name = name;
12    this.artist = artist;
13  } 
14
15  public play (): void {    
16      console.log(`Playing ${this.name} by ${this.artist}`);
17  }
18}

这是一个非常简单的例子,让我们来看一个更好地描述,为什么我们应该关心使用 getter 和 settter 与常规类属性的场景。

防止贫血模式

你还记得贫血模式(译者注:一种反模式)是什么吗?尽早发现贫血模式的方法之一是,假如你的域实体的 每个属性 都有getter和setter(即: set 对域特定语言没有意义的操作)暴露的话。

如果你没有明确地使用 getset 关键字,那么会使所有 public 也有相同的负面影响。

思考这个例子:

 1class User {
 2  // 不好。你现在可以`set` 用户ID。 
 3  // 是否需要将用户的 id 变更为其他标识符? 
 4  // 这样安全吗? 你应该这样做吗?
 5  public id: UserId;
 6
 7  constuctor (id: UserId) {
 8    this.id = id;
 9  }
10}

在领域驱动设计中,为了防止出现贫血模式,并推进特定于领域的语言的创建,对于我们 仅公开对领域 有效的操作非常重要。

这意味着你需要了解自己正在工作的领域【 https://khalilstemmler.com/articles/solid-principles/single-responsibility/ 】。

我会让自己接受审查。让我们来看看 White Label 【 https://github.com/stemmlerjs/white-label 】中的 Vinyl 类,这是一个开源的乙烯基交易程序,使用领域驱动进行设计并基于 TypeScript 构建。

  1import { AggregateRoot } from "../../core/domain/AggregateRoot";
  2import { UniqueEntityID } from "../../core/domain/UniqueEntityID";
  3import { Result } from "../../core/Result";
  4import { Artist } from "./artist";
  5import { Genre } from "./genre";
  6import { TraderId } from "../../trading/domain/traderId";
  7import { Guard } from "../../core/Guard";
  8import { VinylCreatedEvent } from "./events/vinylCreatedEvent";
  9import { VinylId } from "./vinylId";
 10
 11interface VinylProps {
 12  traderId: TraderId;
 13  title: string;
 14  artist: Artist;
 15  genres: Genre[];
 16  dateAdded?: Date;
 17}
 18
 19export type VinylCollection = Vinyl[];
 20
 21export class Vinyl extends AggregateRoot<VinylProps> {
 22
 23  public static MAX_NUMBER_GENRES_PER_VINYL = 3;
 24
 25    //:fire:1. 外观。VinylId 键实际上并不存在
 26  //作为属性的 VinylProps,但我们仍然需要
 27  //提供对它的访问。
 28  get vinylId(): VinylId {
 29    return VinylId.create(this.id)
 30  }
 31
 32  get title (): string {
 33    return this.props.title;
 34  }
 35
 36  // :fire:2.所有这些属性都作为 props 嵌套
 37  // 在一层,这样我们就可以控制对 ACTUAL 值
 38  // 的访问和变化。
 39  get artist (): Artist {
 40    return this.props.artist
 41  }
 42
 43  get genres (): Genre[] {
 44    return this.props.genres;
 45  }
 46
 47  get dateAdded (): Date {
 48    return this.props.dateAdded;
 49  }
 50
 51  // :fire:3. 你会发现到目前为止还没有 setter,
 52  // 因为在创建之后去改变这些东西是没有意义的
 53
 54  get traderId (): TraderId {
 55    return this.props.traderId;
 56  }
 57
 58  // :fire:4. 这种方法称为“封装集合”。
 59  // 是的,我们需要添加类型。 但我们仍
 60  // 然没有公开 setter,因为这里有一
 61  // 些我们想要确保强制执行的不变逻辑。
 62
 63  public addGenre (genre: Genre): void {
 64    const maxLengthExceeded = this.props.genres
 65      .length >= Vinyl.MAX_NUMBER_GENRES_PER_VINYL;
 66
 67    const alreadyAdded = this.props.genres
 68      .find((g) => g.id.equals(genre.id));
 69
 70    if (!alreadyAdded && !maxLengthExceeded) {
 71      this.props.genres.push(genre);
 72    }
 73  }
 74
 75  // :fire: 5. 提供一种删除方式。
 76
 77  public removeGenre (genre: Genre): void {
 78    this.props.genres = this.props.genres
 79      .filter((g) => !g.id.equals(genre.id));
 80  }
 81
 82  private constructor (props: VinylProps, id?: UniqueEntityID) {
 83    super(props, id);
 84  }
 85
 86  // :fire: 6. 这就是我们创建 Vinyl 的方法。
 87  // 创建之后,除了 Genre 之外,所有属性
 88  // 都会变为“只读”,因为启用修改是有意义的。
 89  public static create (props: VinylProps, id?: UniqueEntityID): Result<Vinyl> {
 90    const propsResult = Guard.againstNullOrUndefinedBulk([
 91      { argument: props.title, argumentName: 'title' },
 92      { argument: props.artist, argumentName: 'artist' },
 93      { argument: props.genres, argumentName: 'genres' },
 94      { argument: props.traderId, argumentName: 'traderId' }
 95    ]);
 96
 97    if (!propsResult.succeeded) {
 98      return Result.fail<Vinyl>(propsResult.message)
 99    } 
100
101    const vinyl = new Vinyl({
102      ...props,
103      dateAdded: props.dateAdded ? props.dateAdded : new Date(),
104      genres: Array.isArray(props.genres) ? props.genres : [],
105    }, id);
106    const isNewlyCreated = !!id === false;
107
108    if (isNewlyCreated) {
109      // :fire: 7. 这就是我们需要 VinylId 的原因:
110      // 为这个域事件的所有订阅者提供标识符。
111      vinyl.addDomainEvent(new VinylCreatedEvent(vinyl.vinylId))
112    }
113
114    return Result.ok<Vinyl>(vinyl);
115  }
116}

充当外观、维护只读值、强制执行模型表达、封装集合以及创建域事件【 https://khalilstemmler.com/blogs/domain-driven-design/where-do-domain-events-get-dispatched/ 】是领域驱动设计中 getter 和 setter 的一些非常可靠的用例。

在 Vue.js 中更改检测

Vue.js 是一个较新的前端框架,以其快速和响应式而闻名。

Vue.js 能够如此有效地检测改变的原因是它们用 Object.defineProperty() API 去 监视 对 View Models 的更改!

来自 Vue.js 关于响应式的文档:

当你将纯 JavaScript 对象作为其数据选项传递给 Vue 实例时,Vue 将遍历其所有属性并用 Object.defineProperty 将它们转换为 getter/setter。getter/setter 对用户是不可见的,但是在幕后,它们使 Vue 能够在访问或修改属性时执行依赖关系跟踪和更改通知。——  Vue.js 文档:响应式(https://vuejs.org/v2/guide/reactivity.html)

总之,getter 和 setter 针对很多问题有很大的实用性。不过在现代前端 Web 开发中,这些问题并没有太多出现。

原文: https://www.freecodecamp.org/news/typescript-javascript-getters-and-setters-are-they-useless/

下面夹杂一些私货:也许你和高薪之间只差这一张图

2019年京程一灯课程体系上新,这是我们第一次将全部课程列表对外开放。

愿你有个好前程,愿你月薪30K。我们是认真的 ! BbquyaF.png!web

zMFVruu.jpg!web

在公众号内回复“体系”查看高清大图

长按二维码,加大鹏老师微信好友

拉你加入前端技术交流群

唠一唠怎样才能拿高薪

JFNJFbv.jpg!web

小手一抖,资料全有。长按二维码关注 前端先锋 ,阅读更多技术文章和业界动态。

MFryQjN.gif


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK