Private prototypes that can share scope and instance access

I'm looking for a template that allows me to create a private scope that my function prototype has access to, and I need to be able to access an instance from that scope.

For example, this is how I currently implement "private methods" (ignore what the code actually does, just look at the structure.)

function InfoPreview() {
   this.element = document.createElement('div');
}

//Private Methods
InfoPreview.prototype.__newLine = function () {
  this.element.appendChild(createElement({tagName:'br'}));
}; 

InfoPreview.prototype.__padLeft = function(level) {
  var padding = createElement({tagName: 'span'});
  this.element.appendChild(padding);
  $(padding).width(level * 10);
};

InfoPreview.prototype.__print = function(string) {
  var span = createElement({ tagName: 'span', textContent: string });
  this.element.appendChild(span);
  this.element.style["margin-right"]='10px';
};

InfoPreview.prototype.__puts = function(string) {
  this.__print(string);
  this.__newLine();
};

//Public Methods
InfoPreview.prototype.update = function(info) {
  $(this.element).empty();
  for (var record in info) {
    this.__puts(record);
  }
};   

      

Please note that I am not creating private methods at all, just using the naming convention. Also, please note that I have no way to cache the search chain for example this.element

.

I would like to create a personal area using the disclosure module pattern, for example:

InfoPreview.prototype = (function() {
  var self = this, //<- `this` is actually the global object now.
      el = self.element;

  var newLine = function () {
    el.appendChild(createElement({tagName:'br'}));
  }; 

  var padLeft = function(level) {
    var padding = createElement({tagName: 'span'});
    el.appendChild(padding);
    $(padding).width(level * 10);
  };

  var print = function(string) {
    var span = createElement({ tagName: 'span', textContent: string });
    el.appendChild(span);
    el.style["margin-right"]='10px';
  };

  var puts = function(string) {
    print(string);
    newLine();
  };

  var update = function(info) {
    $(el).empty();
    for (var record in info) {
      puts(record);
    }
  };

  return {
    update: update
  }; 
})();

      

However, the above approach doesn't work as the value this

inside the IIFE is a global object and not an instance. I need a way to access the instance.

+3


source to share


4 answers


Is there a downside to using the constructor pattern?

function Foo(constructorArg) {
    
    /* private variables */
    var privVar = 'I am private',
        cArg = constructorArg;
    
    /* public variables */
    this.pubVar = 'I am public';
    
    /* private function */
    function privFunc() {
        return 'I am a private function';
        
    }
    
    /* public function */
    this.publicFunc = function() {
        return 'I am a public function and I call privVar->"' + privVar + '" and privFunc->"' + privFunc() + '"';
    }
}

var foo = new Foo('something');

console.log('foo.pubVar', foo.pubVar); //ok
console.log('foo.publicFunc()', foo.publicFunc()); // ok

console.log('foo.privVar', foo.privVar); // undefined
console.log('foo.privFunc', foo.privFunc()); //error
      

Run codeHide result


Why you should use it (as pointed out in the comments):

Simply put, because this is the only (sane) way to create a "true private sphere" to your question.

An alternative is to use a convention that tells developers which properties and methods are private, usually by prefixing them with an underscore _

that you've already implemented but didn't like.

Note that a constructor and a prototype are different things and let you do different things. Nothing stops you from mixing both.

Memory usage

In terms of memory usage, in modern js engines like Google V8 JavaScript Engine, the constructor pattern can be faster .

V8 has hidden types created internally for objects at runtime; objects with the same hidden class can then use the same optimized generated code.



For example:

function Point(x, y) {
  this.x = x;
  this.y = y;
}

var p1 = new Point(11, 22);
var p2 = new Point(33, 44);
// At this point, p1 and p2 have a shared hidden class
p2.z = 55;
// warning! p1 and p2 now have different hidden classes!

      

The prototype chain always requires two lookups, so it might even be a little small little LSB. Note. Unable to back up, jsperf.com is down!

The dirty constructor pattern (sic)

Performance was my reason. I didn't realize it. However, it still feels dirty to me.

I don't know why you feel like the constructor pattern is messy. Perhaps this is because it has some "quirks", limitations, and potential pitfalls that you should be aware of.

  • this

    can mean different things
  • It's easy to forget a new keyword causing weird and difficult debugging errors due to shared state
  • You cannot easily split your object into multiple files (without resorting to a build tool or a third party injector)

However, 1 and 2 are also true for the prototype declaration style, so ...

if you think this is not enough, you can look at the module template.

+1


source


Within each function, you will access the value you want this

.



var Example = function() {};

Example.prototype = (function() {
  var privateUpdate = function() {
    document.getElementById('answer').innerHTML = this.foo;
  }
  
  return {
    update: privateUpdate
  }
})();

var e = new Example();
e.foo = 'bar';
e.update();
      

<div id="answer"></div>
      

Run codeHide result


+1


source


Alternatively, Pointy points out you can try this pattern;

infoPreview.prototype = (function() {
  var self = null;

  var update = function(info) {
      ....
  };

  var firstUpdate = function(info) {
      self = this;
      functions.update = update;
      update(info);  
  }

  var functions = {
    update: firstUpdate
  }; 
  return functions;
})();

      

+1


source


Maybe something like this, without prototyping:

https://jsfiddle.net/ynwun1xb

var Fn = function(el) {
    this.el = el;

    var myMethod = function() {
        console.log('do something in method with element', this.el);
    }.bind(this);

    return {
        myPublicMethod: function() {
            return myMethod();
        }
    }
}

var instancedFn = new Fn('first instance element')
    .myPublicMethod()
;

var instancedFn2 = new Fn('second instance element')
    .myPublicMethod()
;

      

0


source







All Articles