Getters & Setters in Javascript
Many people know what getters and setters are in many programming languages. There are these wonderful things in JavaScript too, even though I found out about it recently (I’m dark uneducated). It will be discussed not only and not so much about methods
The easiest way, those getSomeProperty () / setSomeProperty (...):
This is the simplest and most obvious case. But also the worst. By storing data in
Everything, the data is hidden, and
However, we sacrificed a bit of the memory of our computer: creating methods in the constructor, and not in the prototype, we will allocate memory for them for each new instance of this type. But you can not worry about this. We will not have 100% of 1,000,000 such facilities. And the memory capacities of a modern PC allow us such luxury. Examples:
Of course, while everything is simple.
Improving, improving. I think the approach
There is a parameter, then setter. No parameter - getter. The main thing is to describe it in the JSDoc annotation :)
And here you can stop at this method. Yes, he is good, but we will go further!
Seamlessly move on to more interesting options. The first of these is some called legacy syntax. Because it has become so “common” from the time when JavaScript syntactic constructions like get / set (which will be discussed later) have not yet been implemented.
This code works fine in:
FF 1.0+,
Chrome 0.2+,
Safari 3.0+,
Opera 9.5+
and doesn’t work in:
IE - all versions, including 8.0
Yes, and there are some interesting features ( important! ):
Example (see comments in the code):
But this approach is already "according to Feng Shui." We look at an example:
This code works fine in:
FF 1.0+,
Chrome 0.2+,
Safari 3.0+,
Opera 9.5+
and does not work in:
IE - all versions, including 8.0.
I repeated the previous example (from legacy syntax, see above), but at what time times reduced the amount of code! This is wonderful (it would seem)!
But no. In browsers where get / set constructs are not supported, such code will cause a syntax error. Therefore, for now, I would refrain from using it (well, or cram into try-catch, which is not very).
In general, recording via get / set is completely analogous to recording through legacy syntax.
FF went further in getters and setters (I don’t know, by the way, why) and created +2 bicycles :)
No. 1:
Essentially, we can define named handler functions. That's all. I don’t know why anyone would need this.
No. 2:
This code works fine exclusively in :
FF 1.0+
Here you can also specify named handler functions, but there are 2 advantages over the previous one: setting an external handler function and setting getters / setters for attributes with invalid values (for writing through ". ") characters .
Although all this can be done through __defineGetter __ / __ defineSetter__. Because bicycles.
Yes, using either of these two methods will result in a SyntaxError everywhere except FF. Remember! :)
Internet Explorer, as always, said "I am not on your way" and made getters and setters in its own way.
Let's start with version 8. Here the method is implemented
The rules remain valid: a) the attribute of the same name is erased; b) one getter does not add setter automatically (and vice versa).
If you still want to apply this mechanism to non-DOM elements, you will have to dodge and make a substitution of your object for a DOM element.
For versions prior to IE 8.0, there is essentially no mechanism for getters and setters. However, there is a wonderful event
There are differences from the previous one
This approach works since IE 5.0 (as MSDN says ) and to the latest version at the moment. Well, and only for those who are already in the DOM .
I’ll leave the opportunity to implement a small cross-browser framework — you, comrades :) But I’ll say that making it is real. Although for IE it will not be easy.
In the article, I indicated in which the most popular browsers one or another getter and setter mechanism works. But these are far from all browsers. Therefore, I will clarify:
Firefox 0.9+ | Engine: Gecko 1.7+ (JS engine: SpiderMonkey / TraceMonkey)
Chrome 0.2+ | Engine: WebKit 522+ (JS engine: V8)
Safari 3.0+ | Engine: WebKit 522+ (JS engine: JavaScriptCore)
Opera 9.5+ | Engine: Presto 2.1+ (JS engine: Futhark)
IE 5.0 - IE 7.0 | Engine: Trident (unversioned) (JS engine: JScript 5.0 - JScript 5.7)
IE 8.0+ | Engine: Trident 4.0+ (JS engine: JScript 5.8+)
Take a close look at the Engine / JS engine. If I forgot to mention your browser, but it uses one of the listed engines (the version of the engine is important), then everything will work in it the same as in the mentioned browser.
Thank you for your attention.
getSomeProperty()/setSomeProperty(...), but about a more interesting implementation - pseudo-attributes, when changed, handler functions will be called.Obvious
The easiest way, those getSomeProperty () / setSomeProperty (...):
function MyObject(newVal) {
this._secretProperty = newVal;
}
MyObject.prototype = {
getSecretProperty: function() {
return this._secretProperty;
},
setSecretProperty: function(newVal) {
return this._secretProperty = newVal;
}
}
* This source code was highlighted with Source Code Highlighter.This is the simplest and most obvious case. But also the worst. By storing data in
this, we won’t hide it from the “evil hacker”. And it is quite possible to do so:var obj = new MyObject();
obj._secretProperty = 'Ха-ха! Я - злостный хакер, ваша защита мне нипочём!';
// Вот так обходится setSecretProperty. -- Капитан Очевидность :)
* This source code was highlighted with Source Code Highlighter.Step 2: hide data in the constructor closure
function MyObject(newVal) {
var _secretProperty = newVal;
this.getSecretProperty = function() {
return _secretProperty;
}
this.setSecretProperty = function(newVal) {
return _secretProperty = newVal;
}
}
* This source code was highlighted with Source Code Highlighter.Everything, the data is hidden, and
this._secretPropertyalready undefined. Get it, malicious hacker! However, we sacrificed a bit of the memory of our computer: creating methods in the constructor, and not in the prototype, we will allocate memory for them for each new instance of this type. But you can not worry about this. We will not have 100% of 1,000,000 such facilities. And the memory capacities of a modern PC allow us such luxury. Examples:
var obj = new MyObject(42);
alert(obj._secretProperty); // undefined
alert(obj.getSecretProperty()); // 42
obj._secretProperty = 9;
alert(obj._secretProperty); // 9 - НО...
alert(obj.getSecretProperty()); // 42 - фуух, а я уже испугался! :-)
obj.setSecretProperty(78);
alert(obj.getSecretProperty()); // 78
* This source code was highlighted with Source Code Highlighter.Of course, while everything is simple.
Step 3: in more JavaScript way
Improving, improving. I think the approach
getSomeProperty()/setSomeProperty(...)is too cumbersome. You can do it much more succinctly, but at the same time save PC memory and the time of developers who will later use it:function MyObject(newVal) {
var _secretProperty = newVal;
/**
* @param {Object} newVal - новое значение для _secretProperty. Не обязателен.
* Если указан, то secretProperty(...) действует, как setter.
* Если не указан, то secretProperty() действует, как getter.
*/
this.secretProperty = function(newVal) {
if (typeof newVal != "undefined")
_secretProperty = newVal;
return _secretProperty;
}
}
// Примеры:
var obj = new MyObject(42);
alert(obj._secretProperty); // undefined
alert(obj.secretProperty()); // 42
obj.secretProperty(78);
alert(obj.secretProperty()); // 78
* This source code was highlighted with Source Code Highlighter.There is a parameter, then setter. No parameter - getter. The main thing is to describe it in the JSDoc annotation :)
And here you can stop at this method. Yes, he is good, but we will go further!
Legacy syntax
Seamlessly move on to more interesting options. The first of these is some called legacy syntax. Because it has become so “common” from the time when JavaScript syntactic constructions like get / set (which will be discussed later) have not yet been implemented.
var obj = {
real_a: 1
};
// Во всех объектах, включая DOM, могут присутствовать (жаль, но в IE их нет) методы:
// __defineGetter__, __defineSetter__, __lookupGetter__, __lookupSetter__
// Первые два задают getter'ы и setter'ы:
obj.__defineGetter__("a", function() { return this.real_a * 2; });
obj.__defineSetter__("a", function(v) { return this.real_a = v / 2; });
// Вторые два проверяют наличие getter'ов и setter'ов:
alert(obj.__lookupGetter__('a')) // some function-getter for 'a'
alert(obj.__lookupGetter__('b')) // undefined
alert(obj.__lookupSetter__('a')) // some function-setter for 'a'
// Примеры
alert(obj.real_a); // 1
obj.a = 9; // setter в действии
alert(obj.real_a); // 4.5
alert(obj.a); // 9; getter в действии
* This source code was highlighted with Source Code Highlighter.This code works fine in:
and doesn’t work in:
Yes, and there are some interesting features ( important! ):
- if you specify only one (getter / setter), the second will not be created automatically.
- if before creating getter / setter the object already had the same name property - it will be erased (which is logical)
Example (see comments in the code):
var obj = {attr:5};
obj.__defineSetter__('attr', function(val){
// Здесь ни в коем случае нельзя писать:
// this.attr = val * 5
// т.к. это бесконечно зациклит setter.
// Название должно отличаться:
this.v = val * 5;
})
alert(obj.attr); // undefined. Как видим, задание getter/setter "затирает" одноимённый аттрибут, что был до этого.
// Теперь у нас - псевдо-аттрибут с функциями-обработчиками.
// И то, что мы установили setter для 'attr', не создало автоматически одноимённый getter.
obj.attr = 3; // Однако setter вполне исправный :)
alert(obj.v); // 15. Вот сюда setter записал данные.
* This source code was highlighted with Source Code Highlighter.get / set
But this approach is already "according to Feng Shui." We look at an example:
var obj = {
real_a: 1,
get a() { return this.real_a * 2; },
set a(v) { return this.real_a = v / 2; }
};
// Примеры
alert(obj.real_a); // 1
obj.a = 9; // setter в действии
alert(obj.real_a); // 4.5
alert(obj.a); // 9; getter в действии
// __lookupGetter__/__lookupSetter__ - продолжают работать:
alert(obj.__lookupGetter__('a')) // some function-getter for 'a'
// и т.д.
* This source code was highlighted with Source Code Highlighter.This code works fine in:
and does not work in:
I repeated the previous example (from legacy syntax, see above), but at what time times reduced the amount of code! This is wonderful (it would seem)!
But no. In browsers where get / set constructs are not supported, such code will cause a syntax error. Therefore, for now, I would refrain from using it (well, or cram into try-catch, which is not very).
In general, recording via get / set is completely analogous to recording through legacy syntax.
Firefox way
FF went further in getters and setters (I don’t know, by the way, why) and created +2 bicycles :)
No. 1:
var obj = {
get a b() { /**/ },
set a c(v) { /**/ }
};
alert(obj.__lookupGetter__('a')) // function b()
alert(obj.__lookupSetter__('a')) // function c(v)
// и т.д.
* This source code was highlighted with Source Code Highlighter.Essentially, we can define named handler functions. That's all. I don’t know why anyone would need this.
No. 2:
function magicGetter() { return 42; };
var obj = {
a getter:function b() { /**/ },
a setter: function(v) { /**/ },
'^_^' getter: magicGetter
};
alert(obj.__lookupGetter__('a')) // function b()
alert(obj.__lookupSetter__('a')) // function(v)
alert(obj.__lookupGetter__('^_^')) // function magicGetter()
alert(obj["^_^"]); // 42
* This source code was highlighted with Source Code Highlighter.This code works fine exclusively in :
Here you can also specify named handler functions, but there are 2 advantages over the previous one: setting an external handler function and setting getters / setters for attributes with invalid values (for writing through ". ") characters .
Although all this can be done through __defineGetter __ / __ defineSetter__. Because bicycles.
Yes, using either of these two methods will result in a SyntaxError everywhere except FF. Remember! :)
IE way
Internet Explorer, as always, said "I am not on your way" and made getters and setters in its own way.
IE 8.0+
Let's start with version 8. Here the method is implemented
Object.defineProperty(...)But, sadly, it applies only to DOM elements.// Работает только в IE 8.0 и выше
Object.defineProperty(document.body, "description", {
get : function () {
alert('Getting description...');
return this.desc;
},
set : function (val) {
alert('Setting description...');
this.desc = val;
}
});
document.body.description = "Content container"; // "Setting description..."
alert(document.body.description); // "Getting description..." -> "Content container"
alert(document.body.desc); // "Content container"
// Попробуем повторить не для DOM-элемента:
var obj = {};
Object.defineProperty(obj, "prop", {
get : function () { /**/ }
}); // JS ERROR: Object doesn't support this action. Вот так-то. Пока не могём.
* This source code was highlighted with Source Code Highlighter.The rules remain valid: a) the attribute of the same name is erased; b) one getter does not add setter automatically (and vice versa).
If you still want to apply this mechanism to non-DOM elements, you will have to dodge and make a substitution of your object for a DOM element.
IE 5.0+
For versions prior to IE 8.0, there is essentially no mechanism for getters and setters. However, there is a wonderful event
onpropertychange. It is present only in DOM elements. Using it, you can create a setter. However, I never found anything for getters. Example:document.body.onpropertychange = function() {
var pn = window.event.propertyName;
var pv = window.event.srcElement[window.event.propertyName];
if (pn == "description")
alert(pv);
}
document.body.description = "Content container"; // setter alert "Content container"
alert(document.body.description); // "Content container". Это не getter. Просто при присвоении значения у объекта добавился новый атрибут description
// Можно динамически создать DOM-элемент и навесить на него onproperychange
var el = document.createElement("DIV");
el.onpropertychange = function() {
var pn = window.event.propertyName;
var pv = window.event.srcElement[window.event.propertyName];
if (pn == "description")
alert(pv);
}
el.description = "Content container"; // хм. Странно, ничего не произошло...
// Добавим этот элемент в DOM-модель:
document.appendChild(el);
el.description = "Content container"; // setter alert "Content container"
* This source code was highlighted with Source Code Highlighter.There are differences from the previous one
Object.defineProperty:- This is - an event that fulfills immediately after being any attribute of the object has been changed
- This is an event, not a pseudo-attribute, therefore the existing attribute of the same name is not overwritten, but behaves in the same way as a regular attribute should behave. Although what kind of homonymity can we talk about here? :)
This approach works since IE 5.0 (as MSDN says ) and to the latest version at the moment. Well, and only for those who are already in the DOM .
Conclusions, or for now
I’ll leave the opportunity to implement a small cross-browser framework — you, comrades :) But I’ll say that making it is real. Although for IE it will not be easy.
reference
In the article, I indicated in which the most popular browsers one or another getter and setter mechanism works. But these are far from all browsers. Therefore, I will clarify:
Take a close look at the Engine / JS engine. If I forgot to mention your browser, but it uses one of the listed engines (the version of the engine is important), then everything will work in it the same as in the mentioned browser.
Thank you for your attention.