Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top