A javascript issue for closure - javascript

Given the following code:
String.method('deentityify', function () {
var entity = {
quot: '"',
lt: '<',
gt: '>'
};
return function () {
return this.replace(/&([^&;]);/g,
function (a, b) {
var r = entity[b];
return typeof r === 'string' ? r : a;
}
);
};
}());
document.write('deentityify: ' + '<">'.deentityify() + '<br>');
Regarding the
function (a, b) {
var r = entity[b];
return typeof r === 'string' ? r : a;
}
How come the anonymous function get the parameter value a, b? Of course I have tried, the output is right. Can anyone can help me?

The function is actually an argument to the 'replace' call. The regex matches are passed into the function as parameters.
To write the code another way, it would look:
function match(a, b) {
var r = entity[b];
return typeof r === 'string' ? r : a;
}
var result = this.replace(/&([^&;]);/g, match)
The names of the parameters (a & b) are inconsequential and could be anything you like.
The first parameter will be the matched value and the subsequent parameters will be the values of the matched groups. So for clarity the function could be written as:
function matchFn(match, group1, group2, group3) {
var r = entity[group1];
return typeof r === 'string' ? r : match;
}
To quote MDN
A function to be invoked to create the new substring (to put
in place of the substring received from parameter #1). The arguments
supplied to this function are described in the "Specifying a function
as a parameter" section below.

You can handover a function as second parameter to replace(). This function acts as some kind of callback function and receives it's parameters from the calling replace in a fixed order like stated in the Docs.
a and b are just arbitrary names - a is the matched substring and b the capture group ([^&;]).

Related

Javascript : optional parameters in function [duplicate]

This question already has answers here:
Is there a better way to do optional function parameters in JavaScript? [duplicate]
(28 answers)
Closed 6 years ago.
Let's say I have this :
function concatenate(a, b, c) {
// Concatenate a, b, and c
}
How do I handle those calls ?
x = concatenate(a)
x = concatenate(a, b)
x = concatenate(a, c)
How can I make my function aware of the parameter I gave to it ?
Any unfilled argument will be undefined.
concatenate(a, c) is equivalent to concatenate(a, b). You cannot pass the third parameter without passing the second; but you can pass undefined (or null, I suppose) explicitly: concatenate(a, undefined, c).
In the function, you can check for undefined and replace with a default value.
Alternately, you can use an object argument to imitate keyword arguments: concatenate({a: a, c: c}).
Just use arguments array-like object:
function concatenate() {
var result = '';
for (var i = 0; i < arguments.length; i++) {
result += arguments[i];
}
return result;
}
from ES6/ES2015 on you can do similar as in php:
function concatenate(a, b = false, c = false) {
// Concatenate a, b, and c
}
for the older versions, you can do:
function concatenate(a, b, c) {
if( typeof b !== 'undefined' && typeof c !== 'undefined') {
// code when a, b and c are defined
} else if ( typeof b !== 'undefined' && typeof c == 'undefined') {
// code when a and b are defined
} else {
// code
}
}
I am sure there is a better approach too, but should work.
Use the ES6 rest parameters syntax to get an array of arguments. Then simply join its items to retrieve the concatenated string.
concatenate(a);
concatenate(a, b);
concatenate(a, c);
function concatenate(...args){
// for old browsers
// `...args` is an equivalent of `[].slice.call(arguments);`
return args.join('');
}

individual characters treated as string in javascript arguments

I need to write a function that retrieves the last parameter passed into the function. If it's just arguments, it should return the last element. If it's a string, it should return the last character. Arrays should return the last element of the array. My code works for arrays, strings and arguments as long as they are not all strings.
function last(list){
var arr;
if(list instanceof Array){
return list[list.length-1];
}
if(typeof list === 'string' || list instanceof String){
arr = list.split("");
return arr[arr.length-1];
}
return arguments[arguments.length-1];
}
This works for almost every case, but I have a problem when the input is a bunch of string arguments.
Test.assertEquals(last('a','b','c','z'), 'z');
returns 'a'
Why are disjoint strings evaluating to true when testing if the arguments are arrays or strings and how can I universaly access the last value of arbitrary parameters?
Something like this should do that
function last(){
var a = arguments;
return (a.length > 1 ? [].slice.call(a) :
typeof a[0] === 'string' ? a[0].split('') : a[0]).pop();
}
FIDDLE
Change your function call to Test.assertEquals(last(['a','b','c','z']), 'z');
Whereas before you were passing four different arguments to last(), this passes a single argument, a list containing four characters.
Change your last function like this:
function last(list){
var arr;
if(list instanceof Array){
return list[list.length-1];
}
if((typeof list === 'string' || list instanceof String) && (arguments.length == 1)){
arr = list.split("");
return arr[arr.length-1];
}
return arguments[arguments.length-1];
};

Set undefined parameters via arguments

I'm trying to write a function that corrects the arguments of a function based on previously specified optional parameters. I've come to a problem though. It seems that I can't set variables via the arguments array unless they have been defined in any way before. The code below shows an example of the problem I'm facing.
function foo(a, b, c) {
arguments[0] = "lorem";
arguments[1] = "ipsum";
arguments[2] = "dolor";
console.log([a, b, c]);
}
foo(null); // ["lorem", undefined, undefined]
foo(null, null); // ["lorem", "ipsum", undefined]
foo(null, null, null); // ["lorem", "ipsum", "dolor"]
When logging arguments the result is always ["lorem", "ipsum", "dolor"] though.
Is there any way to solve this problem ?
I can't set a, b and c directly because a function called in foo wouldn't have access to these names.
My goal would look like something like this:
function foo(a, b, c) {
var rules = [];
// Rule for optional parameter 1 (b)
// If it equals true the value of b is shifted to the next parameter (c)
rules[1] = function(val) { return val !== "ipsum"; };
optionalize(rules);
console.log([a, b, c]);
}
foo("lorem", "dolor"); // ["lorem", undefined, "dolor"];
The arguments array isn't really an array but an "array-like" object. You can't change its length.
What you try to do is usually done using
a = a || "lorem";
or, if you don't want to replace any "falsy" argument, using
if (typeof a === "undefined") a = "lorem";
It is a bit peculiar, but is this what you had in mind?
function optionalize(fn, options) {
var i, key, rule;
for(i = 0; i < options.length; i += 1) {
key = fn.placement[i];
rule = fn.ruleset[key];
// If the rule exists and returns true, shift value to the right.
if(rule && rule(options[i])) {
options[i+1] = options[i];
options[i] = undefined;
}
// Assign the numeric index to an alphabet key.
// Depending on your use case, you may want to substitute a plain Object here and return that, instead of adding non-numeric properties to an Array.
options[key] = options[i];
}
}
// Test function
function foo(a, opts) {
opts = opts || [];
optionalize(foo, opts);
console.log([a, opts.b, opts.c]);
}
// Optional argument names, in the order that they would be received.
foo.placement = ['b', 'c'];
// Optionalize rules
foo.ruleset = {
b: function (val) { return val !== "ipsum"; }
};
// Demonstration
foo('lorem');
foo('lorem', []);
foo('lorem', ['dolor']);
foo('lorem', ['ipsum', 'dolor']);
As dystroy's answer has already indicated, the arguments variable isn't a real Array, and changing it may not be a good idea. I have provided a solution which does not rely on arguments and fulfills the criteria as far as could be possible using simple JavaScript.
The function foo is specified with a required argument a, followed by an Array of optional arguments named opts. An optionalize specification is set onto foo, through the placement and ruleset properties. The optionalize function takes this information and transforms the array's indices into usable name keys, applying the rules as necessary.
I'm not sure what you're trying to do but something like arguments[0] = a ? a : "lorem" and so on ?
You can convert arguments to an array
function foo () {
var arguments = Array.prototype.slice.call(arguments);
arguments.push(4);
console.log(arguments);
}
foo(1,2,3);
​

Understanding d3.js source: stuck at function.call() and "=+"

In the source code of d3.layout.force, line 158, there is this code
force.charge = function(x) {
if (!arguments.length) return charge;
charge = typeof x === "function" ? x : +x;
return force;
};
Now, if you go to line 225, you will see
charges = [];
if (typeof charge === "function") {
for (i = 0; i < n; ++i) {
charges[i] = +charge.call(this, nodes[i], i);
}
} else {
for (i = 0; i < n; ++i) {
charges[i] = charge;
}
}
What I did not understand here is the line
charges[i] = +charge.call(this, nodes[i], i);
I am new to JavaScript and can not understand what's going on here.
As far as I understood charge takes only 1 argument (x). Here "this" is passed to give the context of current object but what about the other two? Which one of "nodes[i]" and "i" is taken as "x" ?
Again what is "= +" doing here?
Check out the MDN listings for call, apply and bind.
It's a tough concept to wrap your head around but what's happening in call and apply is that you're choosing to execute a function in a different "context."
I say "context" with quotes as "execution context" has an exact meaning in JS and this isn't it. I don't have a great word for it but what's happening here is that you're making swapping out the this object when executing the function.
This might help:
var obj = { foo: "bar" };
method.call( obj, "arg" );
function method( arg ) {
console.log( this.foo ); #bar
console.log( arg ); #"arg"
}
I think you'll find your answer here.
Basically, it's converting this:
function(){ return +new Date; }
into this:
function(){ return Number(new Date); }
Essentially, it is converting the argument into a number, and adding it to the previous value.
More reading about this here
You have to follow charge more carefully. It is variable defined in line 11:
charge = -30,
The function force.charge which you quoted is for setting the charge, it is not the function referred to in +charge.call(this, nodes[i], i);. Have a look at the second line of force.charge:
charge = typeof x === "function" ? x : +x;
x can be a function (callback) you pass, to dynamically calculate the charge. The current node (nodes[i]) and the index of the node (i) will be passed to this callback, so that you can calculate the charge dynamically based on these values:
force.charge(function(node, index) {
return index * 2;
});
x (and therefore charge) can also be a number or numerical string. That's why it is tested beforehand whether charge is a function or not:
if (typeof charge === "function") {
// function so we call it and pass the current node and index
} else {
// static value, the same for each node
}
Apert from that, you can always pass any number of arguments to a function, no matter how many parameters it has defined. For example:
function foo() {
alert([].join.call(null, arguments));
}
foo('a', 'b');
will alert a,b.
To answer your questions: The arguments passed to .call() [MDN] or .apply() [MDN] are passed in the same order to the function. So if I have a function function foo(a, b, c) then foo.call(null, x, y) would pass x as a and y as b (c would be undefined).
The + operator is the unary plus operator [MDN], which simply converts the operand into a number.

Javascript, add string if true

I need to modify this function:
function checkPermissions(fid, obj) {
$("#permissions_"+fid+"_canview").attr("checked", obj.checked);
}
if the param "n" is "true" then instead of #permissions it'll output #permissionsSet.
is that possible?
OP clarified the question to be change this whether or not a 3rd parameter was provided. Here you go.
function checkPermissions(fid, obj, n) {
var id = n !== undefined ? '#permissionsSet' : '#permissions';
$(id + fid + "_canview").attr("checked", obj.checked);
}
Note: This function can be freely used with passing 2 or 3 parameters (or really any number).
checkPermissions(1, this); // Ok n === undefined
checkPermissions(1, this, true); // Ok n === true
checkPermissions(1); // Okish n and obj === undefined
function checkPermissions(fid, obj, n) {
$("#permissions" + ((n) ? "Set" : "") + "_"+fid+"_canview").attr("checked", obj.checked);
}

Categories

Resources