Put function in another scope? - javascript

I have a class that (very) simplified looks like this:
var myClass = (function( window ) {
function myClass(configObject) {
var instanceVar = 'something that is different for every instance of myClass';
configObject.action && configObject.action();
}
return myClass;
})(window);
Now when I pass the option config object (which may contain a theoretically unlimited number of functions that in turn reference variables from the class that are different for every instance of that class) ...
var config = {
"action": function() {
concole.log(instanceVar); // is undefined when called from myClass, because it's in another scope
}
};
var instance = new myClass(config);
... of course those vars are not accessible in the function scope. How can I put the functions from the config into the class scope?
bindcame to my mind, but it only changes the thiscontext, not the scope. I could pass the variable as argument to the function, but then every function would have to check for that argument inside, and with regard to the number of functions coming from the external configuration object that may be out of my control, I would like to prevent making this a prequisite.
Is there a way to "clone" the functions into class scope or anything like that? Could closures be helpful in any way (and if so, please exemplify, as I haven't wrapped my mind around closures completely yet).
Thanks!

Here is my version
var myClass = (function( global ) {
function myClass(configObject) {
var instanceVar = 'something that is different for every instance of myClass';
configObject.action && configObject.action(instanceVar);
}
return myClass;
})(window);
var instance = myClass({
"action": function(instanceVar) {
console.log(instanceVar);
}
});
js fiddle: https://jsfiddle.net/e0pgs4vw/

instanceVar is defined inside a closure, so you can't access it from outside unless explicitly passed.
To prevent the need for passing it to each method, you can create a new configInstance from configObject, and add instanceVar as property to the new instance:
var myClass = (function(window) {
function myClass(configObject) {
var instanceVar = 'something that is different for every instance of myClass';
var configInstance = Object.create(configObject, { // create a new configInstance that inherits from the origin configObject, which means that the original won't be changed
instanceVar: {
writable: false,
configurable: false,
value: instanceVar
} // add instanceVar as a member to configInstance
});
configInstance.action && configInstance.action();
}
return myClass;
})(window);
var config = {
"action": function() {
console.log(this.instanceVar);
}
};
var instance = new myClass(config);

Related

Use object properties without specifying the object in Javascript

The following script is a simple class register script. I don't want to store classes on the global window object, but still I would like to make new MyClass2(); work. If I would be able to define obj variable as global object like the window then the following code should work fine.
So every properties on the obj object should be available as variable inside the scope of the defineClass function.
Is it possible somehow or should I stack with the commented code?
(function() {
var obj = {};
window.defineClass = function(className, cb) {
obj[className] = cb.call(obj /*, obj*/ );
}
window.start = function() {
new obj.MyClass();
}
})();
defineClass('MyClass', function( /*obj*/ ) {
function MyClass() {
this.obj = new MyClass2();
//this.obj = new obj.MyClass2();
}
window.MyClass = MyClass;
return MyClass;
});
defineClass('MyClass2', function( /*obj*/ ) {
function MyClass2() {
console.log('MyClass2');
}
return MyClass2;
});
start()
If you want the notation new MyClass2() to work, either define function MyClass2(){} in global scope or closure, ex:
defineClass('MyClass', function() {
function MyClass() {
this.obj = new MyClass2();
}
window.MyClass = MyClass;
return MyClass;
function MyClass2(){}
});
window is a special global object as window.window is window itself.
Using module loader is handy for this kind of problem, ex. requirejs, ES6 import, etc.
Using iframe is another possible solution for your need.

Creating JS library with dynamic function calling

I'm creating a JS 'library' file, But I want to encapsulate it in it's entirety within an object, to avoid contaminating namespace of the pages that include the file
The twist to this is within a function inside the library I need to call others functions within library by name, eg using window[]
The code below is just a sample there would actually be several hundred functions that could be called by name. It's this that's caused by trouble as I can't get window[] to reference the function, what's the right way to go about this?
I have tried this, in host page:
<script src= "mylib.js"></script>
var oMyLib = new cMyLib(); //there will only ever be one 'instance' of this
In mylib.js everything is contained in one function:
function cMyLib() {
this.doStuff = function () {
someFunc(this); //call another function in the lib
}
// I tried it with prototypes also
// cMyLib.prototype.doStuff = function () {
// someFunc();
// }
function someFunc(that) {
var s='anotherFunc1'
var f = window[s]; //undefined!
f();
s='anotherFunc2'
f=window[s];
f();
}
function anotherFunc1() {}
function anotherFunc2() {}
}
The functions that you want to reference by name (or actually by number, according to your comments) should be part of that object, and not accessed via window, e.g.:
function cMyLib() {
// allow call without new
if (! (this instanceof cMyLib)) {
return new cMyLib();
}
// enforce singleton
if (this.constructor.singleton) {
return this.constructor.singleton;
} else {
Object.defineProperty(this.constructor, 'singleton', {
value: this
});
}
// instruction array (no need to expose via `this`)
var insn = [];
insn[0x4c] = function lda_immediate() { ... }
// instruction execution
this.step = function() {
var opcode = memory[pc++];
if (opcode in insn) {
// `.call` ensures `this` is set inside the instruction fn.
insn[opcode].call(this);
} else {
hcf();
}
}
}
Note the extra stuff at the top - convenience code to ensure that only one cMyLib can exist.
As long as a function is in parent scope you can just reference it directly, i.e.
function someFunc(that) {
anotherFunc1();
};
will simply work.
Another thing is that classical way to do this is to wrap everything in a self-calling anonymous function, i.e.
(function() {
function anotherFunc1() {};
function anotherFunc2() {};
this.cMyLib = function() { ... };
})();
But your approach is fine as well.
If you wish to call your functions by dynamic name, then you can store them in a top level object:
(function() {
var my_functions = {
anotherFunc1: function() {},
anotherFunc2: function() {}
};
this.cMyLib = function() {
var name = 'anotherFunc1';
my_functions[name]();
};
})();
It's like creating an isolated "global" scope.
Side note: Do not use eval. It is very unsafe and slow. It will backfire at you at some point.

Trouble setting variables defined in parent class when creating a new subclass object

I have been trying to learn the ins and outs of Javascript's inheritance structure and I came across this problem. I'm trying to create several subclass objects and assign values to them immediately by passing a variable upon their creation.
For instance, below the parent class GamePiece receives a random property as a variable upon its creation and sets it as myProperty which works fine of course when creating a new GamePiece object. However, if I wanted to set that variable on the creation of a Pawn object, it does not pass into its parent and remains unset. The obvious fix is to just define the variable again on the subclass but that, correct me if I'm wrong, seems to defeat the purpose of defining a parent class. You can also successfully set the variable by passing the parameter as Pawn.prototype = new GameObject("foo"); but that doesn't help when creating multiple Pawn() objects. Is there a common way of doing this that I am missing?
var GamePiece = function (randomProperty) {
this.myProperty = randomProperty || "never set";
this.print = function () {
console.log(this.myProperty);
}
}
var Pawn = function (randomProperty) {
this.print = function () {
console.log(this.myProperty);
}
}
//Setting a value on creation
piece = new GamePiece("foo");
piece.print(); // Produces "foo" naturally
//Setting the prototype
Pawn.prototype = new GamePiece();
//Try to pass value through the creation of subclass
pawn = new Pawn("foo");
pawn.print(); // Produces "never set"
You must call you parent class in context of current this, using call or apply method:
var GamePiece = function (randomProperty) {
this.myProperty = randomProperty || "never set";
this.print = function () {
console.log(this.myProperty);
}
}
var Pawn = function (randomProperty) {
Game.call(this, randomProperty);
// or Game.apply(this, [randomProperty]);
}
But much it's better to keep methods in prototype. So next code will be better:
var GamePiece = function (randomProperty) {
this.myProperty = randomProperty || "never set";
//... some another properties initialization
};
GamePiece.prototype.print = function () {
console.log(this.myProperty);
};
var Pawn = function (randomProperty) {
Game.call(this, randomProperty);
// or Game.apply(this, [randomProperty]);
//... some Pawn properties initialization
};
Pawn.prototype = Object.create(Game.prototype, { constructor: { value: Pawn }});
Pawn.prototype.someMethod = function() {
// Some Pawn method logic
};
But ES6 is coming (will become recommendation in June 2015), so you can start preparation for using them. See here, here and here

Making JavaScript private methods accessible to its public methods

I understand there are couple of patterns to make JavaScript 'class-like'.
I would like to take the 'extending by prototype' way... simply because it looks more neat. I am not worried about performance much here...
In the below example I have a class (basically function) MetricsChart. I have couple of public methods and one private method (basically a reusable method).
Here from the public method (drawOrAdd) I can't access the private method (_convertArrayToTable), how can I do that?
function MetricsChart(containerId, chartType) {
this._container = document.getElementById(containerId);
this._chartType = chartType;
this._isChartDrawn = false;
this._chartData = null;
var _convertArrayToTable = function (data) {
return google.visualization.arrayToDataTable(data);
}
}
MetricsChart.prototype.drawOrAdd = function(data)
{
if (!this.isChartDrawn()) {
var chart = new google.visualization.LineChart(this._container);
if (chart) {
var table = _convertArrayToTable(data);
console.log(table);
this._isChartDrawn = true;
}
}
}
MetricsChart.prototype.isChartDrawn = function () {
return this._isChartDrawn;
}
MetricsChart.prototype.getChartData = function () {
}
One way I accidentally found was to enclose the public methods inside the MetricsChart class itself...
It works for me :): I can access the public methods outside and the public method can access the private method (serves the purpose).
Below code... Is this right? Am I doing anything wrong?
function MetricsChart(containerId, chartType) {
this._container = document.getElementById(containerId);
this._chartType = chartType;
this._isChartDrawn = false;
this._chartData = null;
var _convertArrayToTable = function (data) {
return google.visualization.arrayToDataTable(data);
}
MetricsChart.prototype.drawOrAdd = function (data) {
if (!this.isChartDrawn()) {
var chart = new google.visualization.LineChart(this._container);
if (chart) {
var table = _convertArrayToTable(data);
console.log(table);
this._isChartDrawn = true;
}
}
}
MetricsChart.prototype.isChartDrawn = function () {
return this._isChartDrawn;
}
MetricsChart.prototype.getChartData = function () {
}
}
So, here a couple of things, in order to understand what you have done precisely.
First of all:
function foo() {
var i = 0;
function bar() {
return true;
}
}
What's happening here: every time the function foo is called, it creates in its scope a new variable i, and a new function bar. The function bar and the variable i are in its scope, it means they're local: there is no way, with this code, to access to either i or bar outside the function foo. Also because, once the function foo is terminated, both i and bar are disposed.
So, this is why you cannot access from your "public" method to the "private" one, and I hope it's more clear now. The only way for a function to access to a function or variable is that there is a reference shared in the same scope. So, this is what you have done in your last example: you define your "public" methods in the same scope where you define your "private" method. In this way they can access each other. However, the way you have done, has a big downside. As I said previously, the function bar is created every time the function foo is called. In a "class" example, it means:
function MyClass() {
function myprivate() {}
MyClass.prototype.mypublic = function () { return myprivate() }
}
It means that every time you're creating an instance of MyClass, you're creating two new functions, and you're rewrite all the time the prototype of your "class". This is far from be a good approach. In fact, if you have something like:
var a = new MyClass();
var _mypublic = a.mypublic;
var b = new MyClass();
console.log(_mypublic === b.mypublic) // false
console.log(_mypublic === a.mypublic) // false too!
So, you guess right but you executed wrong. What you need here is a the "module pattern": nowadays you can use CommonJS module in nodejs or AMD in browser and so on, but the basic idea is defined a "scope" and exports from this scope only what you want. In your case, you could have:
// this is your "module"
;(function(exports) {
// your local (private) function
var _convertArrayToTable = function (data) {
return google.visualization.arrayToDataTable(data);
}
function MetricsChart(containerId, chartType) {
this._container = document.getElementById(containerId);
this._chartType = chartType;
this._isChartDrawn = false;
this._chartData = null;
}
MetricsChart.prototype.drawOrAdd = function(data) {
if (!this.isChartDrawn()) {
var chart = new google.visualization.LineChart(this._container);
if (chart) {
var table = _convertArrayToTable(data);
console.log(table);
this._isChartDrawn = true;
}
}
}
// you decided to exports to the main scope what you want
exports.MetricsChart = MetricsChart;
}(this)); // here `this` is the global object
And that's it. You have created a closure, using the "module pattern", and from the "public" method you can access to the "private" function, because they're defined in the same scope. But because you do not do that in the "class" constructor, you don't redefine them every time you instantiate a new object. Therefore, the previous example written in this way, will give the right result:
var a = new MyClass();
var _mypublic = a.mypublic;
var b = new MyClass();
console.log(_mypublic === b.mypublic) // true
console.log(_mypublic === a.mypublic) // true
What you've done isn't necessarily "wrong"...it just looks weird. Also, you won't be able to access "MetricsChart.prototype.*" until after you've created an instance of "MetricsChart". Depending on how you are using this object, it may not matter.
That being said, another way is to keep your original structure, but move the following outside of the constructor:
var _convertArrayToTable = function (data) {
return google.visualization.arrayToDataTable(data);
}
It would still be private to your module which should be good enough (you are using modules right?).
What you have done works perfectly.
You can't inherit private methods in any OOP language in terms of overriding them or accessing them directly. They are private. So it makes no sense to have them prototyped for inheritance purposes. You have wrapped them in function scope so they are as "private" as they need to be.
To access the private methods use privilege methods. Check this document: http://javascript.crockford.com/private.html.
About your code check this answer:
Setting javascript prototype function within object class declaration
p.s.
function Test()
{
var p = function(pv)
{
//
}
this.e = function (ap) { p(ap) }
}
var n = new Test();
n.e("e"); // It is legal call
n.p(); // will throw
But if you declare a private function in c-tor it will be executed on first creation of object of this type. When declare a methods in prototype this methods are add before any code execution. In general the browser first check the js file to collect all methods for prototype and than execute any code. So when you declare a prototype methods into c-tor this methods will be available only after first creation of the object of those type. ( Sorry for my English ).
Check this situation:
function Test()
{
alert(this.ST_A);//alert undefined
alert(this.ST_B);//alert 2
Test.prototype.ST_A = 1;
alert( this.ST_A)//alert 1
}
Test.prototype.ST_B = 2;
In first pass the browser will populate Test with ST_B and ST_B will be available anywhere any time. After than in second pass the browser will start to execute the code in this time ST_A will not be visible until the browser execute the Test.prototype.ST_A = 1;

Encapsulation in javascript

I need to create simple reusable javascript object publishing several methods and parameterized constructor. After reading through several "OOP in JavaScript" guides I'm sitting here with an empty head. How on the Earth can I do this?
Here my last non-working code:
SomeClass = function(id) {
this._id = id;
}
(function() {
function intFun() {
return this._id;
}
SomeClass.prototype.extFun = function() {
return incFun();
}
})();
This is my usual approach:
MyClass = function(x, y, z) {
// This is the constructor. When you use it with "new MyClass(),"
// then "this" refers to the new object being constructed. So you can
// assign member variables to it.
this.x = x;
...
};
MyClass.prototype = {
doSomething: function() {
// Here we can use the member variable that
// we created in the constructor.
return this.x;
},
somethingElse: function(a) {
}
};
var myObj = new MyClass(1,2,3);
alert(myObj.doSomething()); // this will return the object's "x" member
alert(myObj.x); // this will do the same, by accessing the member directly
Normally the "this" keyword, when used in one of the object's methods, will refer to the object itself. When you use it in the constructor, it will refer to the new object that's being created. So in the above example, both alert statements will display "1".
An exception to this rule is when you pass one of your member functions somewhere else, and then call it. For example,
myDiv.onclick = myObj.doSomething;
In this case, JavaScript ignores the fact that "doSomething" belongs to "myObj". As a result, the "this" inside doSomething will point to another object, so the method won't work as expected. To get around this, you need to specify the object to which "this" should refer. You can do so with JavaScript's "call" function:
myDiv.onclick = function() {
myObj.doSomething.call(myObj);
}
It's weird, but you'll get used to it eventually. The bottom line is that, when passing around methods, you also need to pass around the object that they should be called on.
I usually don't worry too much about hiding the internals, although I do prefix them with underscores to mark them as not intended to be used outside the "class". Normally what I will do is:
var MyClass = function() {};
MyClass.prototype = {
_someVar : null,
_otherVar : null,
initialize: function( optionHash ) {
_someVar = optionsHash["varValue"];
_otherVar = optionsHash["otherValue"];
},
method: function( arg ) {
return _someVar + arg;
},
};
And use it as so...
var myClass = new MyClass( { varValue: -1, otherValue: 10 } );
var foo = myClass.method(6);
All vars are private:
SomeClass = function (id) {
var THIS = this; // unambiguous reference
THIS._id = id;
var intFun = function () { // private
return THIS._id;
}
this.extFun = function () { // public
return intFun();
}
}
Use THIS within private methods since this won't equal what you might expect.
From http://learn.jquery.com/code-organization/concepts/#the-module-pattern:
// The module pattern
var feature = (function() {
// private variables and functions
var privateThing = "secret";
var publicThing = "not secret";
var changePrivateThing = function() {
privateThing = "super secret";
};
var sayPrivateThing = function() {
console.log( privateThing );
changePrivateThing();
};
// public API
return {
publicThing: publicThing,
sayPrivateThing: sayPrivateThing
};
})();
feature.publicThing; // "not secret"
// logs "secret" and changes the value of privateThing
feature.sayPrivateThing();
So using returning an object that aliases its "methods" could be another way to do it.
I've read from http://www.amazon.com/Programming-Oracle-Press-Poornachandra-Sarang-ebook/dp/B0079GI6CW that it is always good practice to use getters and setters rather that accessing the variable directly from outside the object, so that would eliminate the need of returning variables by reference.
BTW you could just use this.variable to reference/declare a public variable and var variable to declare a private variable.
I know this is a late answer, but I hope it helps anyone who reads it in the future.

Categories

Resources