As you know, getters and setters have become part of JavaScript. They widely support all major browsers, even IE8.
I don't think this idea is generally wrong, but I think it's not very suitable for JavaScript. It may seem that getters and setters simplify code and save time, but they actually introduce hidden errors that are not obvious at first glance.
First a little summary of what these are:
Sometimes, we want to allow access to a property that returns a dynamically calculated value, or you may want to reflect the state of an internal variable , without using explicit method calls.
To illustrate how they work, let's look at a person object with two properties: firstName and lastName, and a calculated value: fullName.
var obj = { firstName: "Maks", lastName: "Nemisj" }
The calculated value fullName will return the concatenation of firstName and lastName.
Object.defineProperty(person, 'fullName', { get: function () { return this.firstName + ' ' + this.lastName; } });
To get the calculated value of fullName, you don't need the horrible parentheses like person.fullName(), just use simple var fullName = person.fullName.
The same applies to the setter, you can set the value by using the function:
Object.defineProperty(person, 'fullName', { set: function (value) { var names = value.split(' '); this.firstName = names[0]; this.lastName = names[1]; } });
Using is as simple as the getter: person.fullName = ‘Boris Gorbachev’. This will call the function defined above and separate Boris Gorbachev into firstName and lastName.
You may be thinking: "Hey, I like the getter and setter methods, they feel more natural, like JSON." You're right, they are, but let's take a step back and look at it. A look at how fullName works before getters and setters.
To get the value we will use something like getFullName(), and to set the value we will use person.setFullName(‘Maks Nemisj’).
What will happen if the function name is misspelled and person.getFullName() is written as person.getFulName()?
JavaScript will give an error:
person.getFulName(); ^ TypeError: undefined is not a function
This error will be triggered at the appropriate time and in the appropriate place. Accessing an object where the function does not exist will trigger an error - this is good.
Now, let’s see what happens when we use the setter with the wrong name?
person.fulName = 'Boris Gorbachev';
Nothing. Objects are extensible and keys and values can be assigned dynamically, so no errors will be thrown at runtime.
Such behavior means that an error may be displayed somewhere in the user interface, or when some operation is performed on the wrong value, rather than a typographical error.
It's so interesting to track errors that should have occurred in the past but show up in the future code flow.
This problem can be partially solved through sealAPI. As long as the object is sealed, it cannot be mutated, which means fulName will try to assign a new key to the person object, and it will fail.
For some reason, when I tested this in Node.js V4.0, it didn't work as I expected. So, I can't guarantee this solution.
What’s even more frustrating is that there is no solution at all for setters. As I mentioned earlier, objects are extensible and failsafe, which means that accessing a non-existent key will not cause any errors.
If this situation only applied to object literals, I wouldn’t bother writing this article, but after ECMAScript 2015 (ES6) and the rise of the ability to define getters and setters with classes, I decided to write Blog about potential pitfalls.
I know that currently classes are not very popular in some JavaScript communities. People debate whether they are needed in functional/prototype-based languages such as JavaScript. However, the fact is that classes are in the ECMAScript 2015 (ES6) specification and will be there for a while.
To me, classes are a way of specifying a well-defined API between the external world of the class (consumers) and the internal world of the application. This is an abstraction that puts the rules in black and white, and we assume those rules won't change anytime soon.
Improve the person object and make a real class of it. Person defines an interface for getting and setting fullName.
class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } getFullName() { return this.firstName + ' ' + this.lastName; } setFullName(value) { var names = value.split(' '); this.firstName = names[0]; this.lastName = names[1]; } }
The class defines a strict interface description, but the getter and setter methods make it less strict. We're used to bloated errors when working with object literals and misspellings in keys when working with JSON. I wish at least the classes could be more rigorous and, in that sense, provide better feedback to developers.
Although this situation is no different when defining getters and setters on a class. But it won't stop anyone from spelling it wrong.
class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } get fullName() { return this.firstName + ' ' + this.lastName; } set fullName(value) { var names = value.split(' '); this.firstName = names[0]; this.lastName = names[1]; } }
Executions with typos will not give any errors:
var person = new Person('Maks', 'Nemisj'); console.log(person.fulName);
The same lax, verbose, and untraceable behavior may lead to errors.
在我发现这一点后,我有一个问题:在使用getter和setter的时候,有没有什么可以做的,以便于使得类更严格?我发现:有是肯定有,但是这值得吗?增加额外层次的复杂性到代码就只是为了使用数量更少的括号?对于API定义,也可以不使用getter和setter,而这样一来就能解决这个问题。除非你是一个铁杆开发人员,并愿意继续进行,不然还有另一种解决方案,如下所述。
除了getter和setter方法,ECMAScript 2015(ES6)还自带proxy对象。proxy可以帮助你确定委托方法,这些委托方法可以在实际访问键执行之前,用来执行各种操作。事实上,它看起来像动态getter / setter方法。
proxy对象可以用来捕捉任何到类的实例的访问,并且如果在类中没有找到预先定义的getter或setter就会抛出错误。
为了做到这一点,必须执行下面两个操作:
创建基于Person原型的getter和setter清单。
创建将测试这些清单的Proxy对象。
让我们来实现它。
首先,为了找出什么样的getter和setter方法可以用在类Person上,可以使用getOwnPropertyNames和getOwnPropertyDescriptor:
var names = Object.getOwnPropertyNames(Person.prototype); var getters = names.filter((name) => { var result = Object.getOwnPropertyDescriptor(Person.prototype, name); return !!result.get; }); var setters = names.filter((name) => { var result = Object.getOwnPropertyDescriptor(Person.prototype, name); return !!result.set; });
在此之后,创建一个Proxy对象:
var handler = { get(target, name) { if (getters.indexOf(name) != -1) { return target[name]; } throw new Error('Getter "' + name + '" not found in "Person"'); }, set(target, name) { if (setters.indexOf(name) != -1) { return target[name]; } throw new Error('Setter "' + name + '" not found in "Person"'); } }; person = new Proxy(person, handler);
现在,只要你尝试访问person.fulName,就会显示Error: Getter “fulName” not found in “Person”的消息。
The above is the detailed content of Why is it bad to use getters and setters in JavaScript?. For more information, please follow other related articles on the PHP Chinese website!