Dynamically firing a named-spaced method via JavaScript - javascript

I have multiple external JavaScripts that are namespaced based on the section of the site. I am trying to dynamically fire methods, but am unable to get the methods to fire. Can anyone tell me what the problem is?
If I add this, the method fires:
Namespace.Something.init()
But when I try to do it like this, nothing happens (note: namespace equals Namespace.Something and functionname equals init):
namespace[functionname]();

Unless you want to use eval which I am sure you don't the following works.
This assumes that all your methods are the same level deep i.e namespace.somename.somemethod
var Namespace = {
Something: {
init: function() {
console.log('init called');
}
}
};
Namespace.Something.init();
var namespace = "Namespace";
var section = "Something";
var method = "init";
this[namespace][section][method]();
as Namespace is part of the global scope you can access it from this[namespace]

I asked the same question a few weeks ago, though I think I phrased it slightly differently. See this.
Basically, you need to parse the string functionname one piece at a time.
By the way, using the walk_path code from that answer, here's a general purpose function I wrote to run a function from a string including arguments.
// run an arbitrary function from a string. Will attempt to parse the args from parenthesis, if none found, will
// use additional arguments passed to this function.
utils.runFunction = function (funcdef) {
var argPos = funcdef.indexOf('(');
var endArgPos = -1;
var args = undefined;
var func = funcdef;
if (argPos > 0) {
endArgPos = funcdef.indexOf(')', argPos);
if (endArgPos > 0) {
args = funcdef.substring(argPos + 1, endArgPos).split(',');
func = funcdef.substring(0, argPos - 1);
}
} else {
args = Array.prototype.slice.call(arguments, 1);
}
var func = walk_path(window, func);
return !args ? func() : func.apply(null, args);
};

var methodName = 'Namespace.Something.init';
var methodParts = methodName.split('.');
var method = this;
for (var i=0; i < methodParts.length; i++) {
method = method[methodParts[i]];
};
method(the arguments you want);

Related

How do I wrap a javascript function with dynamic arguments?

I'd like to wrap some dynamically created javascript functions, similar to Daniel's accepted answer here:
How do I store javascript functions in a queue for them to be executed eventually
// Function wrapping code.
// fn - reference to function.
// context - what you want "this" to be.
// params - array of parameters to pass to function.
var wrapFunction = function(fn, context, params) {
return function() {
fn.apply(context, params);
};
}
The difference is I'd like the argument values to be dynamic at time of execution - is it possible to pass a reference to a variable in the arguments, which could be updated after it is wrapped?
Here's what I'd like to do:
// I have a function to be wrapped
var sayStuff = function(a,b) {
console.log(a);
console.log(b);
}
// Variables I'd like to pass
var randomNumberA = 0;
var randomNumberB = 0;
// Wrap the function
var fun = wrapFunction(sayStuff, this, [*reference randomNumberA*,*reference randomNumberB*]);
// variables get changed
randomNumberA = Math.random()*100;
randomNumberB = Math.random()*100;
// Execute the function using current values of randomNumberA & randomNumberB
fun();
If possible I'd like to do this without changing sayStuff, I have a lot of existing functions like this I'm hoping to wrap, which also get used outside of the wrapping, so ideally I'd like to not replace the arguments with an object.
Hope that makes sense, Thanks!
If the function and the variable will be created in the same scope you can just use that:
var randomNumber = 0;
var fun = function(){ alert(randomNumber); }
randomNumber = 10;
// Now this will alert 10, because when fun is executed
// JS looks in his scope to find what randomNumber is.
fun();
This happens because functions in javascript works as Closures, they carry their environment with them. See: https://en.wikipedia.org/wiki/Closure_(computer_programming)
So if randomNumber will be changed out of the scope where you bind that function, you need to use an object, this is because in javascript we don't have "pointers" or references to pass by. One way is using a object.
function giveMeAFunction(){
var params = { randomNumber: 0 }
var fun = function(){ alert(scope.randomNumber); }
return {fun: fun, scope: scope};
}
var paramsAndFun = giveMeAFunction()
// Now you can change the variables in the scope and call the function
paramsAndFun.params.randomNumber = 10;
paramsAndFun.fun(); // Will alert 10
// Now if you replace the entire params object it will not work
// This is because you will replacing it with a new object while
// The one that is referenced in the scope where fun was created is
// the old one.
paramsAndFun.params = { randomNumber: 15 };
paramsAndFun.fun(); // will still alert 10
Now let's get to binding part of the problem.
There is already Function.prototype.bind function to help you with that.
For example:
var sayStuff = function(opts) {
alert(otions.randomNumber);
}
var options = { randomNumber: 0 };
var fun = sayStuff.bind(this, options);
options.randomNumber = 10;
fun(); // Will print 10
There is a lot going on here. Sorry if I made everything confuse.
If the dynamic arguments are defined in the context argument, a solution can be based passing the name of the variables and then, at execution time, calculate its current value:
var wrapFunction = function(fn, context) {
var xArg = arguments;
return function() {
var argsArray = [];
for (var i = 2; i < xArg.length; i++) {
argsArray.push(context[xArg[i]]);
}
fn.apply(context, argsArray);
};
}
var sayStuff = function() {
for (var i = 0; i < arguments.length; i++) {
console.log('sayStuff func: ' + arguments[i]);
}
}
var randomNumber1 = 0;
var randomNumber2 = 0;
var fun = wrapFunction(sayStuff, this, 'randomNumber1', 'randomNumber2');
randomNumber1 = Math.random()*100;
randomNumber2 = Math.random()*100;
console.log('randomNumber1: ' + randomNumber1);
console.log('randomNumber2: ' + randomNumber2);
fun();

Override JavaScript (window) Function

I'd like to override a function which is being made by a javascript plugin. I know how to override regular window functions, but this is different. I'm not sure how to name it, but the structure of that function is like:
window.something.function
I have no idea how to override that. I have tried the following:
var originalFunction = window.something.function;
window.something.function = function(parameter) {
alert('called');
return originalFunction(parameter);
}
But it's not working.
Does someone know a solution?
Edit:
As I have been told my question is unclear, I have edited it again using the actual names of the plug-in.
The plugin is meant to be used as:
var myColor = new jscolor(target, options)
When this is being used, there is a function "inside" the object "jscolor" which is being called when setting the value of target element. I want to override that function to add an extra functionality without changing the original js file.
Code:
if (!window.jscolor) { window.jscolor = (function () {
var jsc = {
.....
jscolor : function (targetElement, options) {
....
//Function I want to change:
this.exportColor = function (flags) {
if (!(flags & jsc.leaveValue) && this.valueElement) {
var value = this.toString();
if (this.uppercase) { value = value.toUpperCase(); }
if (this.hash) { value = '#' + value; }
if (jsc.isElementType(this.valueElement, 'input')) {
this.valueElement.value = value;
} else {
this.valueElement.innerHTML = value;
}
}
}
}
};
My attempts so far:
var origJsColor = jscolor.exportColor;
jscolor.exportColor = function(flags) {
console.log('called');
return origJsColor(flags);
}
and the window attempt above.
The jscolor code you've shown creates an object with its own copy of exportColor (one is created for each object). So to replace it, you have to replace it on each instance as the instance is created.
You can do that as a one-off in much the way you showed, just working with the instance rather than the plugin function, and using Function#call to call it with the right this:
// Get the instance
var c = new jscolor(target, options)
// Update it
var origExportColor = c.exportColor;
c.exportColor = function(flags) {
console.log('called');
return origExportColor.call(c, flags); // Note the changes on this line
};
Or instead of
return origExportColor.call(c, flags);
you might use
return origExportColor.apply(c, arguments);
...if there's any chance of the function being called with anything other than exactly one argument. (arguments is a magic pseudo-array containing the arguments used to call the function.)
If you want to do that for all instance you might create, you can put a facade in front of jscolor to do that to each instance:
var realJscolor = jscolor;
jscolor = function() {
// Call the real function, passing along all the arguments we
// get automatically (`arguments` is a magic pseudo-array)
var retVal = realJscolor.apply(this, arguments);
// If it returned a non-`null` object, we want to use that instead
// of `this`; if not, we keep using `this`
if (!retVal || typeof retVal !== "object") {
retVal = this;
}
// Slip in our version of exportColor
var origExportColor = retVal.exportColor;
retVal.exportColor = function(flags) {
console.log('called');
// (Maybe use `apply` here instead)
return origExportColor.call(retVal, flags);
};
// Return the result, in case the real function overrode `this`
return retVal;
};
jscolor.prototype = realJscolor.prototype;
Then just use jscolor normally:
var c = new jscolor(target, options);
The reason for the retVal thing is that although normally a new expression's result is a refernece to the new object created by new, a constructor function can return a non-null object reference and, if it does, the new expression's result is that object reference instead. That's why we check the return value of realJscolor.
Of course, that means that all uses of jscolor on the page that use the global will now use your updated function instead. If you don't want that, just use your own name and don't override jscolor:
var myColor = function() {
var retVal = jscolor.apply(this, arguments);
// ...and so on...
return retVal;
};
myColor.prototype = jscolor.prototype;
Usage:
var c = new myColor(target, options);
Function
function a() {alert(this)} // will print `window` obejct
is defined in the window scope. That is, it is a method of the window. Your more difficult situation comes from the fact that this is different from window if you define function as a method in another object.
var a = {method: function() {alert(this)}}
you call a.method() but see that the same window again. You need to bind your function to the parent object to make it compete method.

`this` is undefined when calling method from another context

This is my first time creating OOP for JS. I followed some tutorials but I can't wrap my head around this issue. I know the problem, but i dont know the solution
function NewApp(name){
this.name = name;
this.currentPage = 1;
this.customObjectWithMethods = //init with options and so on
}
NewApp.prototype.logic = function(){
// Note 1.
var app = this
//Note 3.
this.customObjectWithMethods.method{
if(app.currentpage < 3)
// Note 2.
app.navigate(app.logic)
}
}
NewApp.prototype.navigate = function(sender){
var app = this;
this.customObjectWithMethods.method{
app.currentpage++;
this.method(function() {
return app.currentPage === 2;
}, sender(), this.terminate);
}
}
Note 1: I need to create a reference because after that, this doesn't
work anymore to refer to the current object.
Note 2: After the check I want to do some logic in another method and repeat the current function, but when the function runs again it breaks on the method (this.customObjectWithMethods) because this doesn't exists.
Note 3: This is where it breaks because "this" works the first time not the second time.
It gets very complicated like this with the this-keyword, which makes me think that my design may be flawed.
Is there any solution for this problem, or should I refactor it ?
Surely it will become complicated, thiskeyword doesn't always refer to the main object but to the scope where it is used, take a look at Scope and this in JavaScript for further information.
This is your way to go, make a variable that contains your constructor and add these two methods to this variable, after that you can call your functions:
var newApp = function newApp(name){
this.name = name;
this.currentPage = 1;
//Make a reference to your object here
var THIS = this;
this.logic = function(){
var sender = this;
THIS.customObjectWithMethods.method = function(){
if(THIS.currentpage < 3)
THIS.navigate(sender);
}
}
this.navigate = function(sender){
this.customObjectWithMethods.method = function(){
THIS.currentpage++;
this.method(function() {
return THIS.currentPage === 2;
}, sender(), this.terminate);
}
}
}
And this is how to use your constructor and its methods:
var app = newApp("Test");
//Call the first method
app.customObjectWithMethods();
//Thenn call the second one
app.logic();
Some syntax errors & style issues - here is a short correction
var myFunction = function(){
//code here
};
var mySecondFunction = function(){
//code here
};
function NewApp(name){
this.name = name;
this.currentPage = 1;
this.customObjectWithMethods = function(){}; //empty function so calling doesnt resolve in error
}
NewApp.prototype.logic = function(){
this.customObjectWithMethods.method = mySecondFunction.bind(this);
}
NewApp.prototype.navigate = function(sender){
this.customObjectWithMethods.method = myFunction.bind(this);
}
I have moved the 2 functions outside of the constructor Function so they dont get recreated every time you call the constructor functions.
with _.bind(this) the "this"-reference gets passed into the scope of your functions (i think this is more pretty than creating another var).
with
var reff = new NewApp('namename');
you can get started calling your functions now:
ref.logic();
maybe this approach works for you?

JS and methods in class emulation

I am new to this so I'll try to be as detailed as possible.
I am creating a JS class using functions.
function Bandstar(p, id)
{
var numberOfSides, Xcenter, Ycenter;
var canvas;
var circlesInt;
var linesInt;
var lines;
var linesInt2;
var linesMidToEdge;
.....
When it comes to methods, I just declare them like this:
this.IAset = function(a)
{
for (var i =0; i<= this.numberOfSides-1;i++)
{
if (parseInt(a[i]) == a[i])
this.circles[i].value = a[i];
else
return false;
}
this.IArepaint();
return true
}
and that's pretty much it.
The problem now is that, when I am creating a specific method:
this.moveFunc = function(e)
{
var p = e.target;
this.IAmove(p);
};
with the method IAmove being declared as follows:
this.IAmove = function(p)
{
var m = (p.oTop - this.Ycenter ) / (p.oLeft - this.Xcenter);
var linea = m * ( p.left - p.oLeft) + p.oTop;
var ejeX = p.left;
...
}
The compiler just keeps throwing this error:
Uncaught TypeError: Object function (e) { var p = e.target; this.IAmove(p); } has no method 'IAmove'
So, the thing is that I am declaring moveFunc as a function but then when I try to get to use this property, it doesn't actually use the instance of the class above it, (Bandstar), but of itself (and obviously moveFunc doesn't have a method called IAmove, the class on which it is being created is)...
I thought this was just like it worked, but I must not be getting the concept of heritage and class morphology in JS right.
How can I access a method in a class from another method inside that same class? If I just write IAmove(p) it will just say the method IAmove() is undefined (because it isn't, it's part of the Bandstar namespace.
I know it must be something stupid that I'm not seeing. Any suggestions?
The context inside your function may change based on your call. So this could not be the original object.
You should add a private var like:
function Bandstar(p, id)
{
var numberOfSides, Xcenter, Ycenter;
var canvas;
var that = this; // add this line
and use it when you try to call function (or get properties) of original object
this.moveFunc = function(e)
{
var p = e.target;
that.IAmove(p); // change this line
};
for be sure to point to original object.
EDIT: According with comments you can read more infos in Stuart's answer
You probably wants to add these methods to your Bandstar prototype.
function Bandstar(p, id)
{
// ...
}
Bandstar.prototype.IAset = function(a)
{
// ...
}
Bandstar.prototype.moveFunc = function(e)
{
var p = e.target;
this.IAmove(p);
};
This way your methods keep the Bandstar context, and this will remain Bandstar instance reference.
Have a look at Object.prototype reference
Assuming all of those methods are defined in the constructor the syntax is correct. The following fiddle illustrates it working, the code is based on a simplified version of your code...
function Test() {
this.move = function(p) {
document.write("Moved to: " + p);
};
this.another = function(e) {
this.move(e.target);
};
}
var test = new Test();
test.another({target: "The moon"});
The cause of the issues is likely to be one of two three things:
How the method moveFunc is called
When the method moveFunc is called
How the method IAmove is called (it may not be the moveFunc function that is causing that error).
The this keyword will reference the scope of the function which is typically the owner of the method...
var bandstar = new Bandstar(p, id);
bandstar.moveFunc(e); // this should be bandstar
The only reasons that might not be the case is if you are explicitly binding the moveFunc function to another object or a more common situation is that the function is being called without being attached to the owner...
var bandstar = new Bandstar(p, id);
var moveFunc = bandstar.moveFunc(e);
moveFunc(e); // this will now be window
The this keyword will default to window if the owner is detached from it. You can bind the function using.
var bandstar = new Bandstar(p, id);
var moveFunc = bandstar.moveFunc(e);
moveFunc.bind(bandstar);
moveFunc(e); // this will now be bandstar again
In the second issue both methods must be defined in the constructor before the moveFunc function can be called
this.moveFunc = function(e) { ... }
this.moveFunc(e); // Will throw an error that IAmove does not exist in the object
this.IAmove = function(p) { ... }
You can log the this object using console.log(this); to find out what it is.
It looks as though from your error that you are trying to call IAmove on the moveFunc function. For this error to be caused you would need to have called moveFunc with itself as the owner or the bound object... Which is not a particularly likely.
I have a hunch that the error may not actually be related to the this.IAmove(p); call in the moveFunc as assumed. Post the stack trace and we can use that to find where the error occurred.

Javascript Sandbox Pattern example implementation

On Page 101 of Stoyan Stefanov's great book "JavaScript Patterns" he explains the sandbox pattern.
I liked his book much but I really missed some real life examples here and then to better understand what he talks about.
Like the sandbox pattern!
I'm looking for a real life working implementation, like a copy&paste starting point, just a simple example that will work to fully understand it.
Is there any?
I've simplified Stoyan's example in an attempt to make it easier to understand what's going on. I've also commented it more thoroughly.
/*First define the modules of the sandbox. These will be defined
as properties on the constructor function because this is a
convenient place to keep them.*/
Sandbox.modules = {};
Sandbox.modules.returnNumbers = function(MYAPP) {
MYAPP.return100 = function() {return 100;};
};
Sandbox.modules.returnLetters = function(MYAPP) {
MYAPP.returnABC = function() {return "ABC";};
};
function Sandbox() {
/* Because Sandbox is a constructor, an new object is automatically
created. Because we're in the constructor, we refer to this new object
as 'this'.
A constructor would typically be used as part of an assignment, e.g.
myObject = new Sandbox().
However, it's also legitimate javascript to use a constructor without
the assignment by just writing new Sandbox() with no assignment. The
constructor does return an object, it's just that it doesn't get
assigned to anything so is discarded.
We're going to add functionality (methods) to the 'this' object, but
rather than returning it, we will pass it to the callback function, so
the methods can be used immediately.
*/
var args = Array.prototype.slice.call(arguments); //Put the arguments
//of the call to the Sandbox constructor in an array called args.
var callback = args.pop(); //The last argument is the callback
var requiredmodules = args; //The remaining arguments are the require
// modules
//For each of the modules in 'requiredmodules', add the module's
//methods to 'this'
for (i=0; i< requiredmodules.length; i++) {
Sandbox.modules[requiredmodules[i]](this);
}
//'this' now has methods returnNumbers and returnLetters
//Call the callback. In the example below, 'this' will be called
//MYAPP, which within the callback will have all the methods from
//the required modules.
callback(this);
}
//Finally here is an example of usage
new Sandbox('returnNumbers', 'returnLetters', function (MYAPP) {
console.log(MYAPP.return100());
console.log(MYAPP.returnABC());
});
Stoyan Stefanov mentions in the same chapter that YUI version 3 implements the Sandbox pattern. The YUI add method (API) registers modules and the use method (API) loads the specified ones in the sandbox instance. There are links to the source js file in the API documentation.
Virtually all YUI code examples use this pattern to work with the YUI library. Defining a module is rarely needed - YUI has many core ones and there is a page for custom modules added by the community.
So I tried and came up with this solution:
function Sandbox() {
// turning arguments into an array
var args = Array.prototype.slice.call(arguments),
// the last argument is the callback
callback = args.pop(),
// modules can be passed as an array or as individual parameters
modules = (args[0] && "string" === typeof args[0]) ? args : args[0],
i;
// make sure the function is called
// as a constructor
if (!(this instanceof Sandbox)) {
return new Sandbox(modules, callback);
}
// add properties to 'this' as needed:
this.a = 1;
this.b = 2;
// now add modules to the core 'this' object
// no modules or "*" both mean "use all modules"
if (!modules || '*' === modules) {
modules = [];
for (i in Sandbox.modules) {
if (Sandbox.modules.hasOwnProperty(i)) {
modules.push(i);
}
}
}
// initialize the required modules
for (i = 0; i < modules.length; i += 1) {
Sandbox.modules[modules[i]](this);
}
// call the callback
callback(this);
// any prototype properties as needed
Sandbox.prototype = {
name: "Sandbox",
version: "1.0",
getName: function() {
return this.name;
}
}
};
Sandbox.modules = {};
Sandbox.modules.color = function (box) {
// private
var initialColor = $('#main').css('color');
// set a red color
box.setMainRed = function() {
$('#main').css('color','red');
return false;
},
// get the current color
box.getInitialColor = function () {
return initialColor;
};
}
// another module
Sandbox.modules.style = function (box) {
// set a red color
box.setStyle = function() {
$('#main').css('font-style','italic');
return false;
};
}
// page ready
$.ready(
Sandbox(['color', 'style'], function (box) {
console.log(box);
box.setMainRed();
box.setStyle();
console.log('try access initialColor: ', box.initialColor);
console.log('get initial color: ', box.getInitialColor());
})
);
But I am really unsure weather this is what I should be doing.
Especially adding the "modules" is somewhat confusing. Also earlier in the book he uses the namespace-pattern for this task, but not here. Why? Can't you do it here too?
But I failed to combine these two patterns.
Namespace pattern example inspired by the book:
var APP = APP || {};
// namespace function
APP.namespace = function (nsString) {
var parts = nsString.split('.'),
parent = APP,
i;
// strip redundant leading global
if ("APP" === parts[0]) {
parts = parts.slice(1);
}
for (i = 0; i < parts.length; i += 1) {
// create a property if it doesn't exist
if ("undefined" === typeof parent[parts[i]]) {
parent[parts[i]] = {};
}
parent = parent[parts[i]];
}
return parent;
}
// constructors
APP.namespace('modules.Color');
// immediate function
APP.modules.Color = (function () {
var currentColor = $('#main').css('color'),
// set a red color
setMainRed = function() {
$('#main').css('color','red');
return false;
},
// get the current color
getCurrentColor = function () {
return currentColor;
};
// revealing module pattern
return {
setMainRed: setMainRed,
getCurrentColor: getCurrentColor
};
}());
var doSomething = function () {
var color = APP.modules.Color;
color.setMainRed();
console.log(color.currentColor);
console.log(color.getCurrentColor());
return false;
}
// page ready
$.ready(
doSomething()
);
Here is example with detailed comments:
(function(){
/* function constructor */
function Sandbox(){
//Change arguments to array, as you know 'arguments' are not a true JS array
//Array.prototype.slice will provide shallow copy of 'arguments'
var args = Array.prototype.slice.call(arguments),
//remove last element from array and return it to caller
//our last argument is callback
callback = args.pop(),
//We can pass modules as strings or as array
//if first element is a string, take all arguemnts
//otherwise take one element (array)
modules = (args[0] && typeof args[0] === "string") ? args : args[0],
modulesLength = modules.length,
i;
//handle calling function constructor without 'new' keyword
if(!(this instanceof Sandbox)){
//Invoke me again!
return new Sandbox(modules, callback);
}
//we can add properties to 'this'
this.someProp = "Initialized property";
//Initialize all required modules
for(i = 0; i < modulesLength ; i++){
//pass reference to 'this' for each required module and invoke it
//'this' is poiting to new object which was created
//after calling new Sandbox()
Sandbox.modules[modules[i]](this);
}
//Invoke callback and pass 'this'
//now 'this' cotains all methods and properties
//attached in modules functions
callback(this);
};
//We can optionally create Sandbox methods
Sandbox.prototype = {
version: "1.0.1",
createdAt: new Date()
};
/* function as a first class object - saving all modules*/
Sandbox.modules = {};
/*Create $http,$scope and $ajax modules */
/*We need box object to add new functionality*/
/*We are creating new methods by attatching them to box obect*/
/*box is a reference to 'this' called as initializator from function constructor*/
Sandbox.modules.$http = function(box){
box.get = function(){
console.log("$http.get");
};
box.post = function(){
console.log("$http.post");
};
box.prop = "I'm $http property";
};
Sandbox.modules.$scope = function(box){
box.inject = function(param1, param2){
console.log("$scope.inject: " + param1 + " " + param2);
};
box.destroy = function(o){
console.log("$scope.destroy: " + o + " has been destroyed!");
};
};
Sandbox.modules.$ajax = function(box){
box.call = function(){
console.log("$ajax.call");
};
};
//Sandbox without calling 'new' was handled in function constructor
//We are requesting for 2 modules: $scope and $http
//callback function is our new playground
//box object has $scope and $http methods and properties inside, we are ready to go!
Sandbox(["$scope", '$http'], function(box){
console.log(box); //contains methods from $scope and $http
console.log(box.inject("John", "Doe"));
console.log(box.post());
//we can event nest our playgrounds
Sandbox(["$ajax"], function(box){
console.log(box); //contains only $ajax methods and properties
console.log(box.call());
//we can't invoke $scope or $http functions here
});
//we can't invoke $ajax functions here
});
})();
Link to JSFiddle: http://jsfiddle.net/Lodse4hj/

Categories

Resources