Protected methods in javascript ES5

 3r33557. 3r3-31. A lot of great articles have been written about the object model in jаvascript. And about the various ways to create private class members on the Internet is full of decent descriptions. But about protected methods - there is very little data. I would like to fill this gap and tell you how to create protected methods without libraries on pure ECMAScript 5.3r33636 jаvascript.  3r33557. 3r33536.  3r33557. In this article: 3r33636.  3r33557. 3r33536.  3r33557. 3r3386.  3r33557. 3r3394. Why do we need protected members of the class 3r395.  3r33557. 3r3394. 3r318. What you need to understand the presented method
3r395.  3r33557. 3r3394. Helper class ProtectedError 3r395.  3r33557. 3r3394. Implementing protected members (methods and properties) for classes declared as a function (ECMAScript 5) 3r3107. 3r395.  3r33557.
3r33536.  3r33557. 3r3335. Link to git-hub repository with source code and tests.
“Understanding OOP in JS[часть №1]»3r3107. . 3r33536.  3r33557. About private properties there is a good and, in my opinion, fairly complete description of
as many as 4 different ways to create private members of the class
on the website MDN. 3r33536.  3r33557. 3r33536.  3r33557. As for the Object.defineProperty method, it will allow us to hide properties and methods from for-in loops, and, as a result, from the serialization algorithms: 3r33636.  3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. function MyClass () {
Object.defineProperty (MyClass.prototype, 'protectedNumber', {3r33557. Value: 1?
Enumerable: false
}); 3r33557. this.publicNumber = 25; 3r33557.}; 3r33557. 3r33557. var obj1 = new MyClass (); 3r33557. for (var prop in obj1) {3r33557. console.log ('property:' prop); //prop will never be 'protectedNumber'
} 3r33557. console.log (JSON.stringify (obj1)); //Displays {'publicNumber': 25}
3r33545. 3r33546. 3r33536.  3r33557. Such a concealment is necessary, but this, of course, is not enough. it is still possible to call the method /property directly: 3r33636.  3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. console.log (obj1.protectedNumber); //Displays 12.
3r33545. 3r33546. 3r33536.  3r33557. 3r3165. Helper class ProtectedError
3r33536.  3r33557. First we need the ProtectedError class, which is inherited from Error, and which will be thrown out if there is no access to a protected method or property. 3r33536.  3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. function ProtectedError () {
this.message = "Encapsulation error, trying to address is protected."; 3r33557.} 3r33557. ProtectedError.prototype = new Error (); 3r33557. ProtectedError.prototype.constructor = ProtectedError; 3r33557. 3r33545. 3r33546. 3r33536.  3r33557. 3r3165. Implementing protected class members in ES5
3r33536.  3r33557. Now that we have a ProtectedError class and we understand what Object.defineProperty does with enumerable: false, let's look at creating a base class that wants to share the protectedMethod method with all its derived classes, but hide it from all the others: 3r33635.  3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. function BaseClass () {3r3353557. if (! (this instanceof BaseClass)) 3r33557. return new BaseClass (); 3r33557. 3r33557. var _self = this; //Close the class instance so that in the future it does not depend on the context 3r33557. 3r33557. /** @summary Checks access to protected members of a class * /
function checkAccess () {3r3353557. if (! (this instanceof BaseClass)) 3r33557. throw new ProtectedError (); 3r33557. if (this.constructor === BaseClass)
throw new ProtectedError ()
} 3r33557. Object.defineProperty (_self, 'protectedMethod', {
Enumerable: false, //hide the method from for-in cycles
Configurable: false, //forbid to override this property
Value: function () {
////Since we are here, it means that we were called either as a public method on an instance of the Base class, or from derived classes 3-33557. CheckAccess.call (this); //Check access. 3r35757. ProtectedMethod (); 3r35757.} 3r33557.}); 3r33557. function protectedMethod () {
//If you need to refer to members of this class, 3r33557. //then access them not through this, but through _self
return 'example value'; 3r33557.} 3r33557. 3r33557. this.method = function () {3r3353557. protectedMethod (); //the right way to call a protected method from other methods of the BaseClass
class. //this.protectedMethod (); //Wrong way to call, because it will throw a ProtectedError
exception.} 3r33557.} 3r33557. 3r33545. 3r33546. 3r33536.  3r33557. 3r33518. Description of the constructor of the class BaseClass
3r33536.  3r33557. Perhaps you will be confused by the check:
 3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. if (! (this instanceof BaseClass)) 3r33557. return new BaseClass (); 3r33557. 3r33545. 3r33546. This check is "an amateur." You can remove it, it is not related to protected methods. However, I personally leave it in my code, since it is needed for those cases when an instance of the class is created incorrectly, i.e. without the keyword new. For example, like this:
 3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. var obj1 = BaseClass (); 3r33557. //or so:
var obj2 = BaseClass.call ({}); 3r33557. 3r33545. 3r33546. 3r33536.  3r33557. In such cases, do what you want. You can, for example, generate an error: 3r33536.  3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. if (! (this instanceof BaseClass)) 3r33557. throw new Error ('Wrong instance creation. Maybe operator "new" was forgotten'); 3r33557. 3r33545. 3r33546. 3r33536.  3r33557. Or you can simply create an instance correctly, as is done in BaseClass. 3r33536.  3r33557. 3r33536.  3r33557. Next, we save the new instance to the _self variable (why I’ll need to explain this a bit later). 3r33536.  3r33557. 3r33536.  3r33557. 3r33518. The description of a public property named protectedMethod
3r33536.  3r33557. Entering the method, we call the context check on which we were called. It is better to put the check into a separate method, for example, checkAccess, since the same check will be needed in all protected methods and class properties. So, first of all, we check the context type of the “this” call. If this has a type other than BaseClass, then the type is neither BaseClass itself, nor any of its derivatives. We prohibit such calls. 3r33536.  3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. if (! (this instanceof BaseClass)) 3r33557. throw new ProtectedError (); 3r33557. 3r33545. 3r33546. 3r33536.  3r33557. How can this happen? For example: 3r33636.  3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. var b = new BaseClass (); 3r33557. var someObject = {}; 3r33557. b.protectedMethod.call (someObject); //In this case, inside protectedMethod this will be equal to someObject and we will catch it, because someObject instanceof BaseClass would be false
3r33545. 3r33546. 3r33536.  3r33557. In the case of derived classes, the expression of this instanceof BaseClass will be true. But for BaseClass instances, the expression of this instanceof BaseClass will be true. Therefore, to distinguish instances of the BaseClass class from instances of derived classes, we check the constructor. If the constructor matches BaseClass, then our protectedMethod is called on the BaseClass instance, as is the usual public method:
 3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. var b = new BaseClass (); 3r33557. b.protectedMethod (); 3r33557. 3r33545. 3r33546. 3r33536.  3r33557. We prohibit such calls: 3r33636.  3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. if (this.constructor === BaseClass)
throw new ProtectedError (); 3r33557. 3r33545. 3r33546. 3r33536.  3r33557. Next comes the call to the protectedMethod closed method, which, in fact, is the method we are protecting. Inside the method, if there is a need to refer to the members of the BaseClass class, you can do this using a saved instance of _self. It was for this purpose that _self was created to have access to the members of the class from all closed /private methods. Therefore, if in your protected method or property you do not need to access the members of the class, you can not create the _self variable. 3r33536.  3r33557. 3r33536.  3r33557. 3r33518. Calling a protected method within the BaseClass class
3r33536.  3r33557. Inside the BaseClass class, a protectedMethod should be accessed only by name, and not through this. Otherwise, inside protectedMethod we will not be able to distinguish whether we were called as a public method or from inside the class. In this case, the closure saves us - protectedMethod behaves like a normal private method, closed inside a class and visible only inside the scope of the BaseClass function. 3r33536.  3r33557. 3r33536.  3r33557. 3r33518. DerivedClass
derived class description. 3r33536.  3r33557. Now let's look at the derived class and how to make it access to the protected method of the base class. 3r33536.  3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. function DerivedClass () {3r3353557. var _base = {
protectedMethod: this.protectedMethod.bind (this)
}; 3r33557. /** @summary Checks access to protected members of a class * /
function checkAccess () {3r3353557. if (this.constructor === DerivedClass)
throw new ProtectedError (); 3r33557.} 3r33557. 3r33557. //Override the method for all 3r33557. Object.defineProperty (this, 'protectedMethod', {
Enumerable: false, //because we are creating a property on a specific instance of this
Configurable: false, //then you must again disable redefinition and display in for-in loops
//Now we can declare an anonymous method
Value: function () {3r33535. CheckAccess.call (_self);
Return _base.protectedMethod (); 3r35757.}
}); 3r33557. //Use the protected base class method in the derived 3r33557. this.someMethod = function () {3r33557. console.log (_base.protectedMethod ()); 3r33557.} 3r33557.} 3r33557. DerivedClass.prototype = new BaseClass (); 3r33557. Object.defineProperty (DerivedClass.prototype, 'constructor', {3r33557. Value: DerivedClass,
Configurable: false
}); 3r33557. 3r33545. 3r33546.
 3r33557. 3r33518. Description of the derived class constructor
3r33536.  3r33557. In the derived class, we create an _base object in which we place a reference to the protectedMethod method of the base class, closed to the context of the derived class via the standard bind method. This means that the _base.protectedMethod () call; inside protectedMethod this is not an _base object, but an instance of the DerivedClass class. 3r33536.  3r33557. 3r33536.  3r33557. 3r33518. Description of the protectedMethod method inside the class DerivedClass
3r33536.  3r33557. In the DerivedClass class, it is necessary to declare the protectedMethod public method in the same way as we did in the base class via Object.defineProperty and check access in it by calling the checkAccess method or performing the check directly in the method: 3r3536.  3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. Object.defineProperty (DerivedClass.prototype, 'protectedMethod', {
Enumerable: false,
Configurable: false,
Value: function () {3r3557. Withdraw () 3r3-3557. 3r3-3557. Return _base.protectedMethod (); 3r3-3557.} 3r3-3557.}); 3r33557. 3r33545. 3r33546. 3r33536.  3r33557. Check - [i] “But didn’t they call us as a simple public method?” 3r3-3401. For instances of the class DerivedClass, the constructor will be equal to DerivedClass. If so, then we generate an error. Otherwise, we send it to the base class and it will already do all the other checks. 3r33536.  3r33557. 3r33536.  3r33557. So, in the derived class, we have two functions. One is declared via Object.defineProperty and is needed for classes derived from DerivedClass. It is public, and therefore it has a check that prohibits public calls. The second method is in the _base object, which is closed inside the DerivedClass class and therefore not visible to anyone from the outside and it is used to access the protected method from all DerivedClass methods. 3r33536.  3r33557. 3r33536.  3r33557. 3r33518. Protection of properties 3r3195. 3r33536.  3r33557. With properties, the work happens a little differently. The properties in BaseClass are defined as usual via Object.defineProperty, only in getters and setters you need to first add our check, i.e. call checkAccess:
 3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. function BaseClass () {3r3353557. function checkAccess () {}
3r33557. var _protectedProperty; 3r33557. Object.defineProperty (this, 'protectedProperty', {3r3-3557. Get: function () {3r3-33557. CheckAccess.call (this); 3r?557. Return _protectedProperty; 3r3557.}, 3r35757. Set: function (value) {3r35757. call (this);
_protectedProperty = value;
},
enumerable: false,
configurable: false
}); 3r33557.} 3r33557. 3r33545. 3r33546. 3r33536.  3r33557. Inside the BaseClass class, we access the protected property not through this, but to the closed variable _protectedProperty. In case it is important for us to work the getter and setter when using the property inside the BaseClass class, then we need to create private getProtectedPropety and setProtectedProperty methods, inside which there will be no checks, and call them already. 3r33536.  3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. function BaseClass () {3r3353557. function checkAccess () {}
3r33557. var _protectedProperty; 3r33557. Object.defineProperty (this, 'protectedProperty', {3r-3557. Get: function () {3r-3557. CheckAccess.call (this); 3r-3557. Return getProtectedProperty ();
},
Set: function (value) {
checkAccess.call (this); 3r33535. setProtectedProperty (value); 3r33535.}, 3r33557. enumerable: false,
configurable: false 3r33557.}); 3r33557. function getProtectedProperty () {3r3353557. //Do a useful job
return _protectedProperty; 3r33557.} 3r33557. function setProtectedProperty (value) {3r3353557. //Do a useful job
_protectedProperty = value; 3r33557.} 3r33557.} 3r33557. 3r33545. 3r33546. 3r33536.  3r33557. In derived classes, working with properties is a bit more complicated, since property cannot be replaced with context. Therefore, we will use the standard Object.getOwnPropertyDescriptor method to get the getter and setter as a function from the base class property, which can already change the calling context: 3r33636.  3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. function DerivedClass () {3r3353557. function checkAccess () {}
var _base = {
protectedMethod: _self.protectedMethod.bind (_self),
}; 3r33557. var _baseProtectedPropertyDescriptor = Object.getOwnPropertyDescriptor (_self, 'protectedProperty'); 3r33557. 3r33557. //declare a protected property on the _base object 3r33557. //inside the class DerivedClass to access the protected property 3r33557. Object.defineProperty (_base, 'protectedProperty', {3r3-3557. Get: function () {3r3353557. Return _baseProtectedPropertyDescriptor.get.call (_self); 3r3557.}, 3r35757. Set: function (value) {3r33557. call (_self, value); 3r33557.} 3r3-33557.}) 3r33557. 3r33557. //Here we declare the property public, so that classes derived from DerivedClass have the opportunity to get to the protected method. 3r33557. Object.defineProperty (_self, 'protectedProperty', {
Get: function () {
CheckAccess.call (_self);
Return base.protectedProperty;
},
Set: function (value) {3r35735 set checkAccess.call (_self); 3r33535. _base.protectedProperty = value;
},
enumerable: false,
configurable: false
}); 3r33557.} 3r33557. 3r33545. 3r33546. 3r33536.  3r33557. 3r33518. Description of inheritance
3r33536.  3r33557. And the last thing I would like to comment on is the inheritance of DerivedClass from BaseClass. As you probably know, DerivedClass.prototype = new BaseClass (); not only creates a prototype, but also rewrites its constructor property. Because of this, for each instance of DerivedClass, the constructor property becomes equal to BaseClass. To fix this, usually after creating a prototype, the constructor property is rewritten:
 3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. DerivedClass.prototype = new BaseClass (); 3r33557. DerivedClass.prototype.constructor = DerivedClass; 3r33557. 3r33545. 3r33546. 3r33536.  3r33557. However, so that no one rewrites this property after us, use the same Object.defineProperty. The configurable property: false prevents the property from being redefined again: 3r33636.  3r33557. 3r33536.  3r33557. 3r33538. 3r? 3539. DerivedClass.prototype = new BaseClass (); 3r33557. Object.defineProperty (DerivedClass.prototype, 'constructor', {3r33557. Value: DerivedClass,
Configurable: false
}); 3r33557. 3r33545. 3r33546. 3r33553. 3r33557. 3r33557. 3r33550. ! function (e) {function t (t, n) {if (! (n in e)) {for (var r, a = e.document, i = a.scripts, o = i.length; o-- ;) if (-1! == i[o].src.indexOf (t)) {r = i[o]; break} if (! r) {r = a.createElement ("script"), r.type = "text /jаvascript", r.async =! ? r.defer =! ? r.src = t, r.charset = "UTF-8"; var d = function () {var e = a.getElementsByTagName ("script")[0]; e.parentNode.insertBefore (r, e)}; "[object Opera]" == e.opera? a.addEventListener? a.addEventListener ("DOMContentLoaded", d,! 1): e.attachEvent ("onload", d ): d ()}}} t ("//mediator.mail.ru/script/2820404/"""_mediator") () (); 3r? 3551. 3r33557. 3r33553. 3r33557. 3r33557. 3r33557. 3r33557.
+ 0 -

Add comment