From: Thomas Allen on
On Apr 14, 8:05 pm, Thomas Allen <thomasmal...(a)gmail.com> wrote:
> 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], i, arr) !== false) {
filtered.push(arr[i]);
}
} else if (can.apply) {
if (fn.apply(ctx || this, [arr[i], i, arr]) !== false) {
filtered.push(arr[i]);
}
} else {
if (fn(arr[i], i, arr) !== false) {
filtered.push(arr[i]);
}
}
}
return filtered;
};


Thomas
From: Thomas Allen on
On Apr 14, 8:14 pm, Thomas Allen <thomasmal...(a)gmail.com> wrote:
> 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
From: Thomas 'PointedEars' Lahn on
Thomas Allen wrote:

> Thomas 'PointedEars' Lahn wrote:
>> Thomas Allen wrote:
>> > 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[i] != 'undefined') {
>> > if (fn.call(context, a[i], i, a)) { r[c++] = a[i]; }
>> > }
>> > }
>> > 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
--
Prototype.js was written by people who don't know javascript for people
who don't know javascript. People who don't know javascript are not
the best source of advice on designing systems that use javascript.
-- Richard Cornford, cljs, <f806at$ail$1$8300dec7(a)news.demon.co.uk>
From: Thomas 'PointedEars' Lahn on
Thomas Allen wrote:

> Thomas Allen wrote:
>> 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], i, arr) !== false) {
> filtered.push(arr[i]);
> }
> } else if (can.apply) {
> if (fn.apply(ctx || this, [arr[i], i, arr]) !== false) {
> filtered.push(arr[i]);
> }
> } else {
> if (fn(arr[i], i, arr) !== false) {
> filtered.push(arr[i]);
> }
> }
> }
> 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
--
Anyone who slaps a 'this page is best viewed with Browser X' label on
a Web page appears to be yearning for the bad old days, before the Web,
when you had very little chance of reading a document written on another
computer, another word processor, or another network. -- Tim Berners-Lee
From: Thomas Allen on
On Apr 14, 8:35 pm, Thomas 'PointedEars' Lahn <PointedE...(a)web.de>
wrote:
> 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], i, arr) !== false) {
filtered.push(arr[i]);
}
}
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], i, arr]) !== false)
{
filtered.push(arr[i]);
}
}
return filtered;
};
} else {
filter = function(arr, fn, ctx) {
var filtered = [];
for (var i = 0, len = arr.length; i < len; i++) {
if (fn(arr[i], i, arr) !== false) {
filtered.push(arr[i]);
}
}
return filtered;
};

}

API.filter = filter;
})();

Thomas