I'm seeing something weird when I try to include the ember.js library (ember-1.0.0-rc.7.js).
The javacode I have just prints out a javascript array:
<script type="application/javascript">
var songs = [ 'a','b','c'];
console.debug(songs.toString());
for(key in songs)
{
console.debug(songs[key]);
}
</script>
When I don't include the library, it'll print out a , b, c in the console. However, when i do include it, it start printing out a, b, c, but as well as all the functions to...
Example:
function (idx) {
return this.objectAt(idx);
}
function superWrapper() {
var ret, sup = this._super;
this._super = superFunc || K;
ret = func.apply(this, arguments);
this._super = sup;
return ret;
}
function (key) {
return this.mapProperty(key);
}
Any reason why this occurs with the ember.js library, and how do I resolve this issue?
Any advice appreciated,
Thanks,
D
By default, Ember extends built-in prototypes such as Array.prototype to provide extra methods or shim ES5 methods for non-supporting browsers. You are seeing these methods because for...in iterates over the enumerable properties of an object. These include all properties, even those inherited through the prototype chain.
Instead, you should use a regular for loop to iterate over an array:
for(var i=0; i<songs.length; i++) {
console.debug(songs[i]);
}
This will only ever go over actual array elements, i.e. properties with a numerical key. There are nicer ways though, for example using ES5 Array.forEach (shimmed by Ember in older browsers):
songs.forEach(function(song, i) {
console.debug(song);
});
Optionally, you can disable Ember's prototype extension by configuring Ember.EXTEND_PROPERTIES if you're not planning to use them or if they might conflict with other libraries/scripts. There's a whole page dedicated to this issue in the Ember documentation.
embjer.js must be adding functions to the native Array.prototype. You can check if each key is actually a property on the array itself, and not an inherited property, using hasOwnProperty:
for(key in songs) {
if(songs.hasOwnProperty(key)){
console.debug(songs[key]);
}
}
But, it is usually recommended to always use ordinary for loops with arrays, since they have numerical keys:
for(var i = 0; i < songs.length; i++) {
console.debug(songs[i]);
}
You can also use the forEach function that, ironically, Ember added to the Array prototype if it didn't already exist (in older browsers).
songs.forEach(function(song){
console.debug(song);
});
Demo
Related
With ES6 taking ahold, I'm eager to drop jQuery and use native JS for my website builds keeping them quick and lightweight. Also to improve my JS skills as I'm one of those whose jumped straight in with jQuery.
I'm building a tiny tiny library to make the more common used javascript in a function to keep the files small.
function $(elm) {
var elm = document.querySelectorAll(elm);
this.forEach = function(f) {
[].forEach.call(elm, f);
}
return elm;
}
function slider() {
$(".slider").forEach(function() {
alert("Hello");
});
}
slider();
This works great in Chrome etc.. but in IE10/11 I'm getting the error
Object doesn't support this property or method "forEach"
referring to the $(".slider").forEach
Any ideas?
You're adding forEach to the window object, not to the object you return; you're calling $ as a function, not a constructor. Since you're using loose mode (apparently), this within the function call is a reference to the global object (also accessible as window on browsers). You're returning the collection from querySelectorAll unchanged.
The reason it works on Chrome is that the collection returned by querySelectorAll has its own forEach (this is a fairly recent addition).
For this to work, four options:
Return an object and add forEach to it, copying the elements from the QSA collection to that object. E.g.:
function $(selector) {
const result = Array.from(document.querySelectorAll(selector));
result.forEach = Array.prototype.forEach;
// Perhaps map, filter, etc.; add in a loop?
return result;
}
Or in ES5:
var $ = (function() {
var methods = Array.prototype;
function $(selector) {
var result = methods.slice.call(document.querySelectorAll(selector));
result.forEach = methods.forEach;
// Perhaps map, filter, etc.; add in a loop?
return result;
}
return $;
})();
Add forEach to the NodeList prototype if it's not already there and use forEach directly on the collection from querySelectorAll. For instance:
if (typeof NodeList !== "undefined" &&
NodeList.prototype &&
!NodeList.prototype.forEach) {
// Yes, direct assignment is fine here, no need for `Object.defineProperty`
NodeList.prototype.forEach = Array.prototype.forEach;
}
(No need for Object.defineProperty above, enumerable [surprisingly], configurable, and writable are all true for it on Chrome and Firefox, so we can just do direct assignment as above.)
...and then of course your $ becomes nothing more than
function $(selector) {
return document.querySelectorAll(selector);
}
(To start with. If you wanted to add more features, you'd probably want to go the way of #1.)
Return an array:
function $(selector) {
return Array.from(document.querySelectorAll(selector));
}
Or in ES5:
function $(selector) {
return Array.prototype.slice.call(document.querySelectorAll(selector));
}
Subclass Array (which cannot be perfectly polyfilled on pre-ES2015 JavaScript engines) so you can add your own features on top of Array's features:
class MyThingy extends Array {
// Perhaps further methods here
}
function $(selector) {
return MyThingy.from(document.querySelectorAll(selector));
}
No ES5 option here, you'd need to at least transpile and polyfill.
If you're going to add features beyond those provided by Array, I quite like #4 other than the polyfilling available only being "so" good. It may well be sufficient for your purposes.
I want to define helper methods on the Array.prototype and Object.prototype. My current plan is to do something like:
Array.prototype.find = function(testFun) {
// code to find element in array
};
So that I can do this:
var arr = [1, 2, 3];
var found = arr.find(function(el) { return el > 2; });
It works fine but if I loop over the array in a for in loop the methods appear as values:
for (var prop in arr) { console.log(prop); }
// prints out:
// 1
// 2
// 3
// find
This will screw up anybody else relying on the for in to just show values (especially on Objects). The later versions of javascript have .map and .filter functions built into arrays but those don't show up on for in loops. How can I create more methods like that which won't show up in a for in loop?
It's quite easy: Don't use for-in loops with Arrays. Blame everybody else who does so - here is a nice snippet to tell them during development.
Of course, if one does an enumeration in a generic function and doesn't know whether he gets an array, a plain object or an object with a custom prototype, you can use hasOwnProperty like this:
for (var prop in anyObj )
if (Object.prototype.hasOwnProperty.call(anyObj, prop))
// do something
Notice the explicit use of Object.prototype to get the function - there might be objects that overwrite it (especially in data-maps, the value might not even be a function), objects that do not support it or objects that do not inherit from Object.prototype at all. See also here.
Yet, only a script author who is aware of the problem would filter all his for-in-loops - and some only do it because it gets recommended - and does it mostly wrong, he should have used a for-loop array iteration instead. But our problem are those authors who do not know of it.
An interesting, but Mozilla-only approach would be overwriting the behavior of enumerations on arrays via __iterate__, as demonstrated here.
Fortunately, EcmaScript 5.1 allows us setting properties to be non-enumerable. Of course, this is not supported in older browsers, but why bother? We'd need to use es5-shims anyway for all the cool higher-order array stuff :-) Use defineProperty like this:
Object.defineProperty(Array.prototype, "find", {
enumerable: false,
writable: true,
value: function(testFun) {
// code to find element in array
}
});
Depending on your restrictions:
// In EcmaScript 5 specs and browsers that support it you can use the Object.defineProperty
// to make it not enumerable set the enumerable property to false
Object.defineProperty(Array.prototype, 'find', {
enumerable: false, // this will make it not iterable
get: function(testFun) {
// code to find element in array
};
});
Read more about Object.defineProperty here https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty
The above answers miss a point:
enumerable ... Defaults to false. (mdn)
So simply using Object.defineProperty(Array.prototype, 'myFunc' myFunc) instead of Array.prototype.myFunc = myFunc will resolve the issue.
It's because have to check for hasOwnProperty:
for (var prop in arr) {
if (arr.hasOwnProperty(prop)) {
console.log(prop)
}
}
Now this logs 1, 2, 3.
I have defined prototype for Array indexOf ( to support array indexOf in Internet Explorer )
if(!Array.prototype.indexOf){
Array.prototype.indexOf = function(obj){
for(var i=0; i<this.length; i++){
if(this[i]==obj){
return i;
}
}
return -1;
}
}
When I am creating array with values [1,2,3], this indexOf code snippet added into the Array like below
["1","2","3",function(obj){for(var i=0;i<this.length;i++){if(this[i]==obj){return i;}}return -1;}]
This problem happens only in IE.
Can anyone help me to resolve this issue. Thanks in advance.
I didn't use for...in loop anywhere, for this I am using jQuery sortable toArray method .sortable("toArray");.
I am assuming that at some point you are using a for...in loop to iterate over the elements of your array. For example:
for (var elem in myArray) {
//Do stuff...
}
A for...in loop will enumerate all enumerable properties of an object, including those it has inherited from it's ancestors in its prototype chain. You've added a method to the Array prototype:
Array.prototype.indexOf = function(obj){ //...
This property is enumerable (you can't define non-enumerable properties - see Object.defineProperty - in older versions of IE), so a for...in loop will include this property.
The simple solution is to never use a for...in loop to iterate over an array! Use a normal for loop instead.
Your problem is that your new indexOf() method is marked "enumerable." Unfortunately, there is no fix for this in IE7 or non-standards-mode IE8. But you can at least patch the issue for standards mode IE8 by using some ES5 trickery. Modify your code to look like this and it should get rid of the extra element if you're in IE8 standards mode:
(function () {
var indexOfFn = function(obj){
for(var i=0; i<this.length; i++){
if(this[i]==obj){
return i;
}
}
return -1;
};
if(!Array.prototype.indexOf){
if(typeof Object.defineProperty === "function") {
Object.defineProperty(Array.prototype, "indexOf", {
value: indexOfFn,
enumerable: false
});
} else {
Array.prototype.indexOf = indexOfFn;
}
}
}());
I know it's wordy, so it might not be worth the effort. But it will also protect your JavaScript from other people's bad coding where they might end up using an Array with a for-in loop.
It's quite simple, you're array now contains 4 elements, of which the forth is a function object, you're not defining a new method for the array object, let alone all array objects. Just paste the first snippet at the very top of your script, then:
var foo = [1,2,3];
alert(foo.indexOf(2));//alerts 1
Think of Array.prototype as the template of every array. Whenever you try to access some property or method of an array, that isn't defined, rather then throwing errors, JS will first check the Array.prototype if that object doesn't have that method/property. If it does, JS will use that code, and apply it to the array that initially called it. In the above example foo.indexOf(2) could have been written as Array.prototype.indexOf.apply(foo,[2]);. In other words: JS automatically applied the function of the prototype to foo.
Your "full" code should look like this:
if(!Array.prototype.indexOf)
{
Array.prototype.indexOf = function(obj)
{
for(var i=0; i<this.length; i++)
{
if(this[i] === obj)//preferable, use strict comparison
{
return i;
}
}
return -1;
};
}
var yourArray = [1,2,3,4,'4'];
alert(yourArray.indexOf(4));//alerts 3
alert(yourArray.indexOf('4'));//alerts 4 when using strict comparison, if not, alerts 3
Here's a fiddle, checked in IE8, and it's working just fine
Just google prototypal inheritance and prototype chains or augmenting prototypes in JS and the like, read up and be baffled! ;)
We're using jQuery on a less UI-intensive project whereas we have been using ExtJS on other projects. Regardless, I'm curious if any of the prevalent libraries such as ExtJS, jQuery, or others, provide a utility for "pruning" objects? By which I mean, deleting keys if they don't contain values.
I've devised my own (naive) implementation as follows, so its no biggie. Still though just curious as I believe this could be generally useful. I've googled for "jquery prune object" but no results seem worth investigating though of course I could be wrong ;)
function pruneObj(obj) {
var empty = [];
for (var attr in obj) {
if (!obj[attr]) {
empty.push(attr); //rather than trying to delete the element in place
}
}
for (var i=0, n=empty.length; i<n; i++) {
delete(obj[empty[i]]);
}
return obj;
}
So:
var obj = { hasFoo: false, count: 0 }
would (after a call to prunObj(obj)) be just {}?
I don't think any library out there would make the assumption of what values to deem "empty". It's too destructive/mutative a function to include as part of a standard set of utility functions.
Many libraries or browsers include a facility to filter out items from arrays that don't meet a certain criteria. Eg filter:
list.filter(function(item){
return !item.foo
})
will filter out items in list that have a "falsey" value in the foo property.
Edit:
On further thought, undefined seems to be considered "empty" by both jQuery and Prototype. jQuery's extend function can do what I think you're aiming for with undefined, albeit with a slightly different syntax and without mutating the passed argument:
A = { hasFoo: false, count: 0, foo: null, bar: undefined };
B = jQuery.extend({}, A); // B = { hasFoo: false, count: 0, foo: null }
Swap "jQuery" with "Object" for the Prototype equivalent.
I'm trying to use JavaScript object as an associative array and everything was well until I needed to get number of entries that are stored in it. What is the easiest and most elegant way to do that? All I can think of is to run for each loop or jQuery $.each function and just see how much iterations it would do but that looks like an an awful thing to do.
This worked for me:
Object.keys(obj).length
Old Firefox supports the __count__ property. Newer environments support ES5's Object.keys. For older environments we have to fallback to just iterating over the object and counting manually (ugh!):
function count(obj) {
if (obj.__count__ !== undefined) { // Old FF
return obj.__count__;
}
if (Object.keys) { // ES5
return Object.keys(obj).length;
}
// Everything else:
var c = 0, p;
for (p in obj) {
if (obj.hasOwnProperty(p)) {
c += 1;
}
}
return c;
}
I believe this has been answered on here before, and/or googling can lead you in the right direction. Nevertheless let me point out that one of the biggest gotchas if you use a loop in the form of:
for (var attr in obj) { ....
Is that the object could be cluttered with properties you did not necessarily add, and the standard solution to this as I recall is to additionally use the test for hasOwnProperty, see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/hasOwnProperty
It comes down to that, or keeping count when you add and delete properties from the object.
unfortunately there isn't really a good way to know the number of properties in an object without iterating over them. The following is fairly simple though:
function countProps(obj) {
var l = 0;
for (p in obj) l++;
return l;
}
var bob = { a: 1, b: 2};
alert(countProps(bob));