5

I'm learning about classes in ES6...

I'd like to use private properties, in some form, so that only certain methods can be called from an instance of a class.

For example using Symbol...

/* A.js */

const _privateMethod = Symbol('_privateMethod')
class A {
    [_privateMethod]() {
        return 'yup'
    }
    do() {
        return this[_privateMethod]()
    }
}

const a = new A()
a.do() /* yup */

..._privateMethod cannot be called directly. So far so good.

However, I am wondering how to then go about overriding _privateMethod in a class that inherits from A. For example, the following will not work:

/* B.js */

const _privateMethod = Symbol('_privateMethod')
class B extends A {
    [_privateMethod]() {
        return 'nope'
    }
}

const b = new B()
b.do() /* yup */

How is it recommended to do this?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
user1031947
  • 6,294
  • 16
  • 55
  • 88
  • Possible duplicate of [how to override a javascript function](https://stackoverflow.com/questions/5409428/how-to-override-a-javascript-function) – Cruiser Jan 29 '18 at 20:27
  • `[_privateMethod]` can still be called directly. What is the reason why you want to make the method private? – t.niese Jan 29 '18 at 20:28
  • 1
    @Cruiser I don't see how this question (about how to override a class method identified by a symbol) is at all similar to your proposed duplicate – apsillers Jan 29 '18 at 20:28
  • 2
    Note that you do not truly have private methods. You could, for example, use `a [ Object.getOwnPropertySymbols(Object.getPrototypeOf(a))[0] ]()` to call `a[_privateMethod]` from another file that can't access `_privateMethod` directly. – Paul Jan 29 '18 at 20:33
  • @Paulpro Yeah, I read about that. My original thought was that something is better than nothing. But seeing as a solution that uses Symbol requires hoop jumping, and isn't completely private anyways, I'm rethinking whether it is really worth it. Just sticking with a naming convention for 'private' properties is starting to seem simpler / better – user1031947 Jan 29 '18 at 20:42
  • 1
    OOP best practices are there to help a developer, not to be observed religiously and shoot in the foot. If the benefits are unclear, it's not worth it. – Estus Flask Jan 29 '18 at 21:00

4 Answers4

3

Calling Symbol('_privateMethod') again creates a new, distinct symbol (even if it has the same description). You are creating a different method with a different key, not overwriting the original one.

You will need to use the exact same symbol to define the method in the subclass. You can either get it from Object.getOwnPropertySymbols(A.prototype) or by exporting the _privatMethod symbol as a constant from A.js and importing it in B.js (together with the class A). However, if you want to make the class extensible, I would recommend to simply not use symbols at all.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 1
    Thanks, that makes perfect sense! (I'm beginning to think it is simpler just to use all public methods, with an underscore for a naming convention) – user1031947 Jan 29 '18 at 20:37
2

The reason why programming concepts are used in practice is because they provide some benefits to a developer, this includes encapsulation, too. If it provides more disadvantages than benefits, this means that it shouldn't be applied, or the way it was applied was wrong.

JavaScript doesn't have provide encapsulation as language feature. Symbol is an acceptable way to implement it, but it has its specificity that can make it unsuitable for the task.

A symbol that serves as an identifier for private (protected) member should always be exported together with a class it belongs to:

export const _privateMethod = Symbol('_privateMethod');
export class A {
    [_privateMethod]() {/*...*/}
    /*...*/
}

...

import { _privateMethod, A } from './a';

class B extends A {
    [_privateMethod]() {/*...*/}
}

If this is not possible or practical, this means that a symbol is inappropriate choice for encapsulation, because it provides disadvantages without any real benefits.

Since information hiding doesn't serve security purposes in JavaScript (symbols are accessible with Object.getOwnPropertySymbols), the alternative to encapsulation mechanism is the use of Hungarian notation and/or JSDoc annotations. They provide a developer with necessary information about public interface:

export class A {
    /** @protected */
    _privateMethod() {/*...*/}
    /*...*/
}
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
0

You can use the Object.getOwnPropertySymbols() function:

const A = (function() {
  const _privateMethod = Symbol('_privateMethod')
  return class A {
      [_privateMethod]() {
          return 'yup'
      }
      do() {
          return this[_privateMethod]()
      }
  }
}());

(function() {
  const _privateMethod = Object.getOwnPropertySymbols(A.prototype)
    .find(x => x.toString() === 'Symbol(_privateMethod)')
  class B extends A {
      [_privateMethod]() {
          return 'nope'
      }
  }
  
  const b = new B()
  console.log(b.do());
}());
Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177
0

you already has the right answer, check this

const _privateMethod = Symbol('_privateMethod')
class A {
    [_privateMethod]() {
        return 'yup'
    }
    do() {
        return this[_privateMethod]()
    }
}

const a = new A()
document.write(a.do()+ "<br><br>" )

class B extends A {
    [_privateMethod]() {
        return 'nope'
    }
}

const b = new B()
document.write(b.do()+ "<br><br>")
LPZadkiel
  • 561
  • 1
  • 3
  • 16