当前位置: 首页 > 科技观察

为什么在JavaScript中使用getter和setter是个坏主意

时间:2023-03-20 10:50:24 科技观察

如您所知,getter和setter已经成为JavaScript的一部分。它们在所有主流浏览器中都得到广泛支持,甚至包括IE8。我不认为这个想法通常是错误的,但我不认为它非常适合JavaScript。getters和setters看起来似乎简化了代码并节省了时间,但实际上它们引入了乍看之下并不明显的隐藏错误。getter和setter是如何工作的?首先简要总结一下这些东西是什么:有时我们希望允许访问返回动态计算值的属性,或者您可能希望在不使用显式方法调用的情况下反映内部变量的状态。为了说明它们是如何工作的,让我们看一下具有两个属性的person对象:firstName和lastName,以及一个计算值:fullName。varobj={firstName:"Maks",lastName:"Nemisj"}计算值fullName将返回firstName和lastName的串联。Object.defineProperty(person,'fullName',{get:function(){returnthis.firstName+''+this.lastName;}});为了获得fullName的计算值,不需要像person.fullName()那样使用可怕的括号,只需使用简单的varfullName=person.fullName。这同样适用于setter,您可以使用函数设置值:Object.defineProperty(person,'fullName',{set:function(value){varnames=value.split('');this.firstName=names[0];this.lastName=姓名[1];}});使用就像getter一样简单:person.fullName='BorisGorbachev'。这将调用上面定义的函数并将BorisGorbachev分为firstName和lastName。有什么问题?你可能在想:“嘿,我喜欢getter和setter,它们感觉更自然,就像JSON。”你是对的,他们是,但让我们回顾一下fullName在getter和setter之前是如何工作的。要获取值,我们将使用类似getFullName()的方法,而要设置值,我们将使用person.setFullName('MaksNemisj')。如果函数名称拼写错误并且person.getFullName()写成person.getFulName()会怎样?JavaScript会报错:person.getFulName();^TypeError:undefineddisnotfunction这个错误会在适当的时间和地点被替换触发。访问其功能不存在的对象将触发错误-这很好。现在,让我们看看当我们使用错误名称的setter时会发生什么?person.fulName='鲍里斯戈尔巴乔夫';没有什么。对象是可扩展的,键和值可以动态分配,所以在运行时不会抛出错误。这种行为意味着错误可能显示在UI的某处,或者在对错误值执行某些操作时显示,而不是在输入错误时显示。追踪过去本应发生但在未来代码流中出现的错误非常有趣。通过sealAPI可以部分解决seal是否起作用的问题。只要对象是密封的,它就不能被改变,这意味着fulName将尝试为person对象分配一个新的键,但它会失败。出于某种原因,当我在Node.jsv4.0中对此进行测试时,它并没有像我预期的那样工作。所以,我不能确定这个解决方案。更令人沮丧的是,setter根本没有解决方法。正如我之前提到的,对象是可扩展的和故障安全的,这意味着访问不存在的密钥不会导致任何错误。如果这仅适用于对象文字,我不会费心写这篇文章,但在ECMAScript2015(ES6)以及使用类定义getter和setter的能力兴起之后,我决定写一篇关于潜在陷阱的博客。类的到来我知道类目前在一些JavaScript社区中并不是很流行。人们争论在基于函数/原型的语言(例如JavaScript)中是否需要它们。然而,事实是类在ECMAScript2015(ES6)规范中并且会存在一段时间。对我来说,类是一种在类的外部世界(消费者)和应用程序的内部世界之间指定定义良好的API的方法。这就是将规则黑白分明的抽象概念,我们假设这些规则不会很快改变。改进person对象并为其创建一个真正的类。person定义了一个获取和设置全名的接口。classPerson{constructor(firstName,lastName){this.firstName=firstName;this.lastName=lastName;}getFullName(){returnthis.firstName+''+this.lastName;}setFullName(value){varnames=value.split('');this.firstName=names[0];this.lastName=names[1];}}该类定义了严格的接口描述,但是getter和setter方法使其不那么严格。在使用对象文字和JSON键拼写错误时,我们习惯于膨胀错误。我希望至少课程可以更严格,从这个意义上说,可以为开发人员提供更好的反馈。尽管在类上定义getter和setter时情况没有任何不同。但它不会阻止任何人拼错它。classPerson{constructor(firstName,lastName){this.firstName=firstName;this.lastName=lastName;}getfullName(){returnthis.firstName+''+this.lastName;}setfullName(value){varnames=value.split('');this.firstName=names[0];this.lastName=names[1];}}拼写错误的执行不会给出任何错误:varperson=newPerson('Maks','Nemisj');console.log(person.全名);也不严格,不冗长,无法追踪的行为会导致错误。发现这一点后,我有一个疑问:在使用getter和setter时,我可以做些什么来使类更严格?我发现:是的,有,但是值得吗?添加一个额外的层代码如此复杂只是为了使用更少的括号?对于API定义,getters和setters也可以去掉,这样就解决了这个问题。除非您是铁杆开发人员并且愿意继续,否则还有另一种解决方案,如下所述。代理帮助?除了getter和setter方法之外,ECMAScript2015(ES6)还附带了代理对象。代理可以帮助您识别可用于在执行实际访问密钥之前执行各种操作的委托方法。事实上,它看起来像动态的getter/setter方法。代理对象可用于捕获对类实例的任何访问,如果在类中未找到预定义的getter或setter,则抛出错误。为此,必须完成两件事:基于Person原型创建一个getter和setter列表。创建将测试这些清单的代理对象。一起让它成为现实。首先,要找出可用于类Person的getter和setter,请使用getOwnPropertyNames和getOwnPropertyDescriptor:.prototype,name);return!!result.get;});varsetters=names.filter((name)=>{varresult=Object.getOwnPropertyDescriptor(Person.prototype,name);return!!result.set;});在此之后,创建一个Proxy对象:varhandler={get(target,name){if(getters.indexOf(name)!=-1){returntarget[name];}thrownewError('Getter"'+name+'"notfoundin"Person"');},set(target,name){if(setters.indexOf(name)!=-1){returntarget[name];}thrownewError('Setter''+name+'"notfoundin"Person"');}};person=newProxy(person,handler);现在,每当您尝试访问person.fulName时,都会显示消息错误:在“Person”中找不到Getter“fulName”。希望本文能帮助您全面了解getter和setter方法以及它们可能给您的代码带来的危险。