开发者

Nested class in javascript, inheritance of private methods

开发者 https://www.devze.com 2022-12-20 17:59 出处:网络
i\'m quite a newbie in javascript, and i\'m spending some time trying to create namespaced objects in js.

i'm quite a newbie in javascript, and i'm spending some time trying to create namespaced objects in js.

Now, that's what i'm trying to do:

MainObject = function() {

    var privateVariable = "i'm private";

    var privateMethod = function() {
        // doSomething
   开发者_如何学编程 }

    this.publicMethod = function() {
        // doPublicSomething
    }
}

MainObject.prototype.nested = function() {

    this.publicNestedMethod = function() {

        // that's not working at all
        this.privateMethod(privateVariable);

    }
}

MyObject = new MainObject();

MyObject.publicMethod();
MyObject.publicNestedMethod();

I tried to include the nested class inside the first one, but it's not working also if i try:

this.nested = function() {

    var mainObject = this;

    return {
        publicNestedMethod = function() {
            mainObject.privateMethod();             
        }   
    }
}();

Someone can help me please? i'm gonna loose my mind on this.

Phaedra.


Closures are a lexical feature, not a semantic one. If the object is outside the lexical scope of another, it can no longer be "nested" and access the former's local variables. In the code of your nested function/class, there's no such thing as this.privateMethod, because privateMethod is never made to be a property of MainObject. It's simply a local variable inside a function.

There's no such things as "private properties", "private methods" or "private members" in JavaScript. Hell, there's no such thing as a "class". Some people like to emulate private members using local variables as above, but doing so results in cases like this, where the discrepancy between the two concepts comes and bites one in the behind.

To conclude, it is a bad idea to write Java code, with all its OO techniques in JS, just as it is a bad idea to write C code, with all its pointers and unbounded buffers, in C#. Sure, in both cases you can do it, but you would be failing to appreciate and exploit the language's features this way.

And now that I'm done with the rant, you can do something like this to get "namespaced" functions:

MainObject = function() {
    var privateVariable = "I'm private";

    var privateMethod = function() {
        alert('Private');
    }

    this.publicMethod = function() {
        alert('Public');
    }

    this.nested = {
      publicNestedMethod: function() {
        privateMethod();
      }
    };

    // or

    this.nested = (function() {
      var nestedPrivate = 5;

      return {
        publicNestedMethod: function() {
          alert(nestedPrivate);
          privateMethod();
        }
      };
    })();
}

MyObject = new MainObject();

MyObject.publicMethod();
MyObject.nested.publicNestedMethod();​


Using the convention of underscore for "private" methods is a reasonable way to keep things organized.

  MainObject = function() {

       this._privateVariable = "i'm private";

       this._privateMethod = function() {
          // doSomething
       }

        this.publicMethod = function() {
          // doPublicSomething
        }
}


Well to provide the benefit of prototypal inheritance where all "subclasses" share a single instance of the method in prototype, but to ALSO provide the feature of inheriting private instances... I came up with:

function Person(name,latentPower){
    var privatesForChildren =   { password:"xyz"
                                 ,latentPower:"invisibility"}
    this.inherit = function(){
        for(v in privatesForChildren){
            eval("var " + v + "=privatesForChildren['" + v + "'];");
        }
    }
    this.name = name;
    this.revealName = function(){ alert("My name is" + this.name + "."); }  
    this.revealPowers = function(){ alert("I'm normal."); } 
}       
function Mutant(name,latentPower,fuel){
    this.inherit.call(this);   // Inherit private instance variables
    var fuel = fuel;
    this.name = name;
    this.revealPowers = function(){
    alert("I manifest the powers of " + latentPower + " when I " + fuel + ".");
    }
}
Mutant.prototype = new Person;
Mutant.prototype.constructor = Mutant;

bob = new Person("Bob","telekenesis");
jim = new Mutant("Jim","nausea","eat pizza");
buford = new Mutant("Buford","Teflon Man","breathe");

jim.revealName(); //Inherited properly from prototype
bob.revealPowers();
jim.revealPowers();
buford.revealPowers();  //distinct from Jim's so is an "instance var"
alert(bob.latentPower); //returns undefined 
alert(buford.latentPower); //returns undefined, so is "private".

How useful is that?


JavaScript Classes and Inheritance (ES6)

According to ES6, you can use JavaScript classes and inheritance to accomplish what you need.

JavaScript classes, introduced in ECMAScript 2015, are primarily syntactical sugar over JavaScript's existing prototype-based inheritance.

Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes

I'm showing the examples below with variables, but it can be applied also to functions.


Inheritance (1st Approach)

This solution can only be used with getters for your private variables, otherwise your subclass will not get access to them.

class Main {
  constructor() {
    let privateVariable = "private";
    this.publicVariable = "public";

    this.getPrivateVariable = () => {
      return privateVariable;
    }
  }
}

Main.Sub = class Sub extends Main {
  getAllVariables() {
    return this.publicVariable + "-" + this.getPrivateVariable();
  }
}

// Testing...

let main = new Main();
let sub = new Main.Sub();

console.log(main.privateVariable); // undefined
console.log(main.publicVariable); // "public"

console.log(sub.privateVariable); // undefined
console.log(sub.publicVariable); // "public"

console.log(main.getPrivateVariable()); // "private"
console.log(sub.getPrivateVariable()); // "private"
console.log(sub.getAllVariables()) // "public-private"

Nesting (2nd Approach)

Maybe this solution is better for you because it doesn't expose your private variables outside the Main and Nested classes.

class Main {
  constructor() {
    let privateVariable = "private";
    this.publicVariable = "public";
    
    Main.Nested = class Nested extends Main {
      getAllVariables() {
        return this.publicVariable + "-" + privateVariable;
      }
    }
  }
}

// Testing...

let main = new Main();
let nested = new Main.Nested();

console.log(main.privateVariable); // undefined
console.log(main.publicVariable); // "public"

console.log(nested.privateVariable); // undefined
console.log(nested.publicVariable); // "public"

console.log(main.getPrivateVariable); // undefined
console.log(nested.getPrivateVariable); // undefined
console.log(nested.getAllVariables()) // "public-private"


What OO system lets you inherit private methods? Part of being private is being unaccessible from other objects.

In JS in particular, "private members" are really just local variables of the function where they are declared. JS doesn't have typical OO notions of "class", "inheritance", "public", and "private", so you can't expect to copy your OOP techniques verbatim from other OOP languages.


It is a convention. You can imitate OO Java techniques like private members but that's not recommended. You can imitate in this way:

MyFunction = function(options){
   var private = {};
   //to reference MyFunction as a context
   var that = this;

   function privateFunctionThatCallPublicMethod(){
      that.publicFunction("hello");
   }

   this.publicFunction = function(params){
      alert(params + "  " + private);  
   }
   ...
}

var instance = new MyFunction({oneOption:'fsdfsad'});

This is the bests approach i found to emulate OO Java Techniques...

But there is a problem, is very inefficient... You must use prototype instead, because otherwise it would create one object per function per instance of the "class".

MyFunction = function(options){
   this._private = {};
}

MyFunction.prototype._privateFunctionThatCallPublicMethod = function(){
   this.publicFunction("hello");
}

MyFunction.prototype.publicFunction = function(params){
   alert(params + "  " + this._private);  
}

Like you think private members are (in this way) a convention. Also, there is another thing you must know...

When you pass a function of an object as a parameter to another function you must bind the context of the function...

function bind(fnThis, fn) {
  return function(){
    return fn.apply(fnThis, arguments);
  };
}

function makeSomething(callback){
  callback("hello");
}

var instance = new MyFunction();
makeSomething(bind(instance, instance.publicFunction));

This is because you must bind "this" as instance in the body of the publicFunction, otherwise is gonna be "window" instead.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号