不,TypeScript和JavaScript中的Getter和Setter并不是没有用的

In this blog post, we talk about the utility of getters and setters in modern web development. Are they useless? When does it make sense to use them?
在此博客文章中,我们讨论了getter和setter在现代Web开发中的实用性。 他们没用吗? 什么时候使用它们有意义?

Getters and setters (also known as accessors) were introduced to JavaScript when ECMAScript 5 (2009) was released.

当ECMAScript 5(2009)发行时,JavaScript引入了Getter和setter(也称为访问器)。

The thing is, there's a lot of confusion about their utility and why you would ever even want to use them.

问题是,关于它们的效用以及为什么您甚至想要使用它们,存在很多困惑。

I came across this reddit thread where the discussion was about if they were an anti-pattern.

我碰到了这个reddit主题 ,讨论该主题是否是反模式。

Unfortunately, the general consensus of the thread was "yes". I think that's because the majority of your front-end programming on a daily basis doesn't call for the utility that getters and setters offer.

不幸的是,该线程的普遍共识是“是”。 我认为这是因为您的大多数前端编程每天都不需要getter和setter提供的实用程序。

Though I disagree with getters and setters being an anti-pattern overall. They have a lot of utility in several different cases.

虽然我不同意getter和setter是整体的反模式。 它们在几种不同的情况下都有很大的用途。

他们干什么的? (What are they for?)

Getters and setters are another way for you to provide access to the properties of an object.

获取器和设置器是提供对对象属性的访问的另一种方法。

Trivial usage might look like this:

琐碎的用法可能看起来像这样:

interface ITrackProps {
  name: string;
  artist: string;
}

class Track {  
  private props: ITrackProps;

  get name (): string {
    return this.props.name;
  }

  set name (name: string) {
	  this.props.name = name;
  }

  get artist (): string {
    return this.props.artist;
  }

  set artist (artist: string) {
	  this.props.artist = artist;
  }

  constructor (props: ITrackProps) {
    this.props = props;
  } 

  public play (): void {	
	  console.log(`Playing ${this.name} by ${this.artist}`);
  }
}

The question becomes: "why not just use regular class attributes?"

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

Well, in this case, we could.

好吧,在这种情况下, 我们可以

interface ITrackProps {
  name: string;
  artist: string;
}

class Track {  
  public name: string;
  public artist: string;

  constructor (name: string, artist: string;) {
    this.name = name;
    this.artist = artist;
  } 

  public play (): void {	
	  console.log(`Playing ${this.name} by ${this.artist}`);
  }
}

That's much simpler. And that's also a really simple use case. Let's look at scenarios that better describe why we might care about using getters and settters vs regular class attributes.

这要简单得多。 这也是一个非常简单的用例。 让我们看一下可以更好地描述为什么我们可能会关心使用getter和settter与常规类属性的方案。

防止贫血症域模型 (Preventing Anemic Domain models)

Do you remember what an anemic domain model is? One of the earliest ways to sniff out an anemic domain model is if there are getters and setters for every single attribute of your domain entities (ie: set operations that don't make sense to the domain-specific language are exposed).

您还记得什么是贫血领域模型吗? 嗅探贫乏领域模型的最早方法之一是是否为域实体的每个单个属性都具有getter和setters(即,公开了对特定于域的语言没有意义的set操作)。

And if you don't explicitly use the get or set keywords, making everything public also has the same negative effect.

而且,如果您未明确使用getset关键字, set所有内容public也具有相同的负面影响。

Consider this example:

考虑以下示例:

class User {
  // Bad. You can now `set` the user id.
  // When would you ever need to mutate a user's id to a
  // different identifier? Is that safe? Should you be able to?
  public id: UserId;

  constuctor (id: UserId) {
    this.id = id;
  }
}

In Domain-Driven Design, to prevent an anemic domain model and push forward the creation of a domain-specific language it's really important for us to only expose operations that are valid to the domain.

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

That means understanding the domain that you're working in.

这意味着了解您正在工作的领域

I'll put myself up for scrutiny. Let's take a look at the Vinyl class from White Label, an open-source vinyl-trading app built with TypeScript using Domain-Driven Design.

我会仔细检查一下。 让我们看一下White LabelVinyl类, White Label是使用TypeScript使用域驱动设计构建的开源乙烯基交易应用程序。

import { AggregateRoot } from "../../core/domain/AggregateRoot";
import { UniqueEntityID } from "../../core/domain/UniqueEntityID";
import { Result } from "../../core/Result";
import { Artist } from "./artist";
import { Genre } from "./genre";
import { TraderId } from "../../trading/domain/traderId";
import { Guard } from "../../core/Guard";
import { VinylCreatedEvent } from "./events/vinylCreatedEvent";
import { VinylId } from "./vinylId";

interface VinylProps {
  traderId: TraderId;
  title: string;
  artist: Artist;
  genres: Genre[];
  dateAdded?: Date;
}

export type VinylCollection = Vinyl[];

export class Vinyl extends AggregateRoot<VinylProps> {

  public static MAX_NUMBER_GENRES_PER_VINYL = 3;

  // ? 1. Facade. The VinylId key doesn't actually exist
  // as a property of VinylProps, yet- we still need
  // to provide access to it.

  get vinylId(): VinylId {
    return VinylId.create(this.id)
  }

  get title (): string {
    return this.props.title;
  }

  // ? 2. All of these properties are nested one layer
  // deep as props so that we can control access 
  // and mutations to the ACTUAL values.

  get artist (): Artist {
    return this.props.artist
  }

  get genres (): Genre[] {
    return this.props.genres;
  }

  get dateAdded (): Date {
    return this.props.dateAdded;
  }

  // ? 3. You'll notice that there are no setters so far because 
  // it doesn't make sense for us to be able to change any of these
  // things after it has been created

  get traderId (): TraderId {
    return this.props.traderId;
  }

  // ? 4. This approach is called "Encapsulate Collection". We
  // will need to add genres, yes. But we still don't expose the
  // setter because there's some invariant logic here that we want to
  // ensure is enforced.
  // Invariants: 
  // https://khalilstemmler.com/wiki/invariant/

  public addGenre (genre: Genre): void {
    const maxLengthExceeded = this.props.genres
      .length >= Vinyl.MAX_NUMBER_GENRES_PER_VINYL;

    const alreadyAdded = this.props.genres
      .find((g) => g.id.equals(genre.id));

    if (!alreadyAdded && !maxLengthExceeded) {
      this.props.genres.push(genre);
    }
  }

  // ? 5. Provide a way to remove as well.

  public removeGenre (genre: Genre): void {
    this.props.genres = this.props.genres
      .filter((g) => !g.id.equals(genre.id));
  }

  private constructor (props: VinylProps, id?: UniqueEntityID) {
    super(props, id);
  }

  // ? 6. This is how we create Vinyl. After it's created, all properties 
  // effectively become "read only", except for Genre because that's all that
  // makes sense to enabled to be mutated.

  public static create (props: VinylProps, id?: UniqueEntityID): Result<Vinyl> {
    const propsResult = Guard.againstNullOrUndefinedBulk([
      { argument: props.title, argumentName: 'title' },
      { argument: props.artist, argumentName: 'artist' },
      { argument: props.genres, argumentName: 'genres' },
      { argument: props.traderId, argumentName: 'traderId' }
    ]);

    if (!propsResult.succeeded) {
      return Result.fail<Vinyl>(propsResult.message)
    } 

    const vinyl = new Vinyl({
      ...props,
      dateAdded: props.dateAdded ? props.dateAdded : new Date(),
      genres: Array.isArray(props.genres) ? props.genres : [],
    }, id);
    const isNewlyCreated = !!id === false;

    if (isNewlyCreated) {
      // ? 7. This is why we need VinylId. To provide the identifier
      // for any subscribers to this domain event.
      vinyl.addDomainEvent(new VinylCreatedEvent(vinyl.vinylId))
    }

    return Result.ok<Vinyl>(vinyl);
  }
}

Acting as a facade, maintaining readonly values, enforcing model expressiveness, encapsulating collections, AND creating domain events are some very solid use cases for getters and setters in Domain-Driven Design.

Domain-Driven Design中,对于getter和setter来说,充当外观,维护只读值,增强模型的表达能力,封装集合以及创建域事件是非常可靠的用例。

Vue.js中的更改检测 (Change detection in Vue.js)

Vue.js, one of the newer front-end frameworks, prides itself with being very fast and reactive.

Vue.js是较新的前端框架之一,以其快速和ReactSwift而引以为豪。

The reason why Vue.js does change detection so efficiently is because they use the Object.defineProperty() API to watch for changes to your View Models!

Vue.js如此高效地进行更改检测的原因是因为它们使用Object.defineProperty() API监视对View模型的更改!

From the Vue.js docs on Reactivity,

根据有关响应性的Vue.js文档,

When you pass a plain JavaScript object to a Vue instance as its data option, Vue will walk through all of its properties and convert them to getter/setters using Object.defineProperty. The getter/setters are invisible to the user, but under the hood they enable Vue to perform dependency-tracking and change-notification when properties are accessed or modified. - Vue.js Docs: Reactivity

当您将纯JavaScript对象作为数据选项传递给Vue实例时,Vue将遍历其所有属性,并使用Object.defineProperty将它们转换为getter / setter。 该getter / setter对用户是不可见的,但是在内部,它们使Vue在访问或修改属性时能够执行依赖项跟踪和更改通知。 -Vue.js文件:React性



In conclusion, getters and setters do have a lot of utility for a lot of different problems. Those problems just don't occur a whole lot in modern front-end web development.

总之,吸气剂和吸气剂对于许多不同的问题确实有很多用途。 这些问题只是在现代前端Web开发中很少发生。

--

-

Advanced TypeScript和Node.js博客 (Advanced TypeScript & Node.js blog)

If you enjoyed this article, you should check out my blog. I write about Advanced TypeScript & Node.js best practices for large-scale applications and teach developers how to write flexible, maintainable software.

如果您喜欢这篇文章,则应该查看我的博客 。 我写了针对大型应用程序的Advanced TypeScript和Node.js最佳实践,并教开发人员如何编写灵活,可维护的软件。

翻译自: https://www.freecodecamp.org/news/typescript-javascript-getters-and-setters-are-they-useless/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值