Prev: A general purpose hash function for javascript
Next: FAQ Topic - My element is named myselect[], how do I access it? (2010-04-09)
From: Thomas Allen on 14 Apr 2010 20:14 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 14 Apr 2010 20:16 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 14 Apr 2010 20:17 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 14 Apr 2010 20:35 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 14 Apr 2010 21:25
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 |