My (Your) Library: canCall

T

Thomas Allen

What is the purpose of canCall in My Library (hereafter and forever ML
so as not to imply that this is mine)? Specifically, I am thinking of
the following code:

var canCall = !!Function.prototype.call;
// ...
if (canCall) {
forEachProperty = function(o, fn, context) {
//...

Why does the presence of Function.prototype.call matter, when
Function.prototype.apply can do the same thing? I ask because if
there's a difference, I'd be very interested as I equate the two in
behavior, apart from their different call styles (making me think of
call as more limited).

Thomas
 
T

Thomas Allen

Related, and equally perplexing to me, is the following code:

Updater.prototype.dispatch = function(sEvent, args) {
Requester.prototype.dispatch.apply(this, sEvent, args);

Is the first argument to that apply call intended, when apply (to the
best of my knowledge) only accepts two arguments?

Thomas
 
T

Thomas 'PointedEars' Lahn

Thomas said:
What is the purpose of canCall in My Library (hereafter and forever ML
so as not to imply that this is mine)? Specifically, I am thinking of
the following code:

var canCall = !!Function.prototype.call;
// ...
if (canCall) {
forEachProperty = function(o, fn, context) {
//...

Why does the presence of Function.prototype.call matter, when
Function.prototype.apply can do the same thing

The methods cannot really do the same thing (else one method would be
superfluous). The Matrix so far suggests that both methods have the same
compatibility level.
I ask because if there's a difference, I'd be very interested as I equate
the two in behavior, apart from their different call styles

However, I would not feature-test Function.prototype.call() and then call
Function.prototype.apply(), and vice-versa. The rule of thumb for feature-
testing is: Always test exactly what you are using later. (Perhaps there
is also a canApply in My Library?)
(making me think of call as more limited).

It is not as flexible as apply(), but it is probably more efficient to use
if you know the (number of) arguments to be passed to the method so
invoked.


PointedEars
 
T

Thomas 'PointedEars' Lahn

Thomas said:
Related, and equally perplexing to me, is the following code:

Updater.prototype.dispatch = function(sEvent, args) {
Requester.prototype.dispatch.apply(this, sEvent, args);

Is the first argument to that apply call intended, when apply (to the
best of my knowledge) only accepts two arguments?

Looks like a refactoring bug and should probably be either

Updater.prototype.dispatch = function(sEvent, args) {
Requester.prototype.dispatch.call(this, sEvent, args);
// ...
};

or

Updater.prototype.dispatch = function(sEvent, args) {
Requester.prototype.dispatch.apply(this, arguments);
// ...
};

or

Updater.prototype.dispatch = function(sEvent, args) {
Requester.prototype.dispatch.apply(this, args);
// ...
};

Please learn to quote properly.

<http://jibbering.com/faq/#posting> pp.


PointedEars
 
T

Thomas Allen

However, I would not feature-test Function.prototype.call() and then call
Function.prototype.apply(), and vice-versa.  The rule of thumb for feature-
testing is: Always test exactly what you are using later.  (Perhaps there
is also a canApply in My Library?)

Certainly Function.prototype.apply can do anything that
Function.prototype.call can, so the thrust of my inquiry is: Does the
absence of the former really make the entire following code
unnecessary or useless? Or did the author assume that any browser
which lacks call would also lack apply?

if (canCall) {
filter = function(a, fn, context) {
var i = a.length, r = [], c = 0;
context = context || a;
// Didn't want to use in operator and for in loop does not
preserve order
while (i--) {
if (typeof a != 'undefined') {
if (fn.call(context, a, i, a)) { r[c++] = a; }
}
}
return r.reverse();
};
}

There is no variable "canApply", and I only could find one obvious
point at which it is checked:

if (createXmlHttpRequest && Function.prototype.apply &&
isHostMethod(global, 'setTimeout')) {
API.ajax = (function() {
// ...
}
}

Thomas
 
G

Garrett Smith

Thomas said:
On Apr 14, 6:29 pm, Thomas 'PointedEars' Lahn <[email protected]>
wrote: [...]

// Didn't want to use in operator and for in loop does not
preserve order
while (i--) {

That looks like reverse loop to populate an array followed by a call to
reverse(). He certainly goes to a lot of trouble slow things down.

if (typeof a != 'undefined') {
if (fn.call(context, a, i, a)) { r[c++] = a; }
}
}
return r.reverse();

[...]
 
T

Thomas 'PointedEars' Lahn

Thomas said:
Certainly Function.prototype.apply can do anything that
Function.prototype.call can, so the thrust of my inquiry is: Does the
absence of the former really make the entire following code
unnecessary or useless? Or did the author assume that any browser
which lacks call would also lack apply?

if (canCall) {
filter = function(a, fn, context) {
var i = a.length, r = [], c = 0;
context = context || a;
// Didn't want to use in operator and for in loop does not
preserve order
while (i--) {
if (typeof a != 'undefined') {
if (fn.call(context, a, i, a)) { r[c++] = a; }
}
}
return r.reverse();
};
}


I do not see apply() being called here, so your question does not make
sense to me.
There is no variable "canApply", and I only could find one obvious
point at which it is checked:

if (createXmlHttpRequest && Function.prototype.apply &&
isHostMethod(global, 'setTimeout')) {
API.ajax = (function() {
// ...
}
}

One would probably want to rewrite this like

if (createXmlHttpRequest
&& isNativeMethod(Function, "prototype", "apply")
&& isHostMethod(global, 'setTimeout'))
{
API.ajax = (function() {
// ...
};
}

The `createXmlHttpRequest' test is questionable as it would break (not only
fail) if there was no such property on an object in the scope chain
(ReferenceError). However, the identifier might be declared an initialized
variable or function and nulled on condition afterwards, and then this
would be OK.


PointedEars
 
T

Thomas Allen

Thomas said:
Certainly Function.prototype.apply can do anything that
Function.prototype.call can, so the thrust of my inquiry is: Does the
absence of the former really make the entire following code
unnecessary or useless? Or did the author assume that any browser
which lacks call would also lack apply?
    if (canCall) {
      filter = function(a, fn, context) {
        var i = a.length, r = [], c = 0;
        context = context || a;
        // Didn't want to use in operator and for in loop does not
preserve order
        while (i--) {
          if (typeof a != 'undefined') {
            if (fn.call(context, a, i, a)) { r[c++] = a; }
          }
        }
        return r.reverse();
      };
    }


I do not see apply() being called here, so your question does not make
sense to me.


apply can do everything that call can, so it doesn't make sense to
abandon that block of code if only call is undefined.

Thomas
 
T

Thomas Allen

apply can do everything that call can, so it doesn't make sense to
abandon that block of code if only call is undefined.

Thomas

For instance, this makes more sense to me (the feature checks could
certainly be performed earlier so that the function is defined with
the compatible function calling mechanism, saving a few ticks):

var can = {
call: Function.prototype.call !== 'undefined',
apply: Function.prototype.call !== 'undefined'
};

var filter = function(arr, fn, ctx) {
var filtered = [];
for (var i = 0, len = arr.length; i < len; i++) {
if (can.call) {
if (fn.call(ctx || this, arr, i, arr) !== false) {
filtered.push(arr);
}
} else if (can.apply) {
if (fn.apply(ctx || this, [arr, i, arr]) !== false) {
filtered.push(arr);
}
} else {
if (fn(arr, i, arr) !== false) {
filtered.push(arr);
}
}
}
return filtered;
};


Thomas
 
T

Thomas Allen

var can = {
    call: Function.prototype.call !== 'undefined',
    apply: Function.prototype.call !== 'undefined'

};

Sorry, I meant:

var can = {
call: typeof Function.prototype.call !== 'undefined',
apply: typeof Function.prototype.apply !== 'undefined'
};

Thomas
 
T

Thomas 'PointedEars' Lahn

Thomas said:
Thomas said:
Thomas said:
Certainly Function.prototype.apply can do anything that
Function.prototype.call can, so the thrust of my inquiry is: Does the
absence of the former really make the entire following code
unnecessary or useless? Or did the author assume that any browser
which lacks call would also lack apply?
if (canCall) {
filter = function(a, fn, context) {
var i = a.length, r = [], c = 0;
context = context || a;
// Didn't want to use in operator and for in loop does not
preserve order
while (i--) {
if (typeof a != 'undefined') {
if (fn.call(context, a, i, a)) { r[c++] = a; }
}
}
return r.reverse();
};
}


I do not see apply() being called here, so your question does not make
sense to me.


apply can do everything that call can, so it doesn't make sense to
abandon that block of code if only call is undefined.


The purpose of the test is to not execute the block if the method is
unavailable, as the call() call in the function defined in the block would
throw a TypeError exception otherwise. I do not see anything inherently
wrong with that.


PointedEars
 
T

Thomas 'PointedEars' Lahn

Thomas said:
Thomas said:
apply can do everything that call can, so it doesn't make sense to
abandon that block of code if only call is undefined.

For instance, this makes more sense to me (the feature checks could
certainly be performed earlier so that the function is defined with
the compatible function calling mechanism, saving a few ticks):

var can = {
call: Function.prototype.call !== 'undefined',
apply: Function.prototype.call !== 'undefined'
};

var filter = function(arr, fn, ctx) {
var filtered = [];
for (var i = 0, len = arr.length; i < len; i++) {
if (can.call) {
if (fn.call(ctx || this, arr, i, arr) !== false) {
filtered.push(arr);
}
} else if (can.apply) {
if (fn.apply(ctx || this, [arr, i, arr]) !== false) {
filtered.push(arr);
}
} else {
if (fn(arr, i, arr) !== false) {
filtered.push(arr);
}
}
}
return filtered;
};


It is easy to see that this would not be equivalent. You would be checking
the availability of the methods in each iteration. While the original code
defined a function depending on a one-time check of the methods'
availability, which is a lot more efficient.

However, I presume there is no apply() branch in the original code because
the author assumes that it would be unlikely that
Function.prototype.apply() would be supported in an implementation while
Function.prototype.call() would not, as both methods have been introduced
with JavaScript 1.3 and JScript 5.5. Incidentally, JavaScript 1.3 is the
minimum JavaScript requirement for function expressions as well (but they
are available in JScript since 3.0).

See also the aforementioned ECMAScript Support Matrix:

<http://PointedEars.de/es-matrix>


PointedEars
 
T

Thomas Allen

It is easy to see that this would not be equivalent.  You would be checking
the availability of the methods in each iteration.  While the original code
defined a function depending on a one-time check of the methods'
availability, which is a lot more efficient.

I noted that, and the more efficient approach follows. I believe the
most reasonable, efficient thing to do is to use
Array.prototype.filter if it exists, so I check that first:

var API = {};

(function() {
var can = {
call: typeof Function.prototype.call !== 'undefined',
apply: Function.prototype.call !== 'undefined',
nativeFilter: Array.prototype.filter !== 'undefined'
};

var filter;

if (can.nativeFilter) {
filter = function(arr, fn, ctx) {
return Array.prototype.filter.call(arr, fn, ctx);
};
} else if (can.call) {
filter = function(arr, fn, ctx) {
var filtered = [];
for (var i = 0, len = arr.length; i < len; i++) {
if (fn.call(ctx || this, arr, i, arr) !== false) {
filtered.push(arr);
}
}
return filtered;
};
} else if (can.apply) {
filter = function(arr, fn, ctx) {
var filtered = [];
for (var i = 0, len = arr.length; i < len; i++) {
if (fn.apply(ctx || this, [arr, i, arr]) !== false)
{
filtered.push(arr);
}
}
return filtered;
};
} else {
filter = function(arr, fn, ctx) {
var filtered = [];
for (var i = 0, len = arr.length; i < len; i++) {
if (fn(arr, i, arr) !== false) {
filtered.push(arr);
}
}
return filtered;
};

}

API.filter = filter;
})();

Thomas
 
T

Thomas Allen

use Array.prototype.filter if it exists,

And ML does that:

if (Array.prototype.filter) {
filter = function(a, fn, context) { return a.filter(fn,
context); };
}
else {
// Note: Array.prototype.reverse is not tested as it is from JS
1.1
if (canCall) {
// ...

Thomas
 
T

Thomas 'PointedEars' Lahn

Thomas said:
I noted that, and the more efficient approach follows

Please read my whole posting.
I believe the most reasonable, efficient thing to do is to use
Array.prototype.filter if it exists, [...]

Certainly yes, but ...
[...]
var can = {
call: typeof Function.prototype.call !== 'undefined',
apply: Function.prototype.call !== 'undefined',
nativeFilter: Array.prototype.filter !== 'undefined'
};

var filter;

if (can.nativeFilter) {

.... that test is insufficient for two reasons:

1. You need to test Function.prototype.call(), too, if you
call Array.prototype.filter() like below.

2. You need to make sure that Array.prototype.filter refers
to a method before you call it.

You *really* want to read the archives.
filter = function(arr, fn, ctx) {
return Array.prototype.filter.call(arr, fn, ctx);
};
} [...]


PointedEars
 
G

Garrett Smith

Thomas said:
Thomas said:
I noted that, and the more efficient approach follows

Please read my whole posting.
I believe the most reasonable, efficient thing to do is to use
Array.prototype.filter if it exists, [...]

Certainly yes, but ...
[...]
var can = {
call: typeof Function.prototype.call !== 'undefined',
apply: Function.prototype.call !== 'undefined',
nativeFilter: Array.prototype.filter !== 'undefined'
};

var filter;

if (can.nativeFilter) {

... that test is insufficient for two reasons:

1. You need to test Function.prototype.call(), too, if you
call Array.prototype.filter() like below.

Where the more recent `Array.prototype.filter` is implemented, one can
assume that `Function.prototype.call` is available with fair safety.
2. You need to make sure that Array.prototype.filter refers
to a method before you call it.

If `Array.prototype.filter` is present and is not callable object then
there is something very wrong with the implementation. Simple existence
inference is sufficient.

nativeFilter : !!Array.prototype.filter
 
T

Thomas 'PointedEars' Lahn

Garrett said:
Thomas said:
Thomas said:
[...]
var can = {
call: typeof Function.prototype.call !== 'undefined',
apply: Function.prototype.call !== 'undefined',
nativeFilter: Array.prototype.filter !== 'undefined'
};

var filter;

if (can.nativeFilter) {

... that test is insufficient for two reasons:

1. You need to test Function.prototype.call(), too, if you
call Array.prototype.filter() like below.

Where the more recent `Array.prototype.filter` is implemented, one can
assume that `Function.prototype.call` is available with fair safety.

That is, if nobody has augmented Array.prototype ...
If `Array.prototype.filter` is present and is not callable object then
there is something very wrong with the implementation.

There are no guarantees.
Simple existence inference is sufficient.

Never for method calls.
nativeFilter : !!Array.prototype.filter

Error-prone.


PointedEars
 
D

David Mark

Thomas said:
Looks like a refactoring bug and should probably be either

Definitely a typo (missing brackets)
Updater.prototype.dispatch = function(sEvent, args) {
Requester.prototype.dispatch.call(this, sEvent, args);
// ...
};

or

Updater.prototype.dispatch = function(sEvent, args) {
Requester.prototype.dispatch.apply(this, arguments);
// ...
};

or

Updater.prototype.dispatch = function(sEvent, args) {
Requester.prototype.dispatch.apply(this, args);
// ...
};

Turns out it was none of those. The last two arguments were supposed to
be one array.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,079
Messages
2,570,574
Members
47,205
Latest member
ElwoodDurh

Latest Threads

Top