📜 ⬆️ ⬇️

Getters & setters in javascript

Many people know what getters and setters are in many programming languages. There are these wonderful things in JavaScript, even though I learned about it quite recently (I'm uneducated for darkness). It will not only and not so much about the getSomeProperty()/setSomeProperty(...) methods, but about a more interesting implementation - pseudo-attributes, which, when changed, will call handler functions.



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 the worst. By storing the data in this , we will not hide it from the “evil hacker”. And you can do this:
var obj = new MyObject();
obj._secretProperty = '-! - , !' ;
// setSecretProperty. -- :)

* This source code was highlighted with Source Code Highlighter .

')

Step 2: hide the data in the circuit of the constructor


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 is hidden, and this ._secretProperty already undefined . Get a malicious hacker!
However, we sacrificed a little 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 it. We will not have 100% 1000000 such objects. Yes, and the amount of memory of a modern PC quite allow us such a 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, for now, everything is simple.

Step 3: in more javascript way


We improve, we improve. I think that the getSomeProperty()/setSomeProperty(...) approach is too cumbersome. You can do much more concisely, and 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 on this method, you can already stop. Yes, he is good, but we will go on!

Legacy syntax


Smoothly proceed to more interesting options. The first one is called legacy syntax. Because it has been “worn out” since those times when the get / set JavaScript syntax has not been implemented yet (which will be discussed later).
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 great in:
Icon: Firefox FF 1.0+, Icon: Chrome Chrome 0.2+, Icon: Safari Safari 3.0+, Icon: Opera Opera 9.5+
And does not work in:
Icon: IE IE - all versions including 8.0

Yes, and there are several interesting features ( important! ):

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”. See 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 great in:
Icon: Firefox FF 1.0+, Icon: Chrome Chrome 0.2+, Icon: Safari Safari 3.0+, Icon: Opera Opera 9.5+
And does not work in:
Icon: IE IE - all versions including 8.0

I repeated the previous example (from legacy syntax, see above), but how many times the amount of code decreased! It is wonderful (it would seem)!
But no. In browsers where get / set constructs are not supported, such code will cause a syntax error. Therefore, I would refrain from using it (well, or stuff it into try-catch, which is not very).

In general, the record via get / set is completely analogous to the record through legacy syntax.

Firefox way


FF went further in getters and setters (I don't know, by the way, why) and created 2 bikes :)

# 1:
var obj = {
get ab() { /**/ },
set ac(v) { /**/ }
};

alert(obj.__lookupGetter__( 'a' )) // function b()
alert(obj.__lookupSetter__( 'a' )) // function c(v)
// ..

* This source code was highlighted with Source Code Highlighter .

In essence, we can define named handler functions. That's all. I do not know why anyone would need it.

â„–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 great only in :
Icon: Firefox FF 1.0+

Here, too, you can set named handler functions, but there are 2 advantages over the previous ones: setting an external handler function and setting getters / setters for attributes with invalid (for writing through ".") Characters .

Although all this can be done through __defineGetter __ / __ defineSetter__. Therefore, 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'm not on your way with you,” and did getters and setters in its own way.

IE 8.0+


Let's start with the 8th version. Here the Object.defineProperty(...) method is implemented Object.defineProperty(...) But, Object.defineProperty(...) , it is applicable 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 following rules remain fair: a) the attribute of the same name is deleted; b) one getter does not automatically add a setter (and vice versa).

If you still want to apply this mechanism to non-DOM elements, you will have to dodge and make the substitution of your object for the DOM element.

IE 5.0+


For versions prior to IE 8.0, there is essentially no mechanism for getters and setters at all. However, there is a wonderful event onpropertychange . It is present only in DOM elements. With it you can create a setter. However, for the getters, I never found anything. 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 Object.defineProperty :

This approach works from IE 5.0 (as MSDN says ) to the latest version at the moment. Well, only for those who are already in DOM .

Conclusions, or until all


I will leave the opportunity to implement a small cross-browser framework - to you, comrades :) But I will say that to make it is real. Although for IE it will not be easy.

reference


In the article, I indicated in which most popular browsers certain getter and setter mechanisms work. But this is not all browsers. Therefore, I will clarify:
Icon: Firefox Firefox 0.9+ | Engine: Gecko 1.7+ (JS engine: SpiderMonkey / TraceMonkey)
Icon: Chrome Chrome 0.2+ | Engine: WebKit 522+ (JS engine: V8)
Icon: Safari Safari 3.0+ | Engine: WebKit 522+ (JS engine: JavaScriptCore)
Icon: Opera Opera 9.5+ | Engine: Presto 2.1+ (JS engine: Futhark)
Icon: IE IE 5.0 - IE 7.0 | Engine: Trident (unversioned) (JS engine: JScript 5.0 - JScript 5.7)
Icon: IE 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 engines listed (the engine version is important), then everything will work in it just like in the mentioned browser.

Thank you for your attention.

Source: https://habr.com/ru/post/66242/


All Articles