Related
I apologize in advance if this question is very simple, I'm a beginner in JavaScript.
I found a wealth of information about a resembling pattern (module pattern) but unless I am mistaken, this is either something different or an extension. Here is a typical code excerpt from the (wonderful) domjs project by Mariusz Nowak:
renameReserved = (function (rename) {
return function (scope) {
Object.keys(scope).forEach(rename, scope);
};
}(function (key) {
if (contains.call(reserved, key)) {
this['_' + key] = this[key];
delete this[key];
}
}));
I am finding it difficult to understand exactly what's happening here, even though each part taken independently is quite simple. Detailed help would be greatly appreciated, or a link to where I could learn more about this.
There are two functions involved here. First one
function (rename) {
return function (scope) {
Object.keys(scope).forEach(rename, scope);
};
}
And the other function object is passed as an argument to this function
function (key) {
if (contains.call(reserved, key)) {
this['_' + key] = this[key];
delete this[key];
}
}
Since we execute the first function, with an argument, (rename is the parameter which holds the function object passed) it returns another function which holds the function which we passed as argument because of the closure property.
I'm going to rewrite the code in a way that won't change what happens, but may make it a little clearer:
function makeNameReplacer( rename ) {
return function( scope ) {
Object.keys(scope).forEach(rename, scope);
}
}
function reservedWordRenamer( key ) {
if (contains.call(reserved, key)) {
this['_' + key] = this[key];
delete this[key];
}
}
renameReserved = makeNameReplacer( reservedWordRenamer );
So the first function is something that creates a function. The created function applies a name-substitution strategy to all the property names in a given object ("scope").
The second function is a strategy for replacing property names. Specifically, it checks to see if the property name passed in ("key") is in the set of reserved words. If it is, it replaces it with the name prefixed by an underscore, and removes the old property.
Thus the overall effect is that "renameReserved" becomes a function, one that takes an object as a parameter and which will scrub out property names that are reserved words.
You could come up with another strategy, and make another function. For example, if you wanted objects whose property names were all upper-case, you could do this:
function upperCaseRenamer( key ) {
var uckey = key.toUpperCase();
if (key !== uckey) {
this[uckey] = this[key];
delete this[key];
}
}
renameLowerCase = makeNameReplacer( upperCaseRenamer );
i have a question about passing an arugment in MongoDB javascript to a map function.
Currently, what i had in mind is something like this:
var map = function(n) {
if(this.x == n){
emit(this.x);
}
}
var reduce = function(key, values) {
values.forEach(function(x) {
//do something
});
return {nd:values};
}
db.smsdb.mapReduce(map(2), reduce, "collection")
But as i have tried to do this, the shell returns an error "not code"...so i'm guessing i'm not doing this the right way.
Does anyone have the right solution for this kind of problem, i would be more than glad to get it right.
Thanks
This line:
db.smsdb.mapReduce(map(2), reduce, "collection")
is calling map(2) with the result (undefined) being passed as the map function for mapReduce.
Instead do something like this:
db.smsdb.mapReduce(function(){ map.call(this, 2); }, reduce, "collection")
UPDATE
The above doesn't work because the map function isn't available in the scope the mapReduce map function is run. So you have to wrap it up into a single function that can generate the map function you need:
var mapper = function(n) {
function map() {
if(this.x == n){
emit(this.x);
}
}
return map;
};
db.smsdb.mapReduce(mapper(2), reduce, "collection");
scope In Mapreduce Stands For Global Parameters In Map Reduce
As pointed out by #Dave Griffith, you can use the scope parameter of the mapReduce function.
I struggled a bit to figure out how to properly pass it to the function because, as pointed out by others, the documentation is not very detailed. Finally, I realised that mapReduce is expecting 3 parameters:
map function
reduce function
object with one or more of the params defined in the doc
Eventually, I arrived at the following code in Javascript:
// I define a variable external to my map and to my reduce functions
var KEYS = {STATS: "stats"};
function m() {
// I use my global variable inside the map function
emit(KEYS.STATS, 1);
}
function r(key, values) {
// I use a helper function
return sumValues(values);
}
// Helper function in the global scope
function sumValues(values) {
var result = 0;
values.forEach(function(value) {
result += value;
});
return value;
}
db.something.mapReduce(
m,
r,
{
out: {inline: 1},
// I use the scope param to pass in my variables and functions
scope: {
KEYS: 1,
sumValues: 22// of course, you can pass function objects too
}
}
);
You can do this:
db.smsdb.mapReduce(function() { return map(2); }, reduce, "collection")
I should also point out that this solution also works:
var map = function() {
if(this.x == n){
emit(this.x);
}
}
db.smsdb.mapReduce(mapper, reduce, {out:"nodes",scope:{n:2}});
I want to compare each string in an Array with a given string. My current implementation is:
function startsWith(element) {
return element.indexOf(wordToCompare) === 0;
}
addressBook.filter(startsWith);
This simple function works, but only because right now wordToCompare is being set as a global variable, but of course I want to avoid this and pass it as a parameter. My problem is that I am not sure how to define startsWith() so it accepts one extra parameter, because I dont really understand how the default parameters it takes are passed. I've tried all the different ways I can think of and none of them work.
If you could also explain how the passed parameters to 'built in' callback functions (sorry, I dont know of a better term for these) work that would be great
Make startsWith accept the word to compare against and return a function which will then be used as filter/callback function:
function startsWith(wordToCompare) {
return function(element) {
return element.indexOf(wordToCompare) === 0;
}
}
addressBook.filter(startsWith(wordToCompare));
Another option would be to use Function.prototype.bind [MDN] (only available in browser supporting ECMAScript 5, follow a link for a shim for older browsers) and "fix" the first argument:
function startsWith(wordToCompare, element) {
return element.indexOf(wordToCompare) === 0;
}
addressBook.filter(startsWith.bind(this, wordToCompare));
I dont really understand how the default parameters it takes are passed
There is nothing special about it. At some point, filter just calls the callback and passes the current element of the array. So it's a function calling another function, in this case the callback you pass as argument.
Here is an example of a similar function:
function filter(array, callback) {
var result = [];
for(var i = 0, l = array.length; i < l; i++) {
if(callback(array[i])) { // here callback is called with the current element
result.push(array[i]);
}
}
return result;
}
The second parameter of filter will set this inside of the callback.
arr.filter(callback[, thisArg])
So you could do something like:
function startsWith(element) {
return element.indexOf(this) === 0;
}
addressBook.filter(startsWith, wordToCompare);
For those looking for an ES6 alternative using arrow functions, you can do the following.
let startsWith = wordToCompare => (element, index, array) => {
return element.indexOf(wordToCompare) === 0;
}
// where word would be your argument
let result = addressBook.filter(startsWith("word"));
Updated version using includes:
const startsWith = wordToCompare => (element, index, array) => {
return element.includes(wordToCompare);
}
function startsWith(element, wordToCompare) {
return element.indexOf(wordToCompare) === 0;
}
// ...
var word = "SOMETHING";
addressBook.filter(function(element){
return startsWith(element, word);
});
You can use the arrow function inside a filter, like this:
result = addressBook.filter(element => element.indexOf(wordToCompare) === 0);
Arrow functions on MDN
An arrow function expression has a shorter syntax compared to function expressions and lexically binds the this value (does not bind its own this, arguments, super, or new.target). Arrow functions are always anonymous. These function expressions are best suited for non-method functions and they can not be used as constructors.
For anyone wondering why their fat arrow function is ignoring [, thisArg], e.g. why
["DOG", "CAT", "DOG"].filter(animal => animal === this, "DOG")
returns []
it's because this inside those arrow functions are bound when the function is created and are set to the value of this in the broader encompassing scope, so the thisArg argument is ignored. I got around this pretty easily by declaring a new variable in a parent scope:
let bestPet = "DOG";
["DOG", "CAT", "DOG"].filter(animal => animal === bestPet);
=> ["DOG", "DOG"]
Here is a link to some more reading:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this
based on oddRaven answer
and
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
i did it 2 different way .
1) using function way .
2) using inline way .
//Here is sample codes :
var templateList = [
{ name: "name1", index: 1, dimension: 1 } ,
{ name: "name2", index: 2, dimension: 1 } ,
{ name: "name3", index: 3, dimension: 2 } ];
//Method 1) using function :
function getDimension1(obj) {
if (obj.dimension === 1) // This is hardcoded .
return true;
else return false;
}
var tl = templateList.filter(getDimension1); // it will return 2 results. 1st and 2nd objects.
console.log(tl) ;
//Method 2) using inline way
var tl3 = templateList.filter(element => element.index === 1 || element.dimension === 2 );
// it will return 1st and 3rd objects
console.log(tl3) ;
There is an easy way to use the filter function, access all params, and not over complicate it.
Unless the callback's thisArg is set to another scope filter does not create its own scope, and we can access params within the current scope. We can set 'this' to define a different scope in order to access other values if needed, but by default it is set to the scope it's called from. You can see this being used for Angular scopes in this stack.
Using indexOf is defeating the purpose of filter, and adding more overhead. Filter is already going through the array, so why do we need to iterate through it again? We can instead make it a simple pure function.
Here's a use-case scenario within a React class method where the state has an array called items, and by using filter we can check the existing state:
checkList = (item) => { // we can access this param and globals within filter
var result = this.state.filter(value => value === item); // returns array of matching items
result.length ? return `${item} exists` : this.setState({
items: items.push(item) // bad practice, but to keep it light
});
}
Here's the script:
function runScripts() {
if (arguments.length === 0) return;
chrome.tabs.executeScript(null, {
file: arguments[0]
}, function() {
arguments.shift();
runScripts.apply(null, arguments);
});
}
It doesn't work because arguments is not actually an array, it's just array-like. So how can I "shift" it or hack off the first element so that I can apply this function recursively?
var params = Array.prototype.slice.call(arguments);
params.shift();
You can check out this blog post which explains it in further detail.
I assume you want to reference the original arguments, instead of that from the callback you're passing to chrome.tabs.executeScript.
If so, you'll need to cache it first.
function runScripts() {
if (arguments.length === 0) return;
var args = [];
Array.prototype.push.apply( args, arguments );
chrome.tabs.executeScript(null, {
file: args.shift();
}, function() {
// using the modified Array based on the original arguments object
runScripts.apply(null, args);
});
}
[].shift.call(arguments) is also valid. I'm using this in production code and it works as expected.
With this approach, your function becomes a bit more succinct:
function executeScripts() {
if (arguments.length === 0) return;
chrome.tabs.executeScript(null, {
file: [].shift.call(arguments)
}, function() {
executeScripts.apply(null, arguments);
});
}
If you look on MDN, they state that shift() was implemented with this flexibility in mind.
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/shift
You can transform arguments into a regular array like this:
var args = Array.prototype.slice.call(arguments);
Just wanted to point out a potential problem with [].shift.call(arguments).
This seems to have the perhaps unclear intent of shifting your arguments - even for your function's named parameters - even if used prior to the shift statement.
For example,
function testShift (param1, param2) {
[].shift.call(arguments);
if (param1=="ONE") alert("ONE");
}
If you make the following call, what might you expect to happen?
testShift("ONE", "TWO");
If you expected param1 to stay "ONE", your fix is to set a var to param1 before the shift occurs. It looks like javascript is not binding param1 until the line it is called on - not when the function is called... so modifications to arguments prior to a parameter being used can have unexpected effects.
Hopefully now, you'll be able to expect it.
In ES6 you can now use Array.from() MDN ref
e.g.
const args = Array.from(arguments);
const str = args.shift();
You'll need to convert it to an array and then shift. Or, alternatively, drop the first item when converting to an array. Array.prototype.slice.call(arguments, 1) would work for this.
In newer versions of JS, we can now write:
function f(first, ...rest) { ... }
Or
function f() {
const [first, ...rest] = arguments;
}
Which is a little nicer than "shifting" off the first arg. However, if we did want to, we could first convert arguments into a proper array via Array.from or [...arguments].
Here is an article explains this really well. I copied some key points below.
http://www.javascriptkit.com/javatutors/arrayprototypeslice.shtml
For array, remember you can call the slice function to get a sub array.
var abc = [1,2,3,4,5];
abc.slice(0); //[1,2,3,4,5]
abc.slice(1,3); //[2,3]
Since the argument object is only array like, not really an array. The call() / apply() function basically just "borrow" the slice function from Array and use it on the Argument object, and you can even pass parameters into the slice function just as acting on the array.
var myobject ={ // array-like collection
length: 4,
'0': 'zero',
'1': 'one',
'2': 'two',
'3': 'three'
}
var myarray = Array.prototype.slice.call(myobject)
// returns myobject as a true array: ["zero", "one", "two", "three"]
var myarray = Array.prototype.slice.call(myobject, 1)
// returns ["one", "two", "three"]
The one remaining question is why we're calling slice() on the prototype object of Array instead of an array instance. The reason is because this is the most direct route to accessing the slice() method of Array when that's all we're interested in; we could have first created an array instance, but that's less efficient and arguably more abstruse:
var myarray = new Array().prototype.slice.call(myobject) // less efficient
You could convert the arguments to an actual array and then use that array in the rest of your logic in the function.
function runScripts()
{
var i=0, l=arguments.length, arr=[];
while(i<l)
{
arr.push(arguments[i++]);
}
...rest of your function code
Edit to add: i've had issues with prototype and call in older versions of IE, so it really depends on what support you'll need.
I went with this:
function executeScripts() {
if (arguments.length === 0) return;
var args = Array.prototype.slice.call(arguments);
chrome.tabs.executeScript(null, {
file: args.shift()
}, function() {
executeScripts.apply(null, args);
});
}
It's useful when writing Google Chrome Extensions. I wanted to use jQuery in my content script, but then you have to load it first. Turns out out by chaining calls to chrome.tabs.executeScript you can do this:
chrome.browserAction.onClicked.addListener(function(tab) {
executeScripts('jquery-1.4.4.min.js', 'content.js');
});
I want to be able to check whether a given function is empty or not. That is, there is nothing in its body, eg:
function foo() {}
function iAmEmpty(a) {
// yep, empty
}
With some initial playing around, I've got something which I think might be ok, by using toString() and some regexes.
function foo(a, b, c) {}
/^function[^{]+\{\s*\}/m.test(foo.toString()); // true
function bar(a, b, c) { var d; }
/^function[^{]+\{\s*\}/m.test(bar.toString()); // false
I was just wondering if there was a better approach? Are there any problems with the above you can see?
This isn't advisable. There is no standard determining precisely what a function's toString() method should return, so even if you get this working in current browsers, future browsers may justifiably change their implementation and break your code.
Kangax has written briefly about this: http://perfectionkills.com/those-tricky-functions/
Arrow functions...
As I am sure you are aware, javascript supports arrow functions which are really succinct but unfortunately don't work with your neat regex.
I quickly converted your nice regex into its own function which takes a function as an input and returns whether or not it is empty for simplicity later. Just to demonstrate how arrow functions can be widely used, I put it in one:
isEmpty = f => /^function[^{]+\{\s*\}/m.test(f.toString())
Now, we can easily test an empty function:
function eF() {}
which as we would expect with isEmpty(eF) returns true.
And once more with an actual function:
function retOne() {return 1;}
which again as expected with isEmpty(retOne) returns false.
However, the issue I encountered was with arrow functions so to initialize an empty one again, we have a shorter syntax of the original:
eF = () => {}
and the 'stringified'version of that is quite different to the one before:
"() => {}"
so of course in this case the call isEmpty(eF) returns false when we want true. I'm not sure if you require to test if all functions (i.e. including arrow functions) are empty but if you do, your regexwill need modifying...
I am not great at writing them myself, but have attempted a couple and one further thing that you might want to consider is the lenient nature of the arrow functions especially this part of the documentation:
(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression
// equivalent to: (param1, param2, …, paramN) => { return expression; }
// Parentheses are optional when there's only one parameter name:
(singleParam) => { statements }
singleParam => { statements }
which shows how the curly brackets {...} are not always necessary. So this function:
retOne = () => 1
is valid and could make forming a new regex more difficult. One workaround I thought of is to just remove all curly brackets from f.toString() using:
str.replace(/[{}]/g, '').
and then work with a regex test from there.
Hopefully this is something helpful to consider if you want arrow functions to also be able to be tested.
The best thing you can try, to fit the maximum possibilities (as this is pretty hard to achieve), is to add acorn or esprima (works with arrow functions too) libraries and process the JavaScript function. It will tokenize it for you to parse, so you can process it to your likings, checking if there's actually zero code inside, or there's only variable declarations without any calculation nor return, etc...
Is pretty straightforward to implement:
function check(f) {
console.log("");
console.log("Checking", f);
var syntax = esprima.parse(f);
if (syntax.body.length != 1) {
console.log("Weird process");
} else {
function processBody(body) {
if (!body || body.length == 0) {
console.log("Body seems empty. YAY!");
} else {
for (var i = 0, command; command = body[i]; i++) {
if (command.type != "VariableDeclaration") {
console.log("Body has a", command.type, ", so is not empty.");
return;
}
}
console.log("Body has only variable declarations, so is empty! (or not, whatever fit your needs)");
}
}
function process(dec) {
if (dec.type != "FunctionDeclaration") {
console.log("Weird declaration");
} else {
console.log("Function", dec.id.name, "has", dec.params.length, "params");
processBody(dec.body.body);
}
}
process(syntax.body[0]);
}
}
check("function hasReturn(arg1, arg2) {var a = arg1 + arg2;return a;}");
check("function hasArgumentsButNoBody(arg1, arg2) {}");
check("function isCompletelyEmptyWithSpacesAndTabsAndLinefeeds() { \t \t \r\n \r\n }");
check("function hasOnlyVariables() {var a, b = 2; var c = 1 + b;}");
<script src="https://cdnjs.cloudflare.com/ajax/libs/esprima/2.7.3/esprima.min.js"></script>
This will not run the functions, just parse them, so is safe to run with non-secure functions.
I don't see the use for this, but you could make it simpler by anchoring the pattern to the end of the string.
/[^{\s]\s*\}$/.test(String(bar))
Function.prototype.hasBody = function() {
return !/{\s*}$/.test(this.toString());
};
It's simple just check the function contain and then check the contain if it's empty or not .
check this Plunker
here's full working code:
function foo(a, b, c) {}
function bar(a, b, c) {
var d;
}
function isEmpty(value) {
return (value == null || value.length === 0);
}
function check(test) {
var entire = test.toString();
var body = entire.slice(entire.indexOf("{") + 1, entire.lastIndexOf("}"));
return body;
}
console.log(isEmpty(check(foo)), isEmpty(check(bar))); //return true false