I have a bunch of functions that look like this
'tea' : function (caller) {
this.speak(this.randomOption('tea_msg'));
},
'chill' : function (caller) {
this.speak(this.randomOption('chill_msg'));
},
and what i want is to merge them to something like this
'tea' ,'chill': function (caller) {
// get which function name was called in var lets call it fun
this.speak(this.randomOption(fun +'_msg'));
}
Is this possible or am I going about this the wrong way?
You can make a function-maker-function that would get pretty close
function speaker(message){ return function(caller){
this.speak(this.randomOption(message + '_msg');
};}
{
'tea': speaker('tea'),
'chill': speaker('chill')
}
And if you want to avoid retyping the 'tea' and 'chill' you can use the [] object-subscripting syntax together with some sort of loop:
var funs = {};
var msgs = ['tea', 'chill'];
for(var i=0; i<msgs.length; i++){
var msg = msgs[i];
funs[msg] = speaker(msg);
}
Just be careful about the closures-inside-for-loops if you want to write the speaker function inline instead.
[ 'tea', 'chill', 'elephant', 'moose' ].forEach(function (val) {
//creating a closure so `val` is saved
(function (val) {
obj[ val ] = function ( caller ) {
this.speak( this.randomOption(val + '_msg') );
};
}(val));
});
You can also write a generic say function:
say : function ( caller, type ) {
this.speak( this.randomOption(type + '_msg') );
}
You could pass the function name as a variable?
'text': function (caller, functionName) {
// get which function name was called in var lets call it fun
this.speak(this.randomOption(functionName +'_msg'));
}
Related
I am using node.js.
I have a function that can be called this way;
add_row({location:'L1', row_name:'r1', value:'18.4'});
I have a string like this;
var str_param = "location:'L1', row_name:'r1', value:'18.4'";
I tried to do something like this to keep my code simple;
add_row(str_param);
It did not work. What is a good way to use str_param to call add_row?
You could convert the string to an object that the function accepts.
function toObj(str) {
const a = str.split(/,.?/g);
return a.reduce((p, c) => {
const kv = c.replace(/'/g, '').split(':');
p[kv[0]] = kv[1];
return p;
}, {});
}
toObj(str); // { location: "L1", row_name: "r1", value: "18.4" }
DEMO
I think this may be your issue:
{location:'L1', row_name:'r1', value:'18.4'} // Object
var str_param = "location:'L1', row_name:'r1', value:'18.4'"; // Not object
var str_param = "{location:'L1', row_name:'r1', value:'18.4'}"; // Object String
I do not use Node JS but just taking a shot in dark. If not you could just make function like:
function addRow(pLocation, pRowName, pValue) {
var row = {
location: pLocation,
row_name: pRowName,
value: pValue
}
// Logic ....
}
If that does not work try using Object string and look at function ParseJSON I believe it's called.
I know we can get all arguments in javascript inside function when it is called anywhere . we can get extra arguments which we didnt asked also .
But can we get only asked arguments on javascript function?
Like :
function a(a,b){
console.log(arguments);
}
if we call function a somewhere a(1,2,3,4,5)
then the output will be like [1,2,3,4,5]
but i want only [1,2] as i have expected only two params in function?
My condition is
index: (req, res, next) => {
var params = ['batch_id', 'section_id', 'subject_id', 'term', 'assesment_id', 'assesment_type'];
var _ = req._;
req.utils.requestValidation([req,res,next], params,'query')
// But i dont want to send params like above always instead like below
req.utils.requestValidation(arguments, params,'query')
and where it is called is
requestValidation: (data, options, param) => {
if (!options) return;
var _ = data[0]._ || data._;
var rules = {};
var data = {};
var sanity = {};
var elr = [validator.escape, validator.ltrim, validator.rtrim];
options.map((item, index) => {
rules[item] = 'required';
sanity[item] = elr;
});
data[param] = sanity;
if (typeof data != 'string') {
sanitizer.setOptions(data);
var data = sanitizer.sanitize(data[0], data[1], data[2],param);
return data[0].validation.validate(rules, data[0][param]);
}
return data.validation.validate(rules, data[param]);
},
if you need only two parameters just cut arguments to two items
if you want automatic this you can write function-wrapper, something like this:
function wrapperCutParams(func, paramsCount){
return function(){
var args = Array.prototype.slice(arguments, 0);
if(args.length > paramsCount){
args = args.slice(0, paramsCount)
}
func.apply(this, args)
}
}
Then
var a = function a(a,b){
console.log(arguments);
}
a = wrapperCutParams(a, 2)
Or just
a = wrapperCutParams(function a(a,b){
console.log(arguments);
}, 2)
Since you declared those arguments the most readable way would be to use them as they are, if you need to put them in an array, just do it.
myArgs = [a, b];
Write a higher-order function which takes the underlying function as a parameter, and returns a function which truncates the argument list to the number of arguments the function is asking for, based on its length property.
function slice_args(fn) {
return function() {
return fn.apply(this, Array.prototype.slice.call(arguments, 0, fn.length));
};
}
Then
function a(a,b){
console.log(arguments);
}
var b = slice_args(a);
b(1,2,3,4,5)
> [1, 2]
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 9 years ago.
This is a simplified code that runs on Node.js crawler and it gets all data.
But how do I insert inside the "callback": function value of var "i" from cycle for(var i=0... When I'm adding name: datas[i].name it returns an error:
TypeError: Cannot read property 'undefined' of undefined
var Crawler = require("crawler").Crawler;
var crawler = new Crawler;
var datas = [
{name: 'John', url: 'john025'},
{name: 'Jim', url: 'jim04567'}
];
function crauler(done) {
for (var i = 0; i < datas.length; i++) {
var link = 'http://somesite.com/' + datas[i].url;
crawler.queue([{
"uri": link,
// inside this func
"callback": function (error, result, $, datas, i) {
var arr = $('.blogpost').map(function (index) {
var str = $(this).attr('href');
var object = {
numb: str,
name: datas[i].name
};
return obj;
}).get().join(',');
done(arr);
} }]) }; };
crauler (function (arr) {
console.log(arr);
});
You can't pass datas and i into callback functions like this. What arguments that the callback functions will be called with are up to the caller, you don't have the control of it.
You're seeing "TypeError: Cannot read property 'undefined' of undefined" because you want your callback function to have datas and i as parameters; but the caller will call the callback with the first 3 arguments only [crawler callback reference], so the datas and i are undefined.
Therefore, you should remove the datas and i from in line:
"callback": function (error, result, $, datas, i) {
Because datas is defined in the outer scope of the callback function, the callback can access datas without any special treatment. For the variable i, it's a little bit tricky as mentioned in other answers, so you need to create a closure for it.
So, your callback function definition should be something looks like the following:
"callback": (function(i) { // create closure for i
return function (error, result, $) { // no more datas and i here
var arr = $('.blogpost').map(function (index) {
var str = $(this).attr('href');
var object = {
numb: str,
name: datas[i].name // access datas as it
};
return obj;
}).get().join(',');
done(arr);
}
})(i)
You're trying to create a closure around i inside of a loop which is causing you problems. This answer should help you:
JavaScript closure inside loops – simple practical example
You need a closure to capture the values, this is one way to solve the problem. Read up on closures.
Javascript
var Crawler = require("crawler").Crawler;
var crawler = new Crawler;
var datas = [{
name: 'John',
url: 'john025'
}, {
name: 'Jim',
url: 'jim04567'
}];
function queue(link, i) {
crawler.queue([{
"uri": link,
// inside this func
"callback": function (error, result, $, datas, i) {
var arr = $('.blogpost').map(function (index) {
var str = $(this).attr('href');
var object = {
numb: str,
name: datas[i].name
};
return obj;
}).get().join(',');
done(arr);
}
}]);
}
function crauler(done) {
for (var i = 0; i < datas.length; i++) {
var link = 'http://somesite.com/' + datas[i].url;
queue(link, i);
};
crauler(function (arr) {
console.log(arr);
});
I have written some javascript that I would to encapsulate in a closure so I can use it elsewhere. I would like do do this similar to the way jQuery has done it. I would like to be able to pass in an id to my closure and invoke some functions on it, while setting some options. Similar to this:
<script type="text/javascript">
_snr("#canvas").draw({
imageSrc : someImage.png
});
</script>
I have read a lot of different posts on how to use a closure to do this but am still struggling with the concept. Here is where I left off:
_snr = {};
(function (_snr) {
function merge(root){
for ( var i = 1; i < arguments.length; i++ )
for ( var key in arguments[i] )
root[key] = arguments[i][key];
return root;
}
_snr.draw = function (options) {
var defaults = {
canvasId : 'canvas',
imageSrc : 'images/someimage.png'
}
var options = merge(defaults, options)
return this.each(function() {
//More functions here
});
};
_snr.erase = function () {};
})(_snr);
When ever I try to call the draw function like the first code section above, I get the following error, '_snr is not a function'. Where am I going wrong here?
EDIT
Here is what I ended up doing:
function _snr(id) {
// About object is returned if there is no 'id' parameter
var about = {
Version: 0.2,
Author: "ferics2",
Created: "Summer 2011",
Updated: "3 September 2012"
};
if (id) {
if (window === this) {
return new _snr(id);
}
this.e = document.getElementById(id);
return this;
} else {
// No 'id' parameter was given, return the 'about' object
return about;
}
};
_snr.prototype = (function(){
var merge = function(root) {
for ( var i = 1; i < arguments.length; i++) {
for ( var key in arguments[i] ) {
root[key] = arguments[i][key];
}
}
return root;
};
return {
draw: function(options) {
var defaults = {
canvasId : 'canvas',
imageSrc : 'images/someimage.png'
};
options = merge(defaults, options);
return this;
},
erase: function() {
return this;
}
};
})();
I can now call:
<script type="text/javascript">
_snr("#canvas").draw({
imageSrc : someImage.png
});
</script>
Because you declared _snr as an object and not a function. Functions can have properties and methods, so there's various ways to achieve what you want, for example one of them would be say...
_snr = function(tag) {
this.tag = tag;
}
_snr.foo = function() {
//Code goes here
}
You can also pass the outer context into a closure to hide your variables from accidentally polluting the global namespace, so like...
(function(global) {
var _snr = function(tag) {
this.tag = tag;
}
_snr.foo = function() {
//Code goes here
}
//export the function to the window context:
global._snr = _snr;
})(window);
window._snr('#tag').foo('wat');
Happy coding.
Because your _snr is an object, not a function. You have to call it like this:
_snr.draw({
canvasId: '#canvas',
imageSrc: 'someImage.png'
});
When you do _snr('#canvas') that is a function call which is why you're getting that error. _snr is an object with some methods attached to it such as draw() and erase(). The reason jQuery is able to pass arguments into the $ is because they return the $ as a function object which is why we're able to pass it various selectors as arguments.
You are going wrong at the first line _snr = {}
It needs to be
_snr = function(){
selector = arguments[0]||false;
//snr init on dom object code
return _snrChild;
}
Im on a mobile phone but when im on a pc I will maybe fix the whole code c:
Here you have a snr object and that has erase and draw methods. What you intend to do is to write a _snr function which will get an id and return a wrapper object. That returned object should have erase and draw methods. so you can do
var returnedObject = _snr("my_id");
returnedObject.draw("image.png");
I'm trying to generate an array of callback functions for use in a jQuery UI dialog
Given the following code:
for(var x in methods)
{
buttons[x] = function() {
var method = methods[x];
var data = $('#dialog_'+model+' form').serialize();
data += '&form='+model;
$.post(
$('#dialog_'+model+' form').attr('action')+'method/'+method+'/',
data,
function(r) {
handleFormReturn(r);
},
'json'
);
};
}
When called, the function will obviously use the last known value of the variable x and not the one that I need. How can I avoid this problem without having to resort to using eval() ?
Maybe I'm going about this all wrong but as far as I know it's not possible to pass a parameter to the callback.
You need to create a new variable scope during each pass in the for loop. This can only be done by invoking a function.
function createButton(x) {
buttons[x] = function () {
var method = methods[x];
var data = $('#dialog_' + model + ' form').serialize();
data += '&form=' + model;
$.post(
$('#dialog_' + model + ' form').attr('action') + 'method/' + method + '/', data, function (r) {
handleFormReturn(r);
}, 'json');
};
}
for (var x in methods) {
createButton(x);
}
Now the value of x that the buttons[x] function refers to will be the one that was passed to createButton.
An immediate function version of patrick dw's solution:
for (var x in methods) {
buttons[x] = (function (x) {
return function () {
/* ... x is local for this function ... */
};
})(x);
}
You need to create a closure for each element in methods array:
for(var x in methods) {
buttons[x] = (function(x) {
var method = methods[x];
return function () {
var data = $('#dialog_'+model+' form').serialize();
data += '&form='+model;
$.post(
$('#dialog_'+model+' form').attr('action')+'method/'+method+'/',
data,
function(r) {
handleFormReturn(r);
},
'json'
);
};
})(x);
}