I'm trying to dynamically create function when iterating over an array and
I need the arguments in the array to be set according to the value of the current index.
For example:
var array = ['apple','orange','banana'];
I need to have these three functions:
function() { return 'apple' };
function() { return 'orange' };
function() { return 'banana' };
I tried to return a constructed function from an external one but the expression in it won't evaluate and I end up with three of these:
function() { return array[i] };
Is there a way to dynamically create such a function without using eval()?
You can create the functions like so:
var funcs = {};
for (var i=0;i<array.length;i++)
{
funcs[array[i]] = (function(val)
{
return function()
{
return val;
};
}(array[i]));
}
which can be called like so:
funcs.apple();// returns "apple"
But also, depending on the value of some var:
var someVar = 'banana';
if (funcs.hasOwnProperty(someVar))
{
funcs[someVar]();
}
If what you're after is a single (possibly global) function, that depending on, for example, the URI, you just have to write this:
var myFunc = (function()
{
var retVal = location.mathname.match(/^\/([^\/]+)/)[1];
return function()
{
return retVal;
};
}());
Note that the function won't be hoisted, as it is an expression.
I've written a lot about IIFE's (Immediatly Invoked Function Expressions), how they work and why, so best check my answer here if you don't fully understand these code snippets. It's quite easy once you get the logic, and you'll soon find yourself writing code like this all the time... tey really are incredibly powerful things, closures are!
This is what I would do:
function constant(value) {
return function () {
return value;
};
}
var array = ["apple", "orange", "banana"];
var fruit = array.map(constant);
alert(fruit[0]()); // apple
alert(fruit[1]()); // orange
alert(fruit[2]()); // banana
Simple. See the demo: http://jsfiddle.net/tfS2F/
You can also use the initial array as a key as follows:
alert(fruit[array.indexOf("orange")]()); // orange
See the demo: http://jsfiddle.net/tfS2F/1/
This one will not work, i leave it to illustrate the infamous loop problem:
The best way to achieve this would be to create a context var before creating the function, hope this illustrates it http://jsfiddle.net/jkymq/
var array = ['apple','orange','banana'];
var methods = {}
for (pos = 0 ; pos < array.length; pos ++){
var target = array[pos];
var newMethod = function(){alert (target);}
methods[target] = newMethod;
}
for (foo in methods){
methods[foo]();
}
Related
Today I was at job interview - and I had been presented with a task.
I have never encoutered such a function, so there it is:
Write down a function which after triggering like this:
console.log('hello'.repeatify(3))
returns
hellohellohello
I was trying to create an empty object and append a method repeatify, however i just don't know how to trigger it just with 'hello'.
Maybe i should change console.log method?
Thanks for the help :)
Wasn't that really hard. You have to make a new function for String.prototype and use repeat() function to multiply given string by given argument.
String.prototype.repeatify = function(count){
return this.repeat(count);
};
console.log('hello'.repeatify(3))
You can create your own prototype methods for every object, but you shouldn't do it with native objects:
String.prototype.repeatify = function(num) {
return this.repeat(num);
}
console.log('hello'.repeatify(3));
read more about this bad practice here
String.prototype.repeatify = function(amount)
{
var endString = "";
for(var i = 0; i < amount; ++i)
{
endString += this;
}
return endString;
}
Just modify String's prototype object:
String.prototype.repeatify = function (A) {
var i = 1;
var a = this;
while (i++ < A) {
a += this;
}
return a;
}
console.log('hello'.repeatify(3));
// outputs hellohellohello
There are mixed feelings on modifying the prototype of built in objects. I have both that it is just fine and that it is a bad practice. Personally I try to avoid it and prefer defining a standalone function to modifying the prototype of something I didn't create.
ES6 version
repeatify
String.prototype.repeatify = String.prototype.repeatify || function (times = 1) {
// ES5 this
return [...``.padStart(times, ` `)].map((item, i) => item = this).join(``);
};
`abx`.repeatify(3);
// "abxabxabx"
Good day! I have this code:
function MyArray() {}
MyArray.prototype.length = 0;
(function() {
var methods = ['push', 'pop', 'shift', 'unshift',
'slice', 'splice', 'join'];
for (var i = 0; i < methods.length; i++) (function(name) {
MyArray.prototype[ name ] = function() {
return Array.prototype[ name ].apply(this, arguments);
};
})(methods[i]);
})();
I need explanation. I understood that "methods" is array of real methods, which just "exported" to our new class. But, what is this: MyArray.prototype.length = 0; ? Author create new prototype property and assign it zero. And later use this new property!
var mine = new MyArray();
mine.push(1, 2, 3);
assert(mine.length == 3 ...
.....
How it is work? "length" have not instantiation in code above!
Its getting initialized at zero so that if you never call any of its functions, it will return zero (like a real array) and not undefined. Also it needs to start at zero so that the methods update it correctly. in your example, length its 3 because the push method did so.
You can't really subclass Array http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/
So if you create an instance of MyArray you can't do: MyArr[0]=...
You can wrap an array inside MyArray and take advantage of the Array functions:
var MyArray=function() {
this.arr=[];
[].push.apply(this.arr,arguments);
//following doesn't work in older browsers
Object.defineProperty(this,"length",{
get:function(){return this.arr.length;},
enumerable:true,
configurable:true
});
}
MyArray.prototype.valueOf=function(){return this.arr;};
(function() {
var methods = ['push', 'pop', 'shift', 'unshift',
'slice', 'splice', 'join'],i=methods.length
while(--i!==-1){
;(function(name) {
MyArray.prototype[ name ] = function() {
console.log(arguments);
return Array.prototype[ name ].apply(this.arr, arguments);
};
}(methods[i]));
}
}());
var mArr1=new MyArray(1,2,3);
console.log(mArr1.slice(0,1));
//you cannot do this: myArr1[0]=22;
I fully realize that this code is kinda strange by the very nature of javascript, but look at it:
var arr = [
function () {
this.b = "hello";
//or var b = "hello";
arr[1]();
},
function () {
var str = RealtimeParentFunc.b;
alert(str);
// want to get "hello"
}
];
// want to get "hello"!
arr[0]();
Maybe it's impossible, but I wonder if there's a way of doing this. How to get a variable of an array's function that called a function in the same array?
UPDATE
I had to go back to this problem and decided to post an update: of course I can't reference arr[i] from within itself, but I must emphasize that the code above was not supposed to be taken too literally: it was just a demo of what I wanted; but it looks like I can do what I want if I have a small "adjuvant" function that is previously defined in the code:
var a = function (i, v) {
return arr[i]()[v];
};
var arr = [
function () {
var h = "hello, ";
var n = a(2, "n");
return {
"h": h+n
}
}, function () {
var h = a(0, "h");
alert(h)
}, function () {
return {
"n": "your name"
}
}];
arr[1]();
You can't define an array and access it's values at the same time, since at the time of creation, the arr2 does not exist yet.
The [ function(){ .. }, function(){ .. }] array isn't created and set to arr2 until it reaches the ; semicolon at the end. Therefore you can't reference arr2[1]() while it's being defined.
On that note, what you can do is define the array first, then .push() the functions into it, but you're still limited to doing it sequentially:
var arr2 = [];
arr2.push(
function () {
var b = "hello";
//arr2[1](); // CAN'T DO THIS since arr2[1] doesn't exist
return {
b: b
};
});
arr2.push(
function () {
var str = arr2[0]().b;
alert(str);
// want to get "hello"
});
// want to get "hello"!
arr2[0]();
arr2[1]();
See working fiddle: http://jsfiddle.net/amyamy86/5k57X/
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Javascript - How to extend Array.prototype.push()?
How can I be notified (run a pre-defined function) of any change to a registered array (or at least any addition or removal of elements)?
I tried using prototype. I don't want to be scolded for not providing SOME code examples of my own. So here's how I would like to use it.
var myArray = [];
myArray.bind(function() {
console.log('wtf'); // Wed Thu Fri and what were you thinking?
});
I don't need overkill. I basically know the Array function scope that I will be using (push, pop, splice and maybe a couple others). It's a way to use backbone's MVC. I want to run logic on an array and THEN have the views highlighted accordingly. But the view is already attached to a collection. Any change to that collection re-renders the actual DOM's in the view. I don't want that. I simple want to add, or remove, a class to the corresponding DOM's in the view for CSS purposes.
What I did is I made my own "array" type that just extended the prototype array, which then I added my own handlers to.
For example:
var MyArray = function() {
var arr = [];
arr.push = function() {
console.log("PUSHING", arguments);
return Array.prototype.push.apply(this, arguments);
}
return arr;
};
Usage:
var arr = new MyArray;
arr.push(12, 3, 45);
...
Fiddle: http://jsfiddle.net/maniator/vF659/
You're looking for Object.observe, but it's not widely available yet. In Chrome Canary, with "Experimental JavaScript" enabled on about:flags you can try the following:
var arr = [];
Object.observe(arr, function(changes) {
console.log("The array changed. Changes:", changes);
});
Something like this will set up global monitoring of array push()'s.
(function() {
var _push = Array.prototype.push;
Array.prototype.push = function() {
console.log("push");
return _push.apply(this, arguments);
}
})();
Otherwise, as Neal suggested, you can create another class.
var MonitoredArray = function() {
var rv = [];
var _push = rv.push;
rv.push = function() {
console.log("push()");
console.log(arguments);
return _push.apply(this, arguments);
}
return rv;
}
To set up basic monitoring of N function calls at once.
var MonitoredArray = function() {
var rv = [];
// the names of the functions we want to log:
var logged_fns = ["push", "pop"];
for (var i in logged_fns) { (function() {
var name = logged_fns[i]
var fn = rv[name];
rv[name] = function() {
console.log(name + "()");
console.log(arguments);
return fn.apply(rv, arguments);
}
})()}
return rv;
}
A similar adaptation should work for the first example too.
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);