John D
John D

Reputation: 341

Advice on JS script argument parameters

I am working through 'Javascript: the definitive guide' by Flannagan. There is a section which explains how Javascript classes can be augmented by adding new methods. The example script shows how a new method 'Times' can be added to the number prototype ( this is how I interpret it).

I am struggling to know what the argument parameters are in the following script, particularly 'context'.

 // Invoke the function f this many times, passing the iteration number
// For example, to print "hello" 3 times:
//     var n = 3;
//     n.times(function(n) { console.log("hello"); });



Number.prototype.times = function(f, context) {
    var n = this.valueOf();
    console.log(n);
    for(var i = 0; i < n; i++) f.call(context, i);
};

    var n = 3;

    n.times(function(n) { console.log("hello"); });

I think the value of f becomes:

function(n) { console.log("hello"); })

I'm not sure what 'context' is?

Any help gratefully received...

Upvotes: 2

Views: 56

Answers (3)

CertainPerformance
CertainPerformance

Reputation: 370879

times accepts a function; the context argument allows you to specify the value this refers to inside that function, if needed.

In your example, the callback passed to times doesn't use this at all, so it's not needed, but imagine if the callback depended on this referring to an object:

const obj = {
  count: 3,
  increment: function() {
    this.count++;
    console.log(this.count);
  }
};
obj.increment();
obj.increment();

The obj.increment function depends on obj.count. For this to work in your times function, you'd need the this to refer to obj, so pass it as the second argument to times, so that obj is passed as the first parameter to .call:

Number.prototype.times = function(f, context) {
    var n = this.valueOf();
    console.log('repeating ' + n + ' times:');
    for(var i = 0; i < n; i++) f.call(context, i);
};
const obj = {
  count: 3,
  increment: function() {
    this.count++;
    console.log(this.count);
  }
};

(3).times(obj.increment, obj);

A custom this isn't necessary if you pass a different function to .times, one which invokes obj.increment itself:

Number.prototype.times = function(f, context) {
    var n = this.valueOf();
    console.log('repeating ' + n + ' times:');
    for(var i = 0; i < n; i++) f.call(context, i);
};
const obj = {
  count: 3,
  increment: function() {
    this.count++;
    console.log(this.count);
  }
};

(3).times(() => obj.increment());

Upvotes: 2

wizzwizz4
wizzwizz4

Reputation: 6426

context is the second value passed to times, and becomes the this special variable value inside the function, due to the f.call(context, i) call (the first value is the this value).

Number.prototype.times = function(f, context) {
    var n = this.valueOf();
    console.log(n);
    for(var i = 0; i < n; i++) f.call(context, i);
};

var n = 3;

n.times(function(n) { console.log(this, n); }, "hello");

The reason that this appears to behave strangely is because this must be an object, so the string is converted into a String object (which is similar to an Array object, hence the appearance of an array).

This is more useful in situations where you're using a prototype function:

Number.prototype.times = function(f, context) {
    var n = this.valueOf();
    console.log(n);
    for(var i = 0; i < n; i++) f.call(context, i);
};

var arr = [1, 2, 3, 4];
(3).times(Array.prototype.reverse, arr);

console.log(arr);

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1074585

I think the value of f becomes:

function(n) { console.log("hello"); })

Right!

I'm not sure what 'context' is?

"Context" is a word people sometimes use (incorrectly, IMHO) to refer to the this value for a function call.

In that call to times:

n.times(function(n) { console.log("hello"); });

...there's no argument for the context parameter being passed to times, so times will get undefined for the value of its context parameter. Then it uses that value in f.call(...). When you use undefined (or null) with Function.prototype.call, in loose mode the function is called with this set to the global object; in strict mode, the function sees this ase undefined or null instead.

So in that example, the callback will be called with either the global object as this, or undefined as this.

It's analogous to the thisArg parameter of Array.prototype.forEach and related.

Upvotes: 1

Related Questions