Take this code:
var john = new function () {
var init = function () {
alert("John")
};
return {
init: init
};
};
var jane = new function () {
var init = function () {
alert("Jane")
};
return {
init: init
};
};
function callInit(person) {
var fn = new Function(person); // does not work!
fn.init();
}
$(document).ready(function () {
callInit("john");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
I would like to pass a string to a function - in my example I pass the string "john". Then I need to convert the passed string to the existing function and call init - in my example call john.init()
Is it possible?
Thanks
You can do it by changing your callInit function to:
function callInit(person) {
var fn = window[person];
fn.init();
}
var john = new function () {
var init = function () {
alert("John")
};
return {
init: init
};
};
var jane = new function () {
var init = function () {
alert("Jane")
};
return {
init: init
};
};
function callInit(person) {
var fn = window[person];
fn.init();
}
$(document).ready(function () {
callInit("john");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
One way to achieve this would be by placing the data structures you want to access by key in to an object. You can then use the string passed in to your function as an argument to access that object by key, the advantage being that you avoid using global variables, which pollute the window. It would look like this:
let people = {
john: function() {
var init = function() {
console.log("John")
};
return { init: init };
},
jane: function() {
var init = function() {
console.log("Jane")
};
return { init: init };
}
}
function callInit(person) {
var fn = people[person]();
fn.init();
}
$(document).ready(function() {
callInit("john");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Note that if you are going to be using a repeated data structure in this manner I would strongly suggest creating a reusable class for each property within the object. That would look something like this:
class Person {
constructor(name) {
this.name = name;
}
greeting() {
console.log(`Hello, my name is ${this.name}`);
}
}
let people = {
john: new Person('John'),
jane: new Person('Jane')
}
function callInit(person) {
var fn = people[person];
fn.greeting();
}
$(document).ready(function() {
callInit("john");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Related
I'm learning javascript.
I have this code...
var test = new function () {
var vars = {
$hub: null
};
var init = function () {
vars.$hub = $.connection.blabla;
};
return {
vars: vars,
init: init,
$hub: vars.$hub
};
};
$(document).ready(function () {
test.init();
test.vars.$hub..... // Works perfecetly
test.$hub..... // Doesn't work - test.$hub is null
});
I don't get why test.$hub is null when test.vars.$hub isn't null?
Thanks
You should use Object directly with getter on $hub:
var test = new function() {
return {
vars: {
$hub: null
},
init: function () {
this.vars.$hub = 'something';
},
get $hub() {
return this.vars.$hub;
}
}
};
$(document).ready(function () {
console.log(test.$hub) // before init
test.init();
console.log(test.vars.$hub) // Works perfecetly
console.log(test.$hub) // Works too!!!
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
vars (and $hub: null) is defined within the scope of Function test whereas test.$hub is not defined.
If you wrote:
var test = new function () {
var $hub = null;
var vars = {
$hub: null
};
var init = function () {
vars.$hub = $.connection.blabla;
};
return {
vars: vars,
init: init,
$hub: vars.$hub
};
};
You can now access the value of test.$hub as null.
I would like to use this instead of the name of the object inside the IIFE.
var ops = {
start: (function (){
socket.on('set', function(data) {
ops.getData();
});
}()),
getData: function (){
...
}
};
How it can be done?
You cannot. Not to mention that your function does not return anything to assign to ops.start.
var ops = {
start: function (){
var self = this;
socket.on('set', function(data) {
self.getData();
});
}
};
ops.start();
You could use getter and closure on your function context if needed. Getter automatically invokes a function:
var ops = {
get start() {
return socket.on('set', function(data) {
ops.getData();
});
},
getData: function (){
}
};
Now you can just refer to ops.start
You can create a new object and use it's prototype to access "this":
var o = Object.create(Object.prototype, {
data: {
value: 12
},
getobject: {
get: function() {
return this.data;
}
}
});
o.getobject;
In the snippet below, an object literal holds properties, one of which is a method that needs access to the the object literal.
However, b.c. it is only used as an event handler callback, this always points to the element that triggered the event.
I need to access the containing object.
Otherwise, I'm forced to put a function in a function which seems odd.
/***************************************************************************************************
**MSimMenu - simple drop down menu
*/
NS.parsel({
Name: 'MSimMenu',
E: {
hold_name: '#hold_name',
wrap_bottom: '#wrap_bottom'
},
A: {
time_out_id: null,
TIME_DELAY: 1000
},
// in mouseout this points to the element that triggered the event
// need access to containing object
mouse_out: function () {
this.A.time_out_id = NS.setTimeout(this.hideBottom, this.A.TIME_DELAY);
},
init: function () {
var self = this;
// tempoaray fix - function in function seems odd
function mouse_out() {
self.A.time_out_id = NS.setTimeout(self.hideBottom, self.A.TIME_DELAY);
}
self.E.hold_name.addEventListener("mouseover", function () {
NS.clearTimeout(self.A.time_out_id);
self.showBottom();
}, false);
self.E.wrap_bottom.addEventListener("mouseover", function () {
NS.clearTimeout(self.A.time_out_id);
}, false);
self.E.wrap_bottom.addEventListener("mouseout", mouse_out, false);
self.E.hold_name.addEventListener("mouseout", mouse_out, false);
},
showBottom: function () {
this.E.wrap_bottom.style.visibility = 'visible';
},
hideBottom: function () {
this.E.wrap_bottom.style.visibility = 'hidden';
}
});
Final Code Using Bind
NS.parsel({
Name: 'MSimMenu',
E: {
hold_name: '#hold_name',
wrap_bottom: '#wrap_bottom'
},
A: {
time_out_id: null,
TIME_DELAY: 1000
},
init: function () {
var self = this;
self.E.hold_name.addEventListener("mouseover", function () {
NS.clearTimeout(self.A.time_out_id);
self.showBottom();
}, false);
self.E.wrap_bottom.addEventListener("mouseover", function () {
NS.clearTimeout(self.A.time_out_id);
}, false);
self.E.wrap_bottom.addEventListener("mouseout", self.mouse_out.bind(self), false);
self.E.hold_name.addEventListener("mouseout", self.mouse_out.bind(self), false);
},
mouse_out: function () {
this.A.time_out_id = NS.setTimeout(this.hideBottom, this.A.TIME_DELAY);
},
showBottom: function () {
this.E.wrap_bottom.style.visibility = 'visible';
},
hideBottom: function () {
this.E.wrap_bottom.style.visibility = 'hidden';
}
});
I have seen alot of people create a variable to assign the object to and then use the variable.
var that = {
myfunc:function(){
console.log(that)
}
};
NS.parsel(that);
I actually like moving most of the logic into the init method. Provides nice encapsulation with an easy way to declare public and private methods/variables. For example:
NS.parsel({
init: function() {
var self = this;
//public instance variable
self.Name = 'MSimMenu';
//private instance variables
var A = {
time_out_id: null,
TIME_DELAY: 1000
};
var E = {
hold_name: '#hold_name',
wrap_bottom: '#wrap_bottom'
};
//public instance method
self.showBottom = function () {
E.wrap_bottom.style.visibility = 'visible';
};
//private instance method
E.wrap_bottom.addEventListener("mouseout", mouse_out, false);
function mouse_out() {
A.time_out_id = NS.setTimeout(self.hideBottom, A.TIME_DELAY);
}
}
});
There's a lot of ways you can get what you want.
One trick you can do is to not use the mouse_out function directly, but provide a helper function like get_mouse_out() that returns a bound version of the function.
var myobject = {
data:"Some data",
_mouse_out: function() { console.log(this.data); }
get_mouse_out: function() {
var self = this;
return function(){ return Function.apply(self._mouse_out,self,arguments); }
}
}
//Example call back using function.
function do_callback(fn) { fn(); }
//This doesn't work.
do_callback( myobject._mouse_out);
//But this does
do_callback( myobject.get_mouse_out() );
EDIT: Improved version inlining _mouse_out and using bind.
var myobject = {
data:"Some data",
get_mouse_out: function() {
function _mouse_out() { console.log(this.data); }
return _mouse_out.bind(this);
}
}
//Example call back using function.
function do_callback(fn) { fn(); }
//But this does
do_callback( myobject.get_mouse_out() );
If you're willing to have init be called as setup before mouse_out is used then you can do this.
var myobject = {
data:"Some data",
init: function() {
function _mouse_out() { console.log(this.data); }
this.mouse_out = _mouse_out.bind(this);
}
}
myobject.init();
fn( myobject.mouse_out );
Finally there's a nice variant on Shanimals that works a similar way, but provides encapsulation.
NS.parcel( (function(){
var myobj = {};
myobj.data = "Some data";
myobj.mouse_out = function(){ console.log(myobj.data); }
return myobj;
})()
);
I'm using the Revealing Prototype Pattern and have 2 different prototypes that I'm putting into the same JavaScript file. These links are to articles I found which relate to this.
http://bit.ly/U83hdg, http://bit.ly/VmJ71h.
I was under the impression that these would operate like atomic classes, where functions associated with one would be unaware of functions in the other.
For instance, both of these prototypes have an "init" and a "set" function. The behavior I'm seeing in the browser is that the last version of "init" gets executed, even when the code references the first prototype name.
This is generic stripped-down code from my two prototypes.
var operationA = function (control, settings) {
this.control = control;
this.settings = settings;
};
operationA.prototype = function () {
init = function () {
// do something
return this;
}
set = function () {
// do something
return this;
};
return {
init: init,
set: set
};
}
var operationB = function (control, settings) {
this.control = control;
this.settings = settings;
};
operationB.prototype = function () {
init = function () {
// do something
return this;
}
set = function () {
// do something
return this;
};
return {
init: init,
set: set
};
}
This is how I'm instantiating the first object.
var objectASettings = {
property1: 48,
property2: 37
};
var objectA = new operationA('#mySelector', objectASettings);
objectA.init().set();
When the above runs, the init and set functions from the prototype for operationB are being executed, instead of executing the init and set functions from the prototype for operationA.
I assumed these prototypes basically namespaced their contained functions. Am I required to create unique public function names for operationA and operationB (like initA , setA, initB, setB)?
Is there a way to self-contain and/or namespace these public functions, so I can expose the same operation names of init and set on 2 different prototypes in the same file?
Thanks for your help.
A couple of things to get it working:
Add var before the first member in the prototype function.
Separate each member with a comma (you can certainly put var in front of each member but I like to keep it clean...personal preference though).
The function assigned to the prototype must be self-invoked for the pattern to work properly.
Here's an example that should work for you:
<html>
<head>
<script>
var operationA = function (control, settings) {
this.control = control;
this.settings = settings;
};
operationA.prototype = function () {
var init = function () {
// do something
return this;
},
set = function () {
alert('set A');
return this;
};
return {
init: init,
set: set
};
}();
var operationB = function (control, settings) {
this.control = control;
this.settings = settings;
};
operationB.prototype = function () {
var init = function () {
// do something
return this;
},
set = function () {
alert('set B');
return this;
};
return {
init: init,
set: set
};
}();
window.onload = function() {
var objectASettings = {
property1: 48,
property2: 37
};
var objectBSettings = {
property1: 50,
property2: 50
};
var objectA = new operationA('#mySelector', objectASettings);
objectA.init().set();
var objectB = new operationB('#foo', objectBSettings)
objectB.init().set();
}
</script>
</head>
You're omitting the var keyword when defining init and set so they're both assigned to the global object.
Just define the prototypes as Objects.
var operationA = function (control, settings) {
this.control = control;
this.settings = settings;
};
operationA.prototype = {
init: function () {
// do something
return this;
},
set: function () {
// do something
return this;
}
}
var operationB = function (control, settings) {
this.control = control;
this.settings = settings;
};
operationB.prototype = {
init: function () {
// do something
return this;
},
set: function () {
// do something
return this;
}
};
I am trying to create an object and then call initialize function on that object. However I get initialize not defined. This is my code:
var SomeJSObj = function () {
function Initialize() {
$(document).ready(function () {
alert('hello');
});
};
};
when I create a new object of SomeJSObj and call Initialize I get function not defined. Could anybody please help me track the error?
Initialize is currently not accessible outside of the SomeJSObj constructor. You need to expose it, either on each instance or on the prototype:
var SomeJSObj = function () {
this.Initialize = function () {
$(document).ready(function () {
alert('hello');
});
};
};
On the prototype is more efficient, since only one copy of the function has to exist in memory, instead of one for each instance:
var SomeJSObj = function () {};
SomeJSObj.prototype.Initialize = function () {
//Do stuff
};
Should be like this since you want to make the Initialize function public:
var SomeJSObj = function () {
this.Initialize = function() {
$(document).ready(function () {
alert('hello');
});
}
};
var t = new SomeJSObj();
t.Initialize();
Firstly, $(document).ready should be outside the function definitions:
$(document.ready(function() {
...
});
The problem is that your methods should be defined on the object using the prefix this.:
this.Initialize = function() {
...
};
So all in all it comes to:
$(document).ready(function() {
var SomeJSObj = function () {
this.Initialize = function() {
alert('hello');
};
};
});
And to call your methods you must create an instance:
var instance = new SomeJSObj(); // make sure this in in $(document).ready
and call the method:
instance.initialize(); // works
You can do it this way, Simple and easy way.
var SomeJSObj = function () {
this.Initialize=function() {
$(document).ready(function () {
alert('hello');
});
};
};
var x = new SomeJSObj();
x.Initialize()
This is what you want. See the fiddle.
var SomeJSObj = function () {
this.initialize();
};
SomeJSObj.prototype.initialize = function() {
$(document).ready(function () {
alert('hello');
});
};
new SomeJSObj();