How can I prevent enumeration for polyfills in JavaScript? - javascript

I have been adding an Array.indexOf() polyfill to the main JavaScript file of our project. I took it from devdocs.io:
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement , fromIndex) {
var i,
pivot = (fromIndex) ? fromIndex : 0,
length;
if (!this) {
throw new TypeError();
}
length = this.length;
if (length === 0 || pivot >= length) {
return -1;
}
if (pivot < 0) {
pivot = length - Math.abs(pivot);
}
for (i = pivot; i < length; i++) {
if (this[i] === searchElement) {
return i;
}
}
return -1;
};
}
I need this because we still have to support IE 8, but it seems that in IE 8, the indexOf() function is added enumerable. That means, it appears when iterating over arrays using for..in loops, like this:
var a = [];
a[0]=123;
a[1]=456;
for(var value in a) {
alert(value); // this even alerts "indexOf", if the polyfill above is loaded, and this is a big problem
}
Is it possible to make the polyfill "unenumerable", so that I am able to use Array.indexOf() in IE 8, but it does not appear in for..in loops?

Generally speaking, you can add a property as non-enumerable using Object.defineProperty:
Object.defineProperty(Array.prototytpe, 'indexOf', {
enumerable : false,
value : function(){ /* my polyfill code */}
});
But, as you've probably guessed, there is no support for it in IE8, so you're stuck with #tkone's solution of filtering the for..in with hasOwnProperty.
Another pseudo-solution is to create a function you pass the array parameter to:
function indexOf(array, searchElement, fromIndex) {
if (Array.prototype.indexOf) return array.indexOf(searchElement, fromIndex);
/* my polyfill code with array instead of this*/
}
and simply use that instead of the native indexOf:
[1,2,3].indexOf(2); // change to:
indexOf([1,2,3], 2);
You could namespace your function as well so you remember to change it back to standards when you no longer bother with IE8:
IhateIE8.indexOf = ...

You should be using hasOwnProperty when you enumerate over the members of an object.
var a = [];
a[0] = 123;
a[1] = 456;
for(var value in a){
if(a.hasOwnProperty(value)){
alert(value);
}
}
But why aren't you just doing a simple for loop (or polyfilling something like forEach)

Is it possible to make the polyfill "unenumerable", so that I am able to use Array.indexOf() in IE 8, but it does not appear in for..in loops?
Officially, yes (see defineProperty and enumerable), but specifically for IE8 and lower, not really.
I don't think it's a good idea though (see the comments).

Related

Is an array.forEach with a splice the best way to remove an entry from an array of unique Ids?

I have an array of objects. Each object has a unique userTestrId. Here is the code that I am using when I want to delete one of the objects. Is this the most efficient way I can perform the delete? What I am concerned with is once a row has been deleted the code will still go all the way through the array even though there is no chance of another entry:
var id = 99;
self.tests.forEach(function (elem, index) {
if (elem['userTestId'] === id)
self.tests.splice(index, 1);
});
}
var id = 99;
self.tests.some(function (elem, index) {
if (elem['userTestId'] === id)
self.tests.splice(index, 1);
return true;
});
return false;
}
Could utilise Array.some? Stops looping once you return TRUE from a callback.
This is an alternative to #benhowdle89's answer.
Use Array.prototype.every
The .every method is used to iterate over an array and check whether each and every element passes a test or not. If the callback returns false for any single element, the loop breaks.
Take the following example:
var odds = [3, 5, 7, 9, 11, 12, 17, 19];
//an array with all odd numbers except one
var checkEven = function (n, i, arr) {
console.log ("Checking number ", n);
if (n%2===0) {
arr.splice(i, 1);
return false;
}
return true;
}
console.log(odds.every(checkEven), odds);
If you run the above and look at the console, the loop executed till number 12 only, where it spliced, and stopped.
You can employ similar logic in your code very easily :)
var id = 99;
self.tests.some(function (elem, index) {
if (elem['userTestId'] === id)
self.tests.splice(index, 1);
return true;
});
return false;
}
Polyfill :
some was added to the ECMA-262 standard in the 5th edition; as such it may not be present in all implementations of the standard. You can work around this by inserting the following code at the beginning of your scripts, allowing use of some in implementations which do not natively support it.
// Production steps of ECMA-262, Edition 5, 15.4.4.17
// Reference: http://es5.github.io/#x15.4.4.17
if (!Array.prototype.some) {
Array.prototype.some = function(fun /*, thisArg*/) {
'use strict';
if (this == null) {
throw new TypeError('Array.prototype.some called on null or undefined');
}
if (typeof fun !== 'function') {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
for (var i = 0; i < len; i++) {
if (i in t && fun.call(thisArg, t[i], i, t)) {
return true;
}
}
return false;
};
}
see in detail
While your concern is technically correct, it's unlikely to be an actual problem(Javascript is fast, this is a microoptimization).
What you should do is focus on using the appropriate interface, so your code could be easy to read and understand. .forEach() does not tell you what you want to do, unless you really do want to do something with each element of the array.
Lodash has the .remove() function, which removes all elements matching a predicate. Unfortunately, I couldn't find the exact specific function you wanted in JS's standard library or in lodash, so you would have to write your own wrapper:
var id = 99
removeFirst(tests, function (elem) { return elem.userTestId === id })
function removeFirst(array, callback) {
var index = array.findIndex(callback)
array.splice(index, 1)
}
Having noted that, you should avoid using an array at all - splicing is way more expensive than looping the whole array to begin with! Instead, since you have a unique identifier, you could use a map:
var map = {}
tests.forEach(function mapper(elem) {
map[elem.userTestId] = elem
})
Now, your removal function is simply delete map[id].

What is the equivalent of Python any() and all() functions in JavaScript?

Python has built in functions any() and all(), which are applied on a list (array in JavaScript) as following-
any(): Return True if any element of the iterable is true. If the iterable is empty, return False.
all(): Return True if all elements of the iterable are true (or if the iterable is empty).
We can create our customized functions for above, but please let me know if there any equivalent built-in functions available in JavaScript.
The Python documentation gives you pure-python equivalents for both functions; they are trivial to translate to JavaScript:
function any(iterable) {
for (var index = 0; index < iterable.length; index++) {
if (iterable[index]) return true;
}
return false;
}
and
function all(iterable) {
for (var index = 0; index < iterable.length; index++) {
if (!iterable[index]) return false;
}
return true;
}
Recent browser versions (implementing ECMAScript 5.1, Firefox 1.5+, Chrome, Edge 12+ and IE 9) have native support in the form of Array.some and Array.every; these take a callback that determines if something is 'true' or not:
some_array.some((elem) => !!elem );
some_array.every((elem) => !!elem );
The Mozilla documentation I linked to has polyfills included to recreate these two methods in other JS implementations.
You can use lodash.
lodash.every is equivalent to all
lodash.some is equivalent to any
Build-in function some is equivalent to any I suppose.
const array = [1, 2, 3, 4, 5];
const even = function(element) {
// checks whether an element is even
return element % 2 === 0;
};
console.log(array.some(even));
// expected output: true
You can read more in the docs

does javascript have an exists() or contains() function for an array

or do you just have to do a loop and check each element ?
Mozilla JS implementations and other modern JS engines have adopted an Array.prototype.indexOf method.
[1].indexOf(1) // 0
if it doesn't contain it, it returns -1.
IE of course and possibly other browsers do not have it, the official code for it:
if (!Array.prototype.indexOf)
{
Array.prototype.indexOf = function(elt /*, from*/)
{
var len = this.length >>> 0;
var from = Number(arguments[1]) || 0;
from = (from < 0)
? Math.ceil(from)
: Math.floor(from);
if (from < 0)
from += len;
for (; from < len; from++)
{
if (from in this &&
this[from] === elt)
return from;
}
return -1;
};
}
If you're using jQuery: jQuery.inArray( value, array )
Update: Pointed URL to new jQuery API
You can look at Javascript 1.6 for some functions.
https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Working_with_Arrays#Introduced_in_JavaScript_1.6
If you just want to know if it is in there, you could use indexOf for example, which would meet your needs.
UPDATE:
If you go to this page, http://www.hunlock.com/blogs/Mastering_Javascript_Arrays, you can find a function to use on IE and any other browser that doesn't have a built in function that you want to use.
Here's one way to have your own indexOf method. This version leverages the Array.prototype.indexOf method if it exists in the environment; otherwise, it uses its own implementation.
(This code has been tested, but I don't guarantee its correctness for all cases.)
// If Array.prototype.indexOf exists, then indexOf will contain a closure that simply
// calls Array.prototype.indexOf. Otherwise, indexOf will contain a closure that
// *implements* the indexOf function.
//
// The net result of using two different closures is that we only have to
// test for the existence of Array.prototype.indexOf once, when the script
// is loaded, instead of every time indexOf is called.
var indexOf =
(Array.prototype.indexOf ?
(function(array, searchElement, fromIndex) {
return array.indexOf(searchElement, fromIndex);
})
:
(function(array, searchElement, fromIndex)
{
fromIndex = Math.max(fromIndex || 0, 0);
var i = -1, len = array.length;
while (++i < len) {
if (array[i] === searchElement) {
return i;
}
}
return -1;
})
);

How do I add syntactic sugar in my Javascript library?

Right now the library can translate this operation
Select * from List where name = k% order by desc
to
List.filter(function(x) { return x.first_char() == 'k' }).sort().reverse());
Whats the best hack to remove the () so that the developer can write statements like:
List.filter(fn(x) { return x.first_char == 'k' }).sort.reverse;
Naive approach:
maxfn = function() {this[0]..}; Array.prototype.max = maxfn();
But with this approach I can't access 'this'.
I wanted to add a syntactic sugar for
new Array("1","2","3")
to something like :)(suggestions needed)
_("1","2" ,"3")
like we have in scheme where list -> '
I tried to clone the arguments but failed.
Thanks.
For lists you can use JSON notation:
["1", "2", "3"]
You can use JSON notation as suggested by RoBorg, if you control the list... However, there's no cross-browser way to treat a property as a method. Note: spidermonkey (firefox) does support using a getter (get method for a property).
Whats the best hack to remove the ()
Property getters/setters in JavaScript. Unfortunately it's a relatively new JavaScript feature that won't work on IE6/7 (as well as various other older browsers), so it's not really ready for prime-time yet (despite the intro of the linked article).
You could do this particular example by making a JavaScript object that wrapped a String and shadowed all String's methods, then add a static ‘first_char’ property set to the String's first character on initialisation. But it's really not worth it.
new Array("1","2","3")
to something like :)(suggestions needed)
_("1","2" ,"3")
Well that's simple enough:
function _(/* items */) {
var a= new Array();
for (var i= 0; i<arguments.length; i++)
a[i]= arguments[i];
return a;
}
There's no point in doing it nowadays, though, since the array literal syntax:
['1', '2', '3']
has been available since JavaScript 1.1-1.2 era and is available in every browser today. (It predates JSON by many, many years.)
I'll try to answer one by one:
1) Why would you want to remove parenthesis from a functon call?
2) If the "naive" approach is failing it's probably because you are calling the maxFn and assigning the results to Array.prototype.max. It should be like this:
maxfn = function() {this[0]..}; Array.prototype.max = maxfn;
3) RoBorg is correct, just use literal notation to construct arrays on the fly.
Edit:
Here's one way of implementing a max function on an array object. The optional evaluator argument is a function that takes two parameters, the current max value and current value in array. It should return the object that is "greater". Useful for non-primitives.
Array.prototype.max = function(evaluator) {
var max, i = 1; len = this.length;
if (len > 0) max = this[0];
for (; i < len; i++) {
if (evaluator) {
max = evaluator(max, this[i]);
}
else if(max < this[i]) {
max = this[i];
}
}
return max;
};
var a = [1, 3, 4, 5, 6];
alert(a.max());
var b = ["Arnold", "Billy", "Caesar"];
alert(b.max());
var c = ["Arnold", new Date(), 99, true];
alert(c.max());
var d = [1, 3, 4, 5, 6];
alert(d.max(function (max, val) { return max < val ? val : max }));

How to efficiently count the number of keys/properties of an object in JavaScript

What's the fastest way to count the number of keys/properties of an object? Is it possible to do this without iterating over the object? I.e., without doing:
var count = 0;
for (k in myobj) if (myobj.hasOwnProperty(k)) ++count;
(Firefox did provide a magic __count__ property, but this was removed somewhere around version 4.)
To do this in any ES5-compatible environment, such as Node.js, Chrome, Internet Explorer 9+, Firefox 4+, or Safari 5+:
Object.keys(obj).length
Browser compatibility
Object.keys documentation (includes a method you can add to non-ES5 browsers)
You could use this code:
if (!Object.keys) {
Object.keys = function (obj) {
var keys = [],
k;
for (k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k)) {
keys.push(k);
}
}
return keys;
};
}
Then you can use this in older browsers as well:
var len = Object.keys(obj).length;
If you are using Underscore.js you can use _.size (thanks douwe):
_.size(obj)
Alternatively you can also use _.keys which might be clearer for some:
_.keys(obj).length
I highly recommend Underscore.js. It's a tight library for doing lots of basic things. Whenever possible, they match ECMAScript 5 and defer to the native implementation.
Otherwise I support Avi Flax' answer. I edited it to add a link to the MDC documentation which includes the keys() method you can add to non-ECMAScript 5 browsers.
The standard Object implementation (ES5.1 Object Internal Properties and Methods) does not require an Object to track its number of keys/properties, so there should be no standard way to determine the size of an Object without explicitly or implicitly iterating over its keys.
So here are the most commonly used alternatives:
1. ECMAScript's Object.keys()
Object.keys(obj).length; Works by internally iterating over the keys to compute a temporary array and returns its length.
Pros - Readable and clean syntax. No library or custom code required except a shim if native support is unavailable
Cons - Memory overhead due to the creation of the array.
2. Library-based solutions
Many library-based examples elsewhere in this topic are useful idioms in the context of their library. From a performance viewpoint, however, there is nothing to gain compared to a perfect no-library code since all those library methods actually encapsulate either a for-loop or ES5 Object.keys (native or shimmed).
3. Optimizing a for-loop
The slowest part of such a for-loop is generally the .hasOwnProperty() call, because of the function call overhead. So when I just want the number of entries of a JSON object, I just skip the .hasOwnProperty() call if I know that no code did nor will extend Object.prototype.
Otherwise, your code could be very slightly optimized by making k local (var k) and by using prefix-increment operator (++count) instead of postfix.
var count = 0;
for (var k in myobj) if (myobj.hasOwnProperty(k)) ++count;
Another idea relies on caching the hasOwnProperty method:
var hasOwn = Object.prototype.hasOwnProperty;
var count = 0;
for (var k in myobj) if (hasOwn.call(myobj, k)) ++count;
Whether this is faster or not on a given environment is a question of benchmarking. Very limited performance gain can be expected anyway.
Here are some performance tests for three methods;
https://jsperf.com/get-the-number-of-keys-in-an-object
Object.keys().length
20,735 operations per second
It is very simple and compatible and runs fast but expensive, because it creates a new array of keys, which then gets thrown away.
return Object.keys(objectToRead).length;
Loop through the keys
15,734 operations per second
let size=0;
for(let k in objectToRead) {
size++
}
return size;
It is slightly slower, but nowhere near the memory usage, so it is probably better if you're interested in optimising for mobile or other small machines.
Using Map instead of Object
953,839,338 operations per second
return mapToRead.size;
Basically, Map tracks its own size, so we're just returning a number field. It is far, far faster than any other method. If you have control of the object, convert them to maps instead.
If you are actually running into a performance problem I would suggest wrapping the calls that add/remove properties to/from the object with a function that also increments/decrements an appropriately named (size?) property.
You only need to calculate the initial number of properties once and move on from there. If there isn't an actual performance problem, don't bother. Just wrap that bit of code in a function getNumberOfProperties(object) and be done with it.
As answered in a previous answer: Object.keys(obj).length
But: as we have now a real Map class in ES6, I would suggest to use it instead of using the properties of an object.
const map = new Map();
map.set("key", "value");
map.size; // THE fastest way
this works for both, Arrays and Objects
//count objects/arrays
function count(obj){
return Object.keys(obj).length
}
count objects/arrays with a Loop
function count(obj){
var x=0;
for(k in obj){
x++;
}
return x;
}
count objects/arrays or also the length of a String
function count(obj){
if (typeof (obj) === 'string' || obj instanceof String)
{
return obj.toString().length;
}
return Object.keys(obj).length
}
As stated by Avi Flax,
Object.keys(obj).length
will do the trick for all enumerable properties on your object, but to also include the non-enumerable properties, you can instead use the Object.getOwnPropertyNames. Here's the difference:
var myObject = new Object();
Object.defineProperty(myObject, "nonEnumerableProp", {
enumerable: false
});
Object.defineProperty(myObject, "enumerableProp", {
enumerable: true
});
console.log(Object.getOwnPropertyNames(myObject).length); //outputs 2
console.log(Object.keys(myObject).length); //outputs 1
console.log(myObject.hasOwnProperty("nonEnumerableProp")); //outputs true
console.log(myObject.hasOwnProperty("enumerableProp")); //outputs true
console.log("nonEnumerableProp" in myObject); //outputs true
console.log("enumerableProp" in myObject); //outputs true
As stated here, this has the same browser support as Object.keys.
However, in most cases, you might not want to include the nonenumerables in these type of operations, but it's always good to know the difference ;)
To iterate on Avi Flax' answer, Object.keys(obj).length is correct for an object that doesn’t have functions tied to it.
Example:
obj = {"lol": "what", owo: "pfft"};
Object.keys(obj).length; // should be 2
versus
arr = [];
obj = {"lol": "what", owo: "pfft"};
obj.omg = function(){
_.each(obj, function(a){
arr.push(a);
});
};
Object.keys(obj).length; // should be 3 because it looks like this
/* obj === {"lol": "what", owo: "pfft", omg: function(){_.each(obj, function(a){arr.push(a);});}} */
Steps to avoid this:
do not put functions in an object that you want to count the number of keys in
use a separate object or make a new object specifically for functions (if you want to count how many functions there are in the file using Object.keys(obj).length)
Also, yes, I used the _ or Underscore.js module from Node.js in my example.
Documentation can be found here as well as its source on GitHub and various other information.
And finally a lodash implementation https://lodash.com/docs#size
_.size(obj)
I'm not aware of any way to do this. However, to keep the iterations to a minimum, you could try checking for the existence of __count__ and if it doesn't exist (i.e., not Firefox) then you could iterate over the object and define it for later use, e.g.:
if (myobj.__count__ === undefined) {
myobj.__count__ = ...
}
This way, any browser supporting __count__ would use that, and iterations would only be carried out for those which don't. If the count changes and you can't do this, you could always make it a function:
if (myobj.__count__ === undefined) {
myobj.__count__ = function() { return ... }
myobj.__count__.toString = function() { return this(); }
}
This way, any time you reference myobj.__count__ the function will fire and recalculate.
From Object.defineProperty():
Object.defineProperty(obj, prop, descriptor)
You can either add it to all your objects:
Object.defineProperty(Object.prototype, "length", {
enumerable: false,
get: function() {
return Object.keys(this).length;
}
});
Or a single object:
var myObj = {};
Object.defineProperty(myObj, "length", {
enumerable: false,
get: function() {
return Object.keys(this).length;
}
});
Example:
var myObj = {};
myObj.name = "John Doe";
myObj.email = "leaked#example.com";
myObj.length; // Output: 2
Added that way, it won't be displayed in for..in loops:
for(var i in myObj) {
console.log(i + ": " + myObj[i]);
}
Output:
name: John Doe
email: leaked#example.com
Note: it does not work in browsers before Internet Explorer 9.
For those who have Underscore.js included in their project you can do:
_({a:'', b:''}).size() // => 2
or functional style:
_.size({a:'', b:''}) // => 2
How I've solved this problem is to build my own implementation of a basic list which keeps a record of how many items are stored in the object. It’s very simple. Something like this:
function BasicList()
{
var items = {};
this.count = 0;
this.add = function(index, item)
{
items[index] = item;
this.count++;
}
this.remove = function (index)
{
delete items[index];
this.count--;
}
this.get = function(index)
{
if (undefined === index)
return items;
else
return items[index];
}
}
For those that have Ext JS 4 in their project, you can do:
Ext.Object.getSize(myobj);
The advantage of this is that it'll work on all Ext JS compatible browsers (Internet Explorer 6 - Internet Explorer 8 included). However, I believe the running time is no better than O(n) though, as with other suggested solutions.
You can use:
Object.keys(objectName).length;
and
Object.values(objectName).length;
The OP didn't specify if the object is a nodeList. If it is, then you can just use the length method on it directly. Example:
buttons = document.querySelectorAll('[id=button)) {
console.log('Found ' + buttons.length + ' on the screen');
If jQuery in previous answers does not work, then try
$(Object.Item).length
I try to make it available to all objects like this:
Object.defineProperty(Object.prototype,
"length",
{
get() {
if (!Object.keys) {
Object.keys = function (obj) {
var keys = [],k;
for (k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k)) {
keys.push(k);
}
}
return keys;
};
}
return Object.keys(this).length;
},});
console.log({"Name":"Joe", "Age":26}.length) // Returns 2

Categories

Resources