I'm attempting to loop through an object and use different functions on each different sub-object based on it's type. instanceof seemed to be the only way to identify non-built in types, so I attempted to use it with for...in; however, I'm not getting the expected output.
Here is a sample fiddle to illustrate my issue: http://jsfiddle.net/zv230dvL/2/
function testObject(){}
function testContainer(objects){
this.zero = objects[0];
this.one = objects[1];
this.two = objects[2];
}
var singleObject = new testObject();
var container = new testContainer(
[new testObject(),
new testObject(),
new testObject()]
);
...
//Testing if a single object functions as expected
out(singleObject instanceof testObject);
//Testing if directly referencing an object
//in a container functions as expected
out(container.zero instanceof testObject);
//iterating through a container object...
for(i in container){
out(i instanceof testObject);
}
The output is
true
true
false
false
false
First, what would be the appropriate method to do something like this?
Second, why does the variable in the for...in loop behave in this manner?
no jQuery please.
When you iterate an Object with for..in, the loop variable will have the key, in string type. So, you should be checking like this
for (i in container) {
out(container[i] instanceof testObject);
}
Now, you are using the object corresponding to the key i in container. With this change, all the items will evaluate to true.
Note: for..in is supposed to give all the inherited properties as well. So, the normal looping pattern when you use for..in with an Object is
for (i in container) {
if (container.hasOwnProperty(i)) {
out(container[i] instanceof testObject);
}
}
This will make sure that, only the properties which are directly defined in the object are checked.
Related
I'm implementing a native module for node and trying to return something that looks like an array but is backed by native data. At the moment I'm creating an ObjectTemplate and using SetIndexedPropertyHandler so I can handle attempts to get indexes from JS. This all works, but from JS this just looks like an Object, not an Array. Is there any way I can make this look more like an array?
You can return Object.entries(object).
The Object.entries() method returns an array of a given
object's own enumerable property [key, value] pairs, in the same
order as that provided by a for...in loop (the difference being that a
for-in loop enumerates properties in the prototype chain as well).
const obj = {a:1, b:2, c:3};
console.log(Object.entries(obj));
All arraylike objects must have length. As long as they have that property, you can borrow Array functions and run them on your object (e.g. Array.prototype.forEach.call(obj, ...)), or in ES6 run Array.from on it:
obj = {
0: "foo",
1: "bar",
length: 2
};
// ES5 way to get a real array:
console.log(Array.prototype.slice.call(obj));
// ES6 way to get a real array:
console.log(Array.from(obj));
If you actually want to return an array and not an arraylike object, then I suggest you use array = Array::New() and array->Set(index, element) instead of ObjectTemplate. There's an example in this question.
You could consider using a Proxy around a real array, with traps/handlers that interface with your native object. There's a performance penalty to using proxies (don't try to iterate over a proxied array if it's an important, hot code path), but they let you do just about anything.
var _arr = [];
var arraylike = new Proxy(_arr, {
get: function (target, prop) {
// Interface with your native object here.
if (prop === "length") return 20;
return "hello";
}
});
// Consume
> arraylike.length
20
> arraylike[5]
"hello"
> arraylike instanceof Array
true
> Array.isArray(arraylike)
true
It's also perfectly valid to prototypically inherit from Array in javascript.
So far I have relied on Object.prototype.toString.call(x) to distinguish between the different native object types in Javascript, arrays in particular.
If you subclass arrays, you get some strange behavior:
function Ctor() {}
Ctor.prototype = Object.create(Array.prototype);
var x = new Ctor();
x.push(1);
Object.prototype.toString.call(x); // [object Object]
Probably this is documented in the ES5 specs (and no longer an issue in ES6), but I consider it a quirk of the current version of the language. I adapted my corresponding functions as follows:
function objTypeOf(deep, type) {
return function _objTypeOf(x) {
do {
if (Object.prototype.toString.call(x).slice(8, -1).toLowerCase() === type) return true;
x = Object.getPrototypeOf(x);
} while(deep && x !== null);
return false;
};
}
var arr = objTypeOf(false, "array"),
arrP = objTypeOf(true, "array"); // array prototype
console.log(arr(x)); // false
console.log(arrP(x)); // true
objTypeOf checks the current object and the entire prototype chain until there is a type match. It accepts an object even if merely one of the prototypes matches the expected type. objTypeOf is not based on prototype identities, but on strings (lacking identity).
I wonder now if there are other edge cases when using Object.prototype.toString, that need special treatment?
Well your problem is not with Object.prototype.toString, but that you tried to subclass arrays. It just doesn't work, and toString correctly tells you that you failed to create an array. It's merely an object that has Array.prototype in its prototype chain (if that was what you cared for, use instanceof Array).
Regardless, to answer your title question:
What are the edge cases when using Object.prototype.toString?
Host objects. Everything that is not a native JS object, despite looking like one, might return any [[Class]] value that you didn't expect. There are even known cases where callable objects do not report Function.
Can you explain me this?
JAVASCRIPT
if (typeof Array.prototype.contains != 'function') {
Array.prototype.contains = function (str){
return this.indexOf(str) > -1;
};
}
var text = "Hello world";
var tokens = text.split(" ");
console.log(tokens.length);
for (var i in tokens){
console.log(tokens[i]);
}
OUTPUT
2
"Hello"
"world"
function ()
In other words, the "contains" function i added to the Array prototype appears in the for loop as last element (but the array length is 2). Why?
Why?
Because for-in doesn't loop through array entries, it loops through enumerable properties of an object. You've created a new enumerable property on Array.prototype, and so that property shows up in for-in loops on any array.
The solutions are:
Don't do that, instead loop through arrays in any of several ways that don't have that problem, or
Use hasOwnProperty to filter out inherited properties (see link above for more), or
Define contains as a non-enumerable property via Object.defineProperty or similar (you can only do this on ES5+ engines, it cannot be shimmed).
Given
var o = {};
var p = new Object();
p === o; //false
o.__proto__===p.__proto__ // true
why is this false?
please tell me the immediate reason to return false??
The === for objects is defined as:
11.9.6 The Strict Equality Comparison Algorithm
The comparison x === y, where x and y are values, produces true or
false. Such a comparison is performed as follows:
...
7. Return true if x and y refer to the same object. Otherwise, return
false.
In this case, although both are empty objects, they are created separately and hence do not refer to the same object.
As a side note, both constructions do the same thing; but it is common practice to use {}.
The two objects contain the same thing (i.e. nothing) but they are not the same object.
Javascript's object equality test requires that the two parameters refer to the exact same object.
JavaScript strict comparison for objects tests whether two expressions refer to the same objects (and so does the normal equals operator).
You create the first object using object literal {} which creates a new object with no properties.
You create the second object by calling Object constructor as a function. According to section 15.2.1.1 of ECMAScript Language Specification this also creates a new object just as if new Object() was used.
Thus you create two objects, store their references under p and o and check whether p and o refer to the same object. They don't.
Every time you create an object, the result has its own, distinct identity. So even though they are both "empty", they are not the same thing. Hence the === comparison yields false.
Using the === , the result will show if items on both side is the "Same Instance"
If you like to comparing two item are same type, you should use:
var o1 = {};
var o2 = new Object();
alert( typeof(o1) === typeof(o2));
and if you like to tell if the two object is considered equal (in properties and values), you should try underscore.js library and use the isEqual function.
Is this homework?
In that case, I will only give you some hints:
- Think about what the first two lines do. Do o and p refer to the same object after those two lines?
- Look up exactly what === does. Does it compare two objects to see if their structure are the same? Or does it compare the objects based on their identity?
Object is an unordered collection of properties each of which contains a primitive value, object, or function. So, each object has properties and prototypes and there's no any sense to compare the one.
I have an object with various properties. The name of the object is a global variable but the properties are changed at runtime by methods. Some methods add properties to the object. I'd like to add a method that loops through this object and removes all of its properties that are either null or empty. I could check each property by specifying its name and checking its state but if I add properties later, I'd have to update this cleanup method too. How can I loop through the properties of an object without know the name of the properties first.
Thanks for your suggestions.
Iteration over an object is simple - the for in loop:
for (var key in object) {
if (object.hasOwnProperty(key)) {
//Now, object[key] is the current value
if (object[key] === null || isEmpty(object[key]))
delete object[key];
}
}
isEmpty doesn't exist, you have to define it or replace it with something more meaningful, I couldn't understand what you meant by empty in your question.
I use object.hasOwnProperty because objects inherit things from Object.prototype and possibly other places (arrays for example inherit from Array.prototype, which inherits from Object.prototype). So:
object.toString; //function toString() { [native code] }
But, object.toString actually refers to Object.prototype.toString - it isn't really in your object, but when you type object.toString, the interpreter sees that there's no object.toString, so it checks up the prototype chain until it finds it.
hasOwnProperty checks if a key actually exists on an object:
object.hasOwnProperty("toString"); //false
object.foo = "bar";
object.hasOwnProperty("foo"); //true
Subscript access to objects is also simple:
var object = {foo: "bar"};
object.foo; //"bar"
object["foo"]; //"bar"
var key = "foo";
object[key]; //"bar"
Note that whatever is passed to the brackets gets converted to a string. So, for example, you can do this:
object[Object.prototype.toString] = "magic";
Object.keys(object); //["function toString() { [native code] }"]
In case you're wondering, Object.keys is an ES5 (EcmaScript 5) feature.
You can use a for each loop to iterate through the object properties.
for ( var i in obj ) {
if ( obj[i] === null ) {
delete obj[i];
}
}